diff --git a/.travis.yml b/.travis.yml index 6b8992f95..181262e0d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ before_install: - "ci/setup.sh" - "ci/install_go.sh" - "ci/install_rust.sh" + - "ci/install_vc.sh" - "ci/static-checks.sh" # need to install rust from scratch? @@ -27,7 +28,9 @@ before_install: # which is already installed? install: + - cd ${TRAVIS_BUILD_DIR}/src/runtime && make - cd ${TRAVIS_BUILD_DIR}/src/agent && make script: + - cd ${TRAVIS_BUILD_DIR}/src/runtime && make test - cd ${TRAVIS_BUILD_DIR}/src/agent && make check diff --git a/README.md b/README.md index 195ab2b48..cc82336c8 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ the first place to go if you want to use or contribute to the project. ##### Agent -The [`kata-agent`](https://github.com/kata-containers/agent) runs inside the +The [`kata-agent`](src/agent/README.md) runs inside the virtual machine and sets up the container environment. ##### KSM throttler @@ -83,7 +83,7 @@ virtual machine. ##### Runtime -The [`kata-runtime`](https://github.com/kata-containers/runtime) is usually +The [`kata-runtime`](src/runtime/README.md) is usually invoked by a container manager and provides high-level verbs to manage containers. diff --git a/ci/install_vc.sh b/ci/install_vc.sh new file mode 100755 index 000000000..67968699b --- /dev/null +++ b/ci/install_vc.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -e + +cidir=$(dirname "$0") +vcdir="${cidir}/../src/runtime/virtcontainers/" +source "${cidir}/lib.sh" +export CI_JOB="${CI_JOB:-default}" + +clone_tests_repo + +if [ "${CI_JOB}" != "PODMAN" ]; then + echo "Install virtcontainers" + make -C "${vcdir}" && chronic sudo make -C "${vcdir}" install +fi diff --git a/ci/lib.sh b/ci/lib.sh index 3aa9c5c30..76734f1f5 100644 --- a/ci/lib.sh +++ b/ci/lib.sh @@ -16,10 +16,6 @@ clone_tests_repo() fi go get -d -u "$tests_repo" || true - - if [ -n "${TRAVIS_BRANCH:-}" ]; then - ( cd "${tests_repo_dir}" && git checkout "${TRAVIS_BRANCH}" ) - fi } run_static_checks() diff --git a/src/runtime/.ci/go-no-os-exit.sh b/src/runtime/.ci/go-no-os-exit.sh new file mode 100755 index 000000000..5f0f98436 --- /dev/null +++ b/src/runtime/.ci/go-no-os-exit.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# +# Check there are no os.Exit() calls creeping into the code +# We don't use that exit path in the Kata codebase. + +# Allow the path to check to be over-ridden. +# Default to the current directory. +go_packages=${1:-.} + +echo "Checking for no os.Exit() calls for package [${go_packages}]" + +candidates=`go list -f '{{.Dir}}/*.go' $go_packages` +for f in $candidates; do + filename=`basename $f` + # skip all go test files + [[ $filename == *_test.go ]] && continue + # skip exit.go where, the only file we should call os.Exit() from. + [[ $filename == "exit.go" ]] && continue + files="$f $files" +done + +[ -z "$files" ] && echo "No files to check, skipping" && exit 0 + +if egrep -n '\' $files; then + echo "Direct calls to os.Exit() are forbidden, please use exit() so atexit() works" + exit 1 +fi diff --git a/src/runtime/.ci/go-test.sh b/src/runtime/.ci/go-test.sh new file mode 100755 index 000000000..36fcd9ce4 --- /dev/null +++ b/src/runtime/.ci/go-test.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -e + +cidir=$(dirname "$0") +source "${cidir}/lib.sh" +export CI_JOB="${CI_JOB:-default}" + +if [ "${CI_JOB}" != "PODMAN" ]; then + run_go_test +fi diff --git a/src/runtime/.ci/install-yq.sh b/src/runtime/.ci/install-yq.sh new file mode 100755 index 000000000..d9b57053a --- /dev/null +++ b/src/runtime/.ci/install-yq.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +# If we fail for any reason a message will be displayed +die() { + msg="$*" + echo "ERROR: $msg" >&2 + exit 1 +} + +# Install the yq yaml query package from the mikefarah github repo +# Install via binary download, as we may not have golang installed at this point +function install_yq() { + GOPATH=${GOPATH:-${HOME}/go} + local yq_path="${GOPATH}/bin/yq" + local yq_pkg="github.com/mikefarah/yq" + [ -x "${GOPATH}/bin/yq" ] && return + + read -r -a sysInfo <<< "$(uname -sm)" + + case "${sysInfo[0]}" in + "Linux" | "Darwin") + goos="${sysInfo[0],}" + ;; + "*") + die "OS ${sysInfo[0]} not supported" + ;; + esac + + case "${sysInfo[1]}" in + "aarch64") + goarch=arm64 + ;; + "ppc64le") + goarch=ppc64le + ;; + "x86_64") + goarch=amd64 + ;; + "s390x") + goarch=s390x + ;; + "*") + die "Arch ${sysInfo[1]} not supported" + ;; + esac + + mkdir -p "${GOPATH}/bin" + + # Check curl + if ! command -v "curl" >/dev/null; then + die "Please install curl" + fi + + local yq_version=3.1.0 + + local yq_url="https://${yq_pkg}/releases/download/${yq_version}/yq_${goos}_${goarch}" + curl -o "${yq_path}" -LSsf ${yq_url} + [ $? -ne 0 ] && die "Download ${yq_url} failed" + chmod +x ${yq_path} + + if ! command -v "${yq_path}" >/dev/null; then + die "Cannot not get ${yq_path} executable" + fi +} + +install_yq diff --git a/src/runtime/.ci/install_go.sh b/src/runtime/.ci/install_go.sh new file mode 100755 index 000000000..55c3383cc --- /dev/null +++ b/src/runtime/.ci/install_go.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) 2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -e + +cidir=$(dirname "$0") +source "${cidir}/lib.sh" + +clone_tests_repo + +pushd "${tests_repo_dir}" +.ci/install_go.sh -p -f +popd diff --git a/src/runtime/.ci/lib.sh b/src/runtime/.ci/lib.sh new file mode 100644 index 000000000..96c151444 --- /dev/null +++ b/src/runtime/.ci/lib.sh @@ -0,0 +1,34 @@ +# +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +export tests_repo="${tests_repo:-github.com/kata-containers/tests}" +export tests_repo_dir="$GOPATH/src/$tests_repo" + +clone_tests_repo() +{ + # KATA_CI_NO_NETWORK is (has to be) ignored if there is + # no existing clone. + if [ -d "$tests_repo_dir" -a -n "$KATA_CI_NO_NETWORK" ] + then + return + fi + + go get -d -u "$tests_repo" || true + if [ -n "${TRAVIS_BRANCH:-}" ]; then + ( cd "${tests_repo_dir}" && git checkout "${TRAVIS_BRANCH}" ) + fi +} + +run_static_checks() +{ + clone_tests_repo + bash "$tests_repo_dir/.ci/static-checks.sh" "github.com/kata-containers/runtime" +} + +run_go_test() +{ + clone_tests_repo + bash "$tests_repo_dir/.ci/go-test.sh" +} diff --git a/src/runtime/.ci/run.sh b/src/runtime/.ci/run.sh new file mode 100755 index 000000000..3b2b7d7c2 --- /dev/null +++ b/src/runtime/.ci/run.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -e + +cidir=$(dirname "$0") +source "${cidir}/lib.sh" + +pushd "${tests_repo_dir}" +.ci/run.sh +popd diff --git a/src/runtime/.ci/setup.sh b/src/runtime/.ci/setup.sh new file mode 100755 index 000000000..9c0526754 --- /dev/null +++ b/src/runtime/.ci/setup.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -e + +cidir=$(dirname "$0") +source "${cidir}/lib.sh" +export CI_JOB="${CI_JOB:-default}" + +clone_tests_repo + +pushd "${tests_repo_dir}" +.ci/setup.sh +popd + +if [ "${CI_JOB}" != "PODMAN" ]; then + echo "Setup virtcontainers environment" + chronic sudo -E PATH=$PATH bash -c "${cidir}/../virtcontainers/utils/virtcontainers-setup.sh" + + echo "Install virtcontainers" + make -C "${cidir}/../virtcontainers" && chronic sudo make -C "${cidir}/../virtcontainers" install +fi diff --git a/src/runtime/.ci/static-checks.sh b/src/runtime/.ci/static-checks.sh new file mode 100755 index 000000000..5d0317e41 --- /dev/null +++ b/src/runtime/.ci/static-checks.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +set -e + +cidir=$(dirname "$0") +source "${cidir}/lib.sh" + +# Build kata-runtime before running static checks +make -C "${cidir}/../" + +# Run static checks +run_static_checks diff --git a/src/runtime/.ci/versions_checker.sh b/src/runtime/.ci/versions_checker.sh new file mode 100755 index 000000000..d722d37b9 --- /dev/null +++ b/src/runtime/.ci/versions_checker.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# +# Copyright (c) 2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# +# The purpose of this script is to +# run the tag_repos.sh script that is in the +# packaging repository which checks the VERSION +# file from the components in order to verify +# that the VERSION matches between them. +# This ensures that the rest +# of the components are merged before the runtime + +set -o errexit +set -o nounset +set -o pipefail +set -o errtrace + +GOPATH=${GOPATH:-${HOME}/go} + +PACKAGING_REPO="github.com/kata-containers/packaging" +RUNTIME_REPO="github.com/kata-containers/runtime" +KATA_BRANCH=${target_branch:-master} + +go get -d "${PACKAGING_REPO}" || true + +if ! check_changes=$(git diff --name-only "origin/${KATA_BRANCH}" | grep VERSION); then + echo "No changes in VERSION file - this is not a bump - nothing to check" + exit 0 +fi +version_to_check=$(cat "${GOPATH}/src/${RUNTIME_REPO}/VERSION") + +if [ ! -z "$check_changes" ]; then + echo "Changes detected on VERSION" + echo "Check versions in branch ${KATA_BRANCH}" + pushd "${GOPATH}/src/${PACKAGING_REPO}" + ./release/tag_repos.sh -b "${KATA_BRANCH}" pre-release "${version_to_check}" + popd +fi diff --git a/src/runtime/.gitignore b/src/runtime/.gitignore new file mode 100644 index 000000000..17686ab0f --- /dev/null +++ b/src/runtime/.gitignore @@ -0,0 +1,28 @@ +*.o +*.patch +*.swp +coverage.txt +coverage.html +.git-commit +.git-commit.tmp +/cli/config/configuration-acrn.toml +/cli/config/configuration-clh.toml +/cli/config/configuration-fc.toml +/cli/config/configuration-nemu.toml +/cli/config/configuration-qemu.toml +/cli/config/configuration-qemu-virtiofs.toml +/cli/config/configuration-clh.toml +/cli/config-generated.go +/cli/coverage.html +/containerd-shim-kata-v2 +/data/kata-collect-data.sh +/kata-netmon +/kata-runtime +/pkg/katautils/config-settings.go +/virtcontainers/hack/virtc/virtc +/virtcontainers/hook/mock/hook +/virtcontainers/profile.cov +/virtcontainers/shim/mock/cc-shim/cc-shim +/virtcontainers/shim/mock/kata-shim/kata-shim +/virtcontainers/shim/mock/shim +/virtcontainers/utils/supportfiles diff --git a/src/runtime/.travis.yml b/src/runtime/.travis.yml new file mode 100644 index 000000000..36d79fbba --- /dev/null +++ b/src/runtime/.travis.yml @@ -0,0 +1,42 @@ +# +# Copyright (c) 2018 Intel Corporation +# Copyright (c) 2018 IBM +# +# SPDX-License-Identifier: Apache-2.0 +# + +sudo: required +dist: xenial + +language: go +go: + - 1.13.9 + +os: + - linux + - linux-ppc64le + +matrix: + allow_failures: + - os: linux-ppc64le + +go_import_path: github.com/kata-containers/runtime + +env: + - target_branch=$TRAVIS_BRANCH + +before_install: + - git remote set-branches --add origin "${TRAVIS_BRANCH}" + - git fetch + - ".ci/setup.sh" + - ".ci/versions_checker.sh" + +before_script: + - ".ci/static-checks.sh" + - ".ci/versions_checker.sh" + +install: + - cd ${TRAVIS_BUILD_DIR} + - ".ci/install-yq.sh" + - make + - sudo -E PATH=$PATH make install diff --git a/src/runtime/CODEOWNERS b/src/runtime/CODEOWNERS new file mode 100644 index 000000000..e48c094af --- /dev/null +++ b/src/runtime/CODEOWNERS @@ -0,0 +1,13 @@ +# Copyright 2019 Intel Corporation. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Define any code owners for this repository. +# The code owners lists are used to help automatically enforce +# reviews and acks of the right groups on the right PRs. + +# Order in this file is important. Only the last match will be +# used. See https://help.github.com/articles/about-code-owners/ + +*.md @kata-containers/documentation + diff --git a/src/runtime/CONTRIBUTING.md b/src/runtime/CONTRIBUTING.md new file mode 100644 index 000000000..8a3af744a --- /dev/null +++ b/src/runtime/CONTRIBUTING.md @@ -0,0 +1,5 @@ +# Contributing + +## This repo is part of [Kata Containers](https://katacontainers.io) + +For details on how to contribute to the Kata Containers project, please see the main [contributing document](https://github.com/kata-containers/community/blob/master/CONTRIBUTING.md). \ No newline at end of file diff --git a/src/runtime/LICENSE b/src/runtime/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/src/runtime/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/src/runtime/Makefile b/src/runtime/Makefile new file mode 100644 index 000000000..bff880631 --- /dev/null +++ b/src/runtime/Makefile @@ -0,0 +1,825 @@ +# +# Copyright (c) 2018-2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +distro := $(shell \ +for file in /etc/os-release /usr/lib/os-release; do \ + if [ -e $$file ]; then \ + grep ^ID= $$file|cut -d= -f2-|tr -d '"'; \ + break; \ + fi \ +done) + +SKIP_GO_VERSION_CHECK= +include golang.mk + +#Get ARCH. +ifneq (,$(golang_version_raw)) + GOARCH=$(shell go env GOARCH) + ifeq ($(ARCH),) + ARCH = $(GOARCH) + endif +else + ARCH = $(shell uname -m) + ifeq ($(ARCH),x86_64) + ARCH = amd64 + endif + ifeq ($(ARCH),aarch64) + ARCH = arm64 + endif +endif + +ARCH_DIR = arch +ARCH_FILE_SUFFIX = -options.mk +ARCH_FILE = $(ARCH_DIR)/$(ARCH)$(ARCH_FILE_SUFFIX) +ARCH_FILES = $(wildcard arch/*$(ARCH_FILE_SUFFIX)) +ALL_ARCHES = $(patsubst $(ARCH_DIR)/%$(ARCH_FILE_SUFFIX),%,$(ARCH_FILES)) + +# Load architecture-dependent settings +include $(ARCH_FILE) + +PROJECT_TYPE = kata +PROJECT_NAME = Kata Containers +PROJECT_TAG = kata-containers +PROJECT_URL = https://github.com/kata-containers +PROJECT_BUG_URL = $(PROJECT_URL)/kata-containers/issues/new + +# list of scripts to install +SCRIPTS := + +# list of binaries to install +BINLIST := +BINLIBEXECLIST := + +BIN_PREFIX = $(PROJECT_TYPE) +PROJECT_DIR = $(PROJECT_TAG) +IMAGENAME = $(PROJECT_TAG).img +INITRDNAME = $(PROJECT_TAG)-initrd.img + +TARGET = $(BIN_PREFIX)-runtime +TARGET_OUTPUT = $(CURDIR)/$(TARGET) +BINLIST += $(TARGET) + +NETMON_DIR = netmon +NETMON_TARGET = $(PROJECT_TYPE)-netmon +NETMON_TARGET_OUTPUT = $(CURDIR)/$(NETMON_TARGET) +BINLIBEXECLIST += $(NETMON_TARGET) + +DESTDIR := / + +ifeq ($(PREFIX),) +PREFIX := /usr +EXEC_PREFIX := $(PREFIX)/local +else +EXEC_PREFIX := $(PREFIX) +endif +# Prefix where depedencies are installed +PREFIXDEPS := $(PREFIX) +BINDIR := $(EXEC_PREFIX)/bin +QEMUBINDIR := $(PREFIXDEPS)/bin +CLHBINDIR := $(PREFIXDEPS)/bin +FCBINDIR := $(PREFIXDEPS)/bin +ACRNBINDIR := $(PREFIXDEPS)/bin +VIRTIOFSDBINDIR := $(PREFIXDEPS)/bin +SYSCONFDIR := /etc +LOCALSTATEDIR := /var + +LIBEXECDIR := $(PREFIXDEPS)/libexec +SHAREDIR := $(PREFIX)/share +DEFAULTSDIR := $(SHAREDIR)/defaults + +COLLECT_SCRIPT = data/kata-collect-data.sh +COLLECT_SCRIPT_SRC = $(COLLECT_SCRIPT).in + +GENERATED_FILES += $(COLLECT_SCRIPT) +SCRIPTS += $(COLLECT_SCRIPT) +SCRIPTS_DIR := $(BINDIR) + +BASH_COMPLETIONS := data/completions/bash/kata-runtime +BASH_COMPLETIONSDIR := $(SHAREDIR)/bash-completion/completions + +PKGDATADIR := $(PREFIXDEPS)/share/$(PROJECT_DIR) +PKGLIBDIR := $(LOCALSTATEDIR)/lib/$(PROJECT_DIR) +PKGRUNDIR := $(LOCALSTATEDIR)/run/$(PROJECT_DIR) +PKGLIBEXECDIR := $(LIBEXECDIR)/$(PROJECT_DIR) + +KERNELDIR := $(PKGDATADIR) + +INITRDPATH := $(PKGDATADIR)/$(INITRDNAME) +IMAGEPATH := $(PKGDATADIR)/$(IMAGENAME) +FIRMWAREPATH := + +# Name of default configuration file the runtime will use. +CONFIG_FILE = configuration.toml + +HYPERVISOR_ACRN = acrn +HYPERVISOR_FC = firecracker +JAILER_FC = jailer +HYPERVISOR_QEMU = qemu +HYPERVISOR_CLH = cloud-hypervisor +HYPERVISOR_QEMU_VIRTIOFS = qemu-virtiofs + +# Determines which hypervisor is specified in $(CONFIG_FILE). +DEFAULT_HYPERVISOR ?= $(HYPERVISOR_QEMU) + +# List of hypervisors this build system can generate configuration for. +HYPERVISORS := $(HYPERVISOR_ACRN) $(HYPERVISOR_FC) $(HYPERVISOR_QEMU) $(HYPERVISOR_QEMU_VIRTIOFS) $(HYPERVISOR_CLH) + +QEMUPATH := $(QEMUBINDIR)/$(QEMUCMD) + +QEMUVIRTIOFSPATH := $(QEMUBINDIR)/$(QEMUVIRTIOFSCMD) + +CLHPATH := $(CLHBINDIR)/$(CLHCMD) + +FCPATH = $(FCBINDIR)/$(FCCMD) +FCJAILERPATH = $(FCBINDIR)/$(FCJAILERCMD) + +ACRNPATH := $(ACRNBINDIR)/$(ACRNCMD) +ACRNCTLPATH := $(ACRNBINDIR)/$(ACRNCTLCMD) + +SHIMCMD := $(BIN_PREFIX)-shim +SHIMPATH := $(PKGLIBEXECDIR)/$(SHIMCMD) + +PROXYCMD := $(BIN_PREFIX)-proxy +PROXYPATH := $(PKGLIBEXECDIR)/$(PROXYCMD) + +NETMONCMD := $(BIN_PREFIX)-netmon +NETMONPATH := $(PKGLIBEXECDIR)/$(NETMONCMD) + +# Default number of vCPUs +DEFVCPUS := 1 +# Default maximum number of vCPUs +DEFMAXVCPUS := 0 +# Default memory size in MiB +DEFMEMSZ := 2048 +# Default memory slots +# Cases to consider : +# - nvdimm rootfs image +# - preallocated memory +# - vm template memory +# - hugepage memory +DEFMEMSLOTS := 10 +#Default number of bridges +DEFBRIDGES := 1 +DEFDISABLEGUESTSECCOMP := true +#Default experimental features enabled +DEFAULTEXPFEATURES := [] + +#Default entropy source +DEFENTROPYSOURCE := /dev/urandom + +DEFDISABLEBLOCK := false +DEFSHAREDFS := virtio-9p +DEFSHAREDFS_QEMU_VIRTIOFS := virtio-fs +DEFVIRTIOFSDAEMON := $(VIRTIOFSDBINDIR)/virtiofsd +# Default DAX mapping cache size in MiB +DEFVIRTIOFSCACHESIZE := 1024 +DEFVIRTIOFSCACHE := always +DEFVIRTIOFSEXTRAARGS := [] +DEFENABLEIOTHREADS := false +DEFENABLEMEMPREALLOC := false +DEFENABLEHUGEPAGES := false +DEFENABLEVHOSTUSERSTORE := false +DEFVHOSTUSERSTOREPATH := $(PKGRUNDIR)/vhost-user +DEFENABLESWAP := false +DEFENABLEDEBUG := false +DEFDISABLENESTINGCHECKS := false +DEFMSIZE9P := 8192 +DEFHOTPLUGVFIOONROOTBUS := false +DEFPCIEROOTPORT := 0 + +# Default cgroup model +DEFSANDBOXCGROUPONLY ?= false + +# Features +FEATURE_SELINUX ?= check + +SED = sed + +CLI_DIR = cli +SHIMV2 = containerd-shim-kata-v2 +SHIMV2_OUTPUT = $(CURDIR)/$(SHIMV2) +SHIMV2_DIR = $(CLI_DIR)/$(SHIMV2) + +SOURCES := $(shell find . 2>&1 | grep -E '.*\.(c|h|go)$$') +VERSION := ${shell cat ./VERSION} + +# List of configuration files to build and install +CONFIGS = +CONFIG_PATHS = +SYSCONFIG_PATHS = + +# List of hypervisors known for the current architecture +KNOWN_HYPERVISORS = + +ifneq (,$(QEMUCMD)) + KNOWN_HYPERVISORS += $(HYPERVISOR_QEMU) + + CONFIG_FILE_QEMU = configuration-qemu.toml + CONFIG_QEMU = $(CLI_DIR)/config/$(CONFIG_FILE_QEMU) + CONFIG_QEMU_IN = $(CONFIG_QEMU).in + + CONFIG_PATH_QEMU = $(abspath $(CONFDIR)/$(CONFIG_FILE_QEMU)) + CONFIG_PATHS += $(CONFIG_PATH_QEMU) + + SYSCONFIG_QEMU = $(abspath $(SYSCONFDIR)/$(CONFIG_FILE_QEMU)) + SYSCONFIG_PATHS += $(SYSCONFIG_QEMU) + + CONFIGS += $(CONFIG_QEMU) + + # qemu-specific options (all should be suffixed by "_QEMU") + DEFBLOCKSTORAGEDRIVER_QEMU := virtio-scsi + DEFNETWORKMODEL_QEMU := tcfilter + KERNELNAME = $(call MAKE_KERNEL_NAME,$(KERNELTYPE)) + KERNELPATH = $(KERNELDIR)/$(KERNELNAME) +endif + +ifneq (,$(QEMUVIRTIOFSCMD)) + KNOWN_HYPERVISORS += $(HYPERVISOR_QEMU_VIRTIOFS) + + CONFIG_FILE_QEMU_VIRTIOFS = configuration-qemu-virtiofs.toml + CONFIG_QEMU_VIRTIOFS = $(CLI_DIR)/config/$(CONFIG_FILE_QEMU_VIRTIOFS) + CONFIG_QEMU_VIRTIOFS_IN = $(CONFIG_QEMU_VIRTIOFS).in + + CONFIG_PATH_QEMU_VIRTIOFS = $(abspath $(CONFDIR)/$(CONFIG_FILE_QEMU_VIRTIOFS)) + CONFIG_PATHS += $(CONFIG_PATH_QEMU_VIRTIOFS) + + SYSCONFIG_QEMU_VIRTIOFS = $(abspath $(SYSCONFDIR)/$(CONFIG_FILE_QEMU_VIRTIOFS)) + SYSCONFIG_PATHS += $(SYSCONFIG_QEMU_VIRTIOFS) + + CONFIGS += $(CONFIG_QEMU_VIRTIOFS) + + # qemu-specific options (all should be suffixed by "_QEMU") + DEFBLOCKSTORAGEDRIVER_QEMU_VIRTIOFS := virtio-fs + DEFNETWORKMODEL_QEMU := tcfilter + KERNELNAMEVIRTIOFS = $(call MAKE_KERNEL_VIRTIOFS_NAME,$(KERNELTYPE)) + KERNELVIRTIOFSPATH = $(KERNELDIR)/$(KERNELNAMEVIRTIOFS) +endif + +ifneq (,$(CLHCMD)) + KNOWN_HYPERVISORS += $(HYPERVISOR_CLH) + + CONFIG_FILE_CLH = configuration-clh.toml + CONFIG_CLH = $(CLI_DIR)/config/$(CONFIG_FILE_CLH) + CONFIG_CLH_IN = $(CONFIG_CLH).in + + CONFIG_PATH_CLH = $(abspath $(CONFDIR)/$(CONFIG_FILE_CLH)) + CONFIG_PATHS += $(CONFIG_PATH_CLH) + + SYSCONFIG_CLH = $(abspath $(SYSCONFDIR)/$(CONFIG_FILE_CLH)) + SYSCONFIG_PATHS += $(SYSCONFIG_CLH) + + CONFIGS += $(CONFIG_CLH) + + # CLH-specific options (all should be suffixed by "_CLH") + # currently, huge pages are required for virtiofsd support + DEFENABLEHUGEPAGES_CLH := true + DEFNETWORKMODEL_CLH := tcfilter + KERNELTYPE_CLH = uncompressed + KERNEL_NAME_CLH = $(call MAKE_KERNEL_VIRTIOFS_NAME,$(KERNELTYPE_CLH)) + KERNELPATH_CLH = $(KERNELDIR)/$(KERNEL_NAME_CLH) +endif + +ifneq (,$(FCCMD)) + KNOWN_HYPERVISORS += $(HYPERVISOR_FC) + + CONFIG_FILE_FC = configuration-fc.toml + CONFIG_FC = $(CLI_DIR)/config/$(CONFIG_FILE_FC) + CONFIG_FC_IN = $(CONFIG_FC).in + + CONFIG_PATH_FC = $(abspath $(CONFDIR)/$(CONFIG_FILE_FC)) + CONFIG_PATHS += $(CONFIG_PATH_FC) + + SYSCONFIG_FC = $(abspath $(SYSCONFDIR)/$(CONFIG_FILE_FC)) + SYSCONFIG_PATHS += $(SYSCONFIG_FC) + + CONFIGS += $(CONFIG_FC) + + # firecracker-specific options (all should be suffixed by "_FC") + DEFBLOCKSTORAGEDRIVER_FC := virtio-mmio + DEFNETWORKMODEL_FC := tcfilter + KERNELTYPE_FC = uncompressed + KERNEL_NAME_FC = $(call MAKE_KERNEL_NAME,$(KERNELTYPE_FC)) + KERNELPATH_FC = $(KERNELDIR)/$(KERNEL_NAME_FC) +endif + +ifneq (,$(ACRNCMD)) + KNOWN_HYPERVISORS += $(HYPERVISOR_ACRN) + + CONFIG_FILE_ACRN = configuration-acrn.toml + CONFIG_ACRN = $(CLI_DIR)/config/$(CONFIG_FILE_ACRN) + CONFIG_ACRN_IN = $(CONFIG_ACRN).in + + CONFIG_PATH_ACRN = $(abspath $(CONFDIR)/$(CONFIG_FILE_ACRN)) + CONFIG_PATHS += $(CONFIG_PATH_ACRN) + + SYSCONFIG_ACRN = $(abspath $(SYSCONFDIR)/$(CONFIG_FILE_ACRN)) + SYSCONFIG_PATHS += $(SYSCONFIG_ACRN) + + CONFIGS += $(CONFIG_ACRN) + + # acrn-specific options (all should be suffixed by "_ACRN") + DEFMAXVCPUS_ACRN := 1 + DEFBLOCKSTORAGEDRIVER_ACRN := virtio-blk + DEFNETWORKMODEL_ACRN := macvtap + KERNEL_NAME_ACRN = $(call MAKE_KERNEL_NAME,$(KERNELTYPE)) + KERNELPATH_ACRN = $(KERNELDIR)/$(KERNEL_NAME_ACRN) +endif + +ifeq (,$(KNOWN_HYPERVISORS)) + $(error "ERROR: No hypervisors known for architecture $(ARCH) (looked for: $(HYPERVISORS))") +endif + +ifeq (,$(findstring $(DEFAULT_HYPERVISOR),$(HYPERVISORS))) + $(error "ERROR: Invalid default hypervisor: '$(DEFAULT_HYPERVISOR)'") +endif + +ifeq (,$(findstring $(DEFAULT_HYPERVISOR),$(KNOWN_HYPERVISORS))) + $(error "ERROR: Default hypervisor '$(DEFAULT_HYPERVISOR)' not known for architecture $(ARCH)") +endif + +ifeq ($(DEFAULT_HYPERVISOR),$(HYPERVISOR_QEMU)) + DEFAULT_HYPERVISOR_CONFIG = $(CONFIG_FILE_QEMU) +endif + +ifeq ($(DEFAULT_HYPERVISOR),$(HYPERVISOR_QEMU_VIRTIOFS)) + DEFAULT_HYPERVISOR_CONFIG = $(CONFIG_FILE_QEMU_VIRTIOFS) +endif + +ifeq ($(DEFAULT_HYPERVISOR),$(HYPERVISOR_FC)) + DEFAULT_HYPERVISOR_CONFIG = $(CONFIG_FILE_FC) +endif + +ifeq ($(DEFAULT_HYPERVISOR),$(HYPERVISOR_ACRN)) + DEFAULT_HYPERVISOR_CONFIG = $(CONFIG_FILE_ACRN) +endif + +ifeq ($(DEFAULT_HYPERVISOR),$(HYPERVISOR_CLH)) + DEFAULT_HYPERVISOR_CONFIG = $(CONFIG_FILE_CLH) +endif + +CONFDIR := $(DEFAULTSDIR)/$(PROJECT_DIR) +SYSCONFDIR := $(SYSCONFDIR)/$(PROJECT_DIR) + +# Main configuration file location for stateless systems +CONFIG_PATH := $(abspath $(CONFDIR)/$(CONFIG_FILE)) + +# Secondary configuration file location. Note that this takes precedence +# over CONFIG_PATH. +SYSCONFIG := $(abspath $(SYSCONFDIR)/$(CONFIG_FILE)) + +SHAREDIR := $(SHAREDIR) + +# list of variables the user may wish to override +USER_VARS += ARCH +USER_VARS += BINDIR +USER_VARS += CONFIG_PATH +USER_VARS += DESTDIR +USER_VARS += DEFAULT_HYPERVISOR +USER_VARS += ACRNCMD +USER_VARS += ACRNCTLCMD +USER_VARS += ACRNPATH +USER_VARS += ACRNCTLPATH +USER_VARS += FCCMD +USER_VARS += FCPATH +USER_VARS += FCJAILERPATH +USER_VARS += SYSCONFIG +USER_VARS += IMAGENAME +USER_VARS += IMAGEPATH +USER_VARS += INITRDNAME +USER_VARS += INITRDPATH +USER_VARS += MACHINETYPE +USER_VARS += KERNELDIR +USER_VARS += KERNELTYPE +USER_VARS += KERNELTYPE_FC +USER_VARS += KERNELTYPE_ACRN +USER_VARS += KERNELTYPE_CLH +USER_VARS += FIRMWAREPATH +USER_VARS += MACHINEACCELERATORS +USER_VARS += DEFMACHINETYPE_CLH +USER_VARS += KERNELPARAMS +USER_VARS += LIBEXECDIR +USER_VARS += LOCALSTATEDIR +USER_VARS += PKGDATADIR +USER_VARS += PKGLIBDIR +USER_VARS += PKGLIBEXECDIR +USER_VARS += PKGRUNDIR +USER_VARS += PREFIX +USER_VARS += PROJECT_NAME +USER_VARS += PROJECT_PREFIX +USER_VARS += PROJECT_TYPE +USER_VARS += PROXYPATH +USER_VARS += NETMONPATH +USER_VARS += QEMUBINDIR +USER_VARS += QEMUCMD +USER_VARS += QEMUPATH +USER_VARS += QEMUVIRTIOFSCMD +USER_VARS += QEMUVIRTIOFSPATH +USER_VARS += SHAREDIR +USER_VARS += SHIMPATH +USER_VARS += SYSCONFDIR +USER_VARS += DEFVCPUS +USER_VARS += DEFMAXVCPUS +USER_VARS += DEFMAXVCPUS_ACRN +USER_VARS += DEFMEMSZ +USER_VARS += DEFMEMSLOTS +USER_VARS += DEFBRIDGES +USER_VARS += DEFNETWORKMODEL_ACRN +USER_VARS += DEFNETWORKMODEL_FC +USER_VARS += DEFNETWORKMODEL_QEMU +USER_VARS += DEFDISABLEGUESTSECCOMP +USER_VARS += DEFAULTEXPFEATURES +USER_VARS += DEFDISABLEBLOCK +USER_VARS += DEFBLOCKSTORAGEDRIVER_ACRN +USER_VARS += DEFBLOCKSTORAGEDRIVER_FC +USER_VARS += DEFBLOCKSTORAGEDRIVER_QEMU +USER_VARS += DEFBLOCKSTORAGEDRIVER_QEMU_VIRTIOFS +USER_VARS += DEFSHAREDFS +USER_VARS += DEFSHAREDFS_QEMU_VIRTIOFS +USER_VARS += DEFVIRTIOFSDAEMON +USER_VARS += DEFVIRTIOFSCACHESIZE +USER_VARS += DEFVIRTIOFSCACHE +USER_VARS += DEFVIRTIOFSEXTRAARGS +USER_VARS += DEFENABLEIOTHREADS +USER_VARS += DEFENABLEMEMPREALLOC +USER_VARS += DEFENABLEHUGEPAGES +USER_VARS += DEFENABLEVHOSTUSERSTORE +USER_VARS += DEFVHOSTUSERSTOREPATH +USER_VARS += DEFENABLESWAP +USER_VARS += DEFENABLEDEBUG +USER_VARS += DEFDISABLENESTINGCHECKS +USER_VARS += DEFMSIZE9P +USER_VARS += DEFHOTPLUGVFIOONROOTBUS +USER_VARS += DEFPCIEROOTPORT +USER_VARS += DEFENTROPYSOURCE +USER_VARS += DEFSANDBOXCGROUPONLY +USER_VARS += FEATURE_SELINUX +USER_VARS += BUILDFLAGS + + +V = @ +Q = $(V:1=) +QUIET_BUILD = $(Q:@=@echo ' BUILD '$@;) +QUIET_CHECK = $(Q:@=@echo ' CHECK '$@;) +QUIET_CLEAN = $(Q:@=@echo ' CLEAN '$@;) +QUIET_GENERATE = $(Q:@=@echo ' GENERATE '$@;) +QUIET_INST = $(Q:@=@echo ' INSTALL '$@;) +QUIET_TEST = $(Q:@=@echo ' TEST '$@;) + +BUILDTAGS := + +ifneq ($(FEATURE_SELINUX),no) + SELINUXTAG := $(shell ./hack/selinux_tag.sh) + + ifneq ($(SELINUXTAG),) + override FEATURE_SELINUX = yes + BUILDTAGS += --tags "$(SELINUXTAG)" + else + ifeq ($(FEATURE_SELINUX),yes) + $(error "ERROR: SELinux support requested, but libselinux is not available") + endif + + override FEATURE_SELINUX = no + endif +endif + +# go build common flags +BUILDFLAGS := -buildmode=pie ${BUILDTAGS} + +# whether stipping the binary +ifeq ($(STRIP),yes) + KATA_LDFLAGS := -ldflags "-w -s" +endif + +# Return non-empty string if specified directory exists +define DIR_EXISTS +$(shell test -d $(1) && echo "$(1)") +endef + +# $1: name of architecture to display +define SHOW_ARCH + $(shell printf "\\t%s%s\\\n" "$(1)" $(if $(filter $(ARCH),$(1))," (default)","")) +endef + +all: runtime containerd-shim-v2 netmon + +# Targets that depend on .git-commit can use $(shell cat .git-commit) to get a +# git revision string. They will only be rebuilt if the revision string +# actually changes. +.PHONY: .git-commit.tmp +.git-commit: .git-commit.tmp + @cmp $< $@ >/dev/null 2>&1 || cp $< $@ +.git-commit.tmp: + @echo -n "$$(git rev-parse HEAD 2>/dev/null)" >$@ + @test -n "$$(git status --porcelain --untracked-files=no)" && echo -n "-dirty" >>$@ || true + +containerd-shim-v2: $(SHIMV2_OUTPUT) + +netmon: $(NETMON_TARGET_OUTPUT) + +$(NETMON_TARGET_OUTPUT): $(SOURCES) VERSION + $(QUIET_BUILD)(cd $(NETMON_DIR) && go build $(BUILDFLAGS) -o $@ -ldflags "-X main.version=$(VERSION)" $(KATA_LDFLAGS)) + +runtime: $(TARGET_OUTPUT) $(CONFIGS) +.DEFAULT: default + +build: default + +#Install an executable file +# params: +# $1 : file to install +# $2 : directory path where file will be installed +define INSTALL_EXEC + install -D $1 $(DESTDIR)$2/$(notdir $1); +endef + +# Install a configuration file +# params: +# $1 : file to install +# $2 : directory path where file will be installed +define INSTALL_CONFIG + install --mode 0644 -D $1 $(DESTDIR)$2/$(notdir $1); +endef + +# Returns the name of the kernel file to use based on the provided KERNELTYPE. +# $1 : KERNELTYPE (compressed or uncompressed) +define MAKE_KERNEL_NAME +$(if $(findstring uncompressed,$1),vmlinux.container,vmlinuz.container) +endef + +define MAKE_KERNEL_VIRTIOFS_NAME +$(if $(findstring uncompressed,$1),vmlinux-virtiofs.container,vmlinuz-virtiofs.container) +endef + + +GENERATED_FILES += $(CLI_DIR)/config-generated.go +GENERATED_FILES += pkg/katautils/config-settings.go + +$(TARGET_OUTPUT): $(SOURCES) $(GENERATED_FILES) $(MAKEFILE_LIST) | show-summary + $(QUIET_BUILD)(cd $(CLI_DIR) && go build $(KATA_LDFLAGS) $(BUILDFLAGS) -o $@ .) + +$(SHIMV2_OUTPUT): $(SOURCES) $(GENERATED_FILES) $(MAKEFILE_LIST) + $(QUIET_BUILD)(cd $(SHIMV2_DIR)/ && go build $(KATA_LDFLAGS) -i -o $@ .) + +.PHONY: \ + check \ + check-go-static \ + check-go-test \ + coverage \ + default \ + install \ + show-header \ + show-summary \ + show-variables + +$(TARGET).coverage: $(SOURCES) $(GENERATED_FILES) $(MAKEFILE_LIST) + $(QUIET_TEST)go test -o $@ -covermode count + +GENERATED_FILES += $(CONFIGS) + +$(GENERATED_FILES): %: %.in $(MAKEFILE_LIST) VERSION .git-commit + $(QUIET_GENERATE)$(SED) \ + -e "s|@COMMIT@|$(shell cat .git-commit)|g" \ + -e "s|@VERSION@|$(VERSION)|g" \ + -e "s|@CONFIG_ACRN_IN@|$(CONFIG_ACRN_IN)|g" \ + -e "s|@CONFIG_QEMU_IN@|$(CONFIG_QEMU_IN)|g" \ + -e "s|@CONFIG_QEMU_VIRTIOFS_IN@|$(CONFIG_QEMU_VIRTIOFS_IN)|g" \ + -e "s|@CONFIG_CLH_IN@|$(CONFIG_CLH_IN)|g" \ + -e "s|@CONFIG_FC_IN@|$(CONFIG_FC_IN)|g" \ + -e "s|@CONFIG_PATH@|$(CONFIG_PATH)|g" \ + -e "s|@FCPATH@|$(FCPATH)|g" \ + -e "s|@FCJAILERPATH@|$(FCJAILERPATH)|g" \ + -e "s|@ACRNPATH@|$(ACRNPATH)|g" \ + -e "s|@ACRNCTLPATH@|$(ACRNCTLPATH)|g" \ + -e "s|@CLHPATH@|$(CLHPATH)|g" \ + -e "s|@SYSCONFIG@|$(SYSCONFIG)|g" \ + -e "s|@IMAGEPATH@|$(IMAGEPATH)|g" \ + -e "s|@KERNELPATH_ACRN@|$(KERNELPATH_ACRN)|g" \ + -e "s|@KERNELPATH_FC@|$(KERNELPATH_FC)|g" \ + -e "s|@KERNELPATH_CLH@|$(KERNELPATH_CLH)|g" \ + -e "s|@KERNELPATH@|$(KERNELPATH)|g" \ + -e "s|@KERNELVIRTIOFSPATH@|$(KERNELVIRTIOFSPATH)|g" \ + -e "s|@INITRDPATH@|$(INITRDPATH)|g" \ + -e "s|@FIRMWAREPATH@|$(FIRMWAREPATH)|g" \ + -e "s|@MACHINEACCELERATORS@|$(MACHINEACCELERATORS)|g" \ + -e "s|@FIRMWAREPATH_CLH@|$(FIRMWAREPATH_CLH)|g" \ + -e "s|@DEFMACHINETYPE_CLH@|$(DEFMACHINETYPE_CLH)|g" \ + -e "s|@KERNELPARAMS@|$(KERNELPARAMS)|g" \ + -e "s|@LOCALSTATEDIR@|$(LOCALSTATEDIR)|g" \ + -e "s|@PKGLIBEXECDIR@|$(PKGLIBEXECDIR)|g" \ + -e "s|@PKGRUNDIR@|$(PKGRUNDIR)|g" \ + -e "s|@PROXYPATH@|$(PROXYPATH)|g" \ + -e "s|@NETMONPATH@|$(NETMONPATH)|g" \ + -e "s|@PROJECT_BUG_URL@|$(PROJECT_BUG_URL)|g" \ + -e "s|@PROJECT_URL@|$(PROJECT_URL)|g" \ + -e "s|@PROJECT_NAME@|$(PROJECT_NAME)|g" \ + -e "s|@PROJECT_TAG@|$(PROJECT_TAG)|g" \ + -e "s|@PROJECT_TYPE@|$(PROJECT_TYPE)|g" \ + -e "s|@QEMUPATH@|$(QEMUPATH)|g" \ + -e "s|@QEMUVIRTIOFSPATH@|$(QEMUVIRTIOFSPATH)|g" \ + -e "s|@RUNTIME_NAME@|$(TARGET)|g" \ + -e "s|@MACHINETYPE@|$(MACHINETYPE)|g" \ + -e "s|@SHIMPATH@|$(SHIMPATH)|g" \ + -e "s|@DEFVCPUS@|$(DEFVCPUS)|g" \ + -e "s|@DEFMAXVCPUS@|$(DEFMAXVCPUS)|g" \ + -e "s|@DEFMAXVCPUS_ACRN@|$(DEFMAXVCPUS_ACRN)|g" \ + -e "s|@DEFMEMSZ@|$(DEFMEMSZ)|g" \ + -e "s|@DEFMEMSLOTS@|$(DEFMEMSLOTS)|g" \ + -e "s|@DEFBRIDGES@|$(DEFBRIDGES)|g" \ + -e "s|@DEFNETWORKMODEL_ACRN@|$(DEFNETWORKMODEL_ACRN)|g" \ + -e "s|@DEFNETWORKMODEL_CLH@|$(DEFNETWORKMODEL_CLH)|g" \ + -e "s|@DEFNETWORKMODEL_FC@|$(DEFNETWORKMODEL_FC)|g" \ + -e "s|@DEFNETWORKMODEL_QEMU@|$(DEFNETWORKMODEL_QEMU)|g" \ + -e "s|@DEFDISABLEGUESTSECCOMP@|$(DEFDISABLEGUESTSECCOMP)|g" \ + -e "s|@DEFAULTEXPFEATURES@|$(DEFAULTEXPFEATURES)|g" \ + -e "s|@DEFDISABLEBLOCK@|$(DEFDISABLEBLOCK)|g" \ + -e "s|@DEFBLOCKSTORAGEDRIVER_ACRN@|$(DEFBLOCKSTORAGEDRIVER_ACRN)|g" \ + -e "s|@DEFBLOCKSTORAGEDRIVER_FC@|$(DEFBLOCKSTORAGEDRIVER_FC)|g" \ + -e "s|@DEFBLOCKSTORAGEDRIVER_QEMU@|$(DEFBLOCKSTORAGEDRIVER_QEMU)|g" \ + -e "s|@DEFBLOCKSTORAGEDRIVER_QEMU_VIRTIOFS@|$(DEFBLOCKSTORAGEDRIVER_QEMU_VIRTIOFS)|g" \ + -e "s|@DEFSHAREDFS@|$(DEFSHAREDFS)|g" \ + -e "s|@DEFSHAREDFS_QEMU_VIRTIOFS@|$(DEFSHAREDFS_QEMU_VIRTIOFS)|g" \ + -e "s|@DEFVIRTIOFSDAEMON@|$(DEFVIRTIOFSDAEMON)|g" \ + -e "s|@DEFVIRTIOFSCACHESIZE@|$(DEFVIRTIOFSCACHESIZE)|g" \ + -e "s|@DEFVIRTIOFSCACHE@|$(DEFVIRTIOFSCACHE)|g" \ + -e "s|@DEFVIRTIOFSEXTRAARGS@|$(DEFVIRTIOFSEXTRAARGS)|g" \ + -e "s|@DEFENABLEIOTHREADS@|$(DEFENABLEIOTHREADS)|g" \ + -e "s|@DEFENABLEMEMPREALLOC@|$(DEFENABLEMEMPREALLOC)|g" \ + -e "s|@DEFENABLEHUGEPAGES@|$(DEFENABLEHUGEPAGES)|g" \ + -e "s|@DEFENABLEVHOSTUSERSTORE@|$(DEFENABLEVHOSTUSERSTORE)|g" \ + -e "s|@DEFVHOSTUSERSTOREPATH@|$(DEFVHOSTUSERSTOREPATH)|g" \ + -e "s|@DEFENABLEMSWAP@|$(DEFENABLESWAP)|g" \ + -e "s|@DEFENABLEDEBUG@|$(DEFENABLEDEBUG)|g" \ + -e "s|@DEFDISABLENESTINGCHECKS@|$(DEFDISABLENESTINGCHECKS)|g" \ + -e "s|@DEFMSIZE9P@|$(DEFMSIZE9P)|g" \ + -e "s|@DEFHOTPLUGVFIOONROOTBUS@|$(DEFHOTPLUGVFIOONROOTBUS)|g" \ + -e "s|@DEFPCIEROOTPORT@|$(DEFPCIEROOTPORT)|g" \ + -e "s|@DEFENTROPYSOURCE@|$(DEFENTROPYSOURCE)|g" \ + -e "s|@DEFSANDBOXCGROUPONLY@|$(DEFSANDBOXCGROUPONLY)|g" \ + -e "s|@FEATURE_SELINUX@|$(FEATURE_SELINUX)|g" \ + $< > $@ + +generate-config: $(CONFIGS) + +check: check-go-static + +test: go-test + +go-test: $(GENERATED_FILES) + go test -v -mod=vendor ./... + +check-go-static: + $(QUIET_CHECK).ci/static-checks.sh + $(QUIET_CHECK).ci/go-no-os-exit.sh ./cli + $(QUIET_CHECK).ci/go-no-os-exit.sh ./virtcontainers + +coverage: + go test -v -mod=vendor -covermode=atomic -coverprofile=coverage.txt ./... + go tool cover -html=coverage.txt -o coverage.html + +install: default install-runtime install-containerd-shim-v2 install-netmon + +install-bin: $(BINLIST) + $(QUIET_INST)$(foreach f,$(BINLIST),$(call INSTALL_EXEC,$f,$(BINDIR))) + +install-runtime: runtime install-scripts install-completions install-configs install-bin + +install-netmon: install-bin-libexec + +install-containerd-shim-v2: $(SHIMV2) + $(QUIET_INST)$(call INSTALL_EXEC,$<,$(BINDIR)) + +install-bin-libexec: $(BINLIBEXECLIST) + $(QUIET_INST)$(foreach f,$(BINLIBEXECLIST),$(call INSTALL_EXEC,$f,$(PKGLIBEXECDIR))) + +install-configs: $(CONFIGS) + $(QUIET_INST)$(foreach f,$(CONFIGS),$(call INSTALL_CONFIG,$f,$(dir $(CONFIG_PATH)))) + $(QUIET_INST)ln -sf $(DEFAULT_HYPERVISOR_CONFIG) $(DESTDIR)/$(CONFIG_PATH) + +install-scripts: $(SCRIPTS) + $(QUIET_INST)$(foreach f,$(SCRIPTS),$(call INSTALL_EXEC,$f,$(SCRIPTS_DIR))) + +install-completions: + $(QUIET_INST)install --mode 0644 -D $(BASH_COMPLETIONS) $(DESTDIR)/$(BASH_COMPLETIONSDIR)/$(notdir $(BASH_COMPLETIONS)); + +clean: + $(QUIET_CLEAN)rm -f $(TARGET) $(SHIMV2) $(NETMON_TARGET) $(CONFIGS) $(GENERATED_FILES) .git-commit .git-commit.tmp + +show-usage: show-header + @printf "• Overview:\n" + @printf "\n" + @printf "\tTo build $(TARGET), just run, \"make\".\n" + @printf "\n" + @printf "\tFor a verbose build, run \"make V=1\".\n" + @printf "\n" + @printf "• Additional targets:\n" + @printf "\n" + @printf "\tbuild : standard build (build everything).\n" + @printf "\tcheck : run tests.\n" + @printf "\tclean : remove built files.\n" + @printf "\tcontainerd-shim-v2 : only build containerd shim v2.\n" + @printf "\tcoverage : run coverage tests.\n" + @printf "\tdefault : same as 'make build' (or just 'make').\n" + @printf "\tgenerate-config : create configuration file.\n" + @printf "\tinstall : install everything.\n" + @printf "\tinstall-containerd-shim-v2 : only install containerd shim v2 files.\n" + @printf "\tinstall-netmon : only install netmon files.\n" + @printf "\tinstall-runtime : only install runtime files.\n" + @printf "\tnetmon : only build netmon.\n" + @printf "\truntime : only build runtime.\n" + @printf "\tshow-arches : show supported architectures (ARCH variable values).\n" + @printf "\tshow-summary : show install locations.\n" + @printf "\n" + +handle_help: show-usage show-summary show-variables show-footer + +usage: handle_help +help: handle_help + +show-variables: + @printf "• Variables affecting the build:\n\n" + @printf \ + "$(foreach v,$(sort $(USER_VARS)),$(shell printf "\\t$(v)='$($(v))'\\\n"))" + @printf "\n" + +show-header: .git-commit + @printf "%s - version %s (commit %s)\n\n" $(TARGET) $(VERSION) $(shell cat .git-commit) + +show-arches: show-header + @printf "Supported architectures (possible values for ARCH variable):\n\n" + @printf \ + "$(foreach v,$(ALL_ARCHES),$(call SHOW_ARCH,$(v)))\n" + +show-footer: + @printf "• Project:\n" + @printf "\tHome: $(PROJECT_URL)\n" + @printf "\tBugs: $(PROJECT_BUG_URL)\n\n" + +show-summary: show-header +ifneq (,$(golang_version_raw)) + @printf "• architecture:\n" + @printf "\tHost: $(HOST_ARCH)\n" + @printf "\tgolang: $(GOARCH)\n" + @printf "\tBuild: $(ARCH)\n" + @printf "\n" + @printf "• golang:\n" + @printf "\t" + @go version +else + @printf "• No GO command or GOPATH not set:\n" + @printf "\tCan only install prebuilt binaries\n" +endif + @printf "\n" + @printf "• hypervisors:\n" + @printf "\tDefault: $(DEFAULT_HYPERVISOR)\n" + @printf "\tKnown: $(sort $(HYPERVISORS))\n" + @printf "\tAvailable for this architecture: $(sort $(KNOWN_HYPERVISORS))\n" + @printf "\n" + @printf "• Features:\n" + @printf "\tSELinux (FEATURE_SELINUX): $(FEATURE_SELINUX)\n" + @printf "\n" + @printf "• Summary:\n" + @printf "\n" + @printf "\tdestination install path (DESTDIR) : %s\n" $(abspath $(DESTDIR)) + @printf "\tbinary installation path (BINDIR) : %s\n" $(abspath $(BINDIR)) + @printf "\tbinaries to install :\n" + @printf \ + "$(foreach b,$(sort $(BINLIST)),$(shell printf "\\t - $(shell readlink -m $(DESTDIR)/$(BINDIR)/$(b))\\\n"))" + @printf \ + "$(foreach b,$(sort $(SHIMV2)),$(shell printf "\\t - $(shell readlink -m $(DESTDIR)/$(BINDIR)/$(b))\\\n"))" + @printf \ + "$(foreach b,$(sort $(BINLIBEXECLIST)),$(shell printf "\\t - $(shell readlink -m $(DESTDIR)/$(PKGLIBEXECDIR)/$(b))\\\n"))" + @printf \ + "$(foreach s,$(sort $(SCRIPTS)),$(shell printf "\\t - $(shell readlink -m $(DESTDIR)/$(BINDIR)/$(s))\\\n"))" + @printf "\tconfigs to install (CONFIGS) :\n" + @printf \ + "$(foreach c,$(sort $(CONFIGS)),$(shell printf "\\t - $(c)\\\n"))" + @printf "\tinstall paths (CONFIG_PATHS) :\n" + @printf \ + "$(foreach c,$(sort $(CONFIG_PATHS)),$(shell printf "\\t - $(c)\\\n"))" + @printf "\talternate config paths (SYSCONFIG_PATHS) : %s\n" + @printf \ + "$(foreach c,$(sort $(SYSCONFIG_PATHS)),$(shell printf "\\t - $(c)\\\n"))" + + @printf "\tdefault install path for $(DEFAULT_HYPERVISOR) (CONFIG_PATH) : %s\n" $(abspath $(CONFIG_PATH)) + @printf "\tdefault alternate config path (SYSCONFIG) : %s\n" $(abspath $(SYSCONFIG)) +ifneq (,$(findstring $(HYPERVISOR_QEMU),$(KNOWN_HYPERVISORS))) + @printf "\t$(HYPERVISOR_QEMU) hypervisor path (QEMUPATH) : %s\n" $(abspath $(QEMUPATH)) +endif +ifneq (,$(findstring $(HYPERVISOR_QEMU_VIRTIOFS),$(KNOWN_HYPERVISORS))) + @printf "\t$(HYPERVISOR_QEMU_VIRTIOFS) hypervisor path (QEMUVIRTIOFSPATH) : %s\n" $(abspath $(QEMUVIRTIOFSPATH)) +endif +ifneq (,$(findstring $(HYPERVISOR_CLH),$(KNOWN_HYPERVISORS))) + @printf "\t$(HYPERVISOR_CLH) hypervisor path (CLHPATH) : %s\n" $(abspath $(CLHPATH)) +endif +ifneq (,$(findstring $(HYPERVISOR_FC),$(KNOWN_HYPERVISORS))) + @printf "\t$(HYPERVISOR_FC) hypervisor path (FCPATH) : %s\n" $(abspath $(FCPATH)) +endif +ifneq (,$(findstring $(HYPERVISOR_ACRN),$(KNOWN_HYPERVISORS))) + @printf "\t$(HYPERVISOR_ACRN) hypervisor path (ACRNPATH) : %s\n" $(abspath $(ACRNPATH)) +endif + @printf "\tassets path (PKGDATADIR) : %s\n" $(abspath $(PKGDATADIR)) + @printf "\tproxy+shim path (PKGLIBEXECDIR) : %s\n" $(abspath $(PKGLIBEXECDIR)) + @printf "\n" diff --git a/src/runtime/README.md b/src/runtime/README.md new file mode 100644 index 000000000..5c49f1b3e --- /dev/null +++ b/src/runtime/README.md @@ -0,0 +1,203 @@ +[![Build Status](https://travis-ci.org/kata-containers/runtime.svg?branch=master)](https://travis-ci.org/kata-containers/runtime) +[![Build Status](http://jenkins.katacontainers.io/job/kata-containers-runtime-ubuntu-18-04-master/badge/icon)](http://jenkins.katacontainers.io/job/kata-containers-runtime-ubuntu-18-04-master/) +[![Go Report Card](https://goreportcard.com/badge/github.com/kata-containers/runtime)](https://goreportcard.com/report/github.com/kata-containers/runtime) +[![GoDoc](https://godoc.org/github.com/kata-containers/runtime?status.svg)](https://godoc.org/github.com/kata-containers/runtime) + +# Runtime + +This repository contains the runtime for the +[Kata Containers](https://github.com/kata-containers) project. + +For details of the other Kata Containers repositories, see the +[repository summary](https://github.com/kata-containers/kata-containers). + +* [Introduction](#introduction) +* [License](#license) +* [Platform support](#platform-support) + * [Hardware requirements](#hardware-requirements) +* [Download and install](#download-and-install) +* [Quick start for developers](#quick-start-for-developers) +* [Architecture overview](#architecture-overview) +* [Configuration](#configuration) +* [Logging](#logging) + * [Kata OCI](#kata-oci) + * [Kata containerd shimv2](#kata-containerd-shimv2) +* [Debugging](#debugging) +* [Limitations](#limitations) +* [Community](#community) + * [Contact](#contact) +* [Further information](#further-information) +* [Additional packages](#additional-packages) + +## Introduction + +`kata-runtime`, referred to as "the runtime", is the Command-Line Interface +(CLI) part of the Kata Containers runtime component. It leverages the +[virtcontainers](virtcontainers) +package to provide a high-performance standards-compliant runtime that creates +hardware-virtualized [Linux](https://www.kernel.org/) containers running on Linux hosts. + +The runtime is +[OCI](https://github.com/opencontainers/runtime-spec)-compatible, +[CRI-O](https://github.com/cri-o/cri-o)-compatible, and +[Containerd](https://github.com/containerd/containerd)-compatible, + allowing it +to work seamlessly with both Docker and Kubernetes respectively. + +## License + +The code is licensed under an Apache 2.0 license. + +See [the license file](LICENSE) for further details. + +## Platform support + +Kata Containers currently works on systems supporting the following +technologies: + +- [Intel](https://www.intel.com) VT-x technology. +- [ARM](https://www.arm.com) Hyp mode (virtualization extension). +- [IBM](https://www.ibm.com) Power Systems. +- [IBM](https://www.ibm.com) Z mainframes. +### Hardware requirements + +The runtime has a built-in command to determine if your host system is capable +of running and creating a Kata Container: + +```bash +$ kata-runtime kata-check +``` + +> **Note:** +> +> - By default, only a brief success / failure message is printed. +> If more details are needed, the `--verbose` flag can be used to display the +> list of all the checks performed. +> +> - `root` permission is needed to check if the system is capable of running +> Kata containers. In this case, additional checks are performed (e.g., if another +> incompatible hypervisor is running). + +## Download and install + +[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/kata-containers) + +See the [installation guides](https://github.com/kata-containers/documentation/tree/master/install/README.md) +available for various operating systems. + +## Quick start for developers + +See the +[developer guide](https://github.com/kata-containers/documentation/blob/master/Developer-Guide.md). + +## Architecture overview + +See the [architecture overview](https://github.com/kata-containers/documentation/blob/master/design/architecture.md) +for details on the Kata Containers design. + +## Configuration + +The runtime uses a TOML format configuration file called `configuration.toml`. +The file contains comments explaining all options. + +> **Note:** +> +> The initial values in the configuration file provide a good default configuration. +> You may need to modify this file to optimise or tailor your system, or if you have +> specific requirements. + +Since the runtime supports a +[stateless system](https://clearlinux.org/about), +it checks for this configuration file in multiple locations, two of which are +built in to the runtime. The default location is +`/usr/share/defaults/kata-containers/configuration.toml` for a standard +system. However, if `/etc/kata-containers/configuration.toml` exists, this +takes priority. + +The below command lists the full paths to the configuration files that the +runtime attempts to load. The first path that exists will be used: + +```bash +$ kata-runtime --kata-show-default-config-paths +``` + +Aside from the built-in locations, it is possible to specify the path to a +custom configuration file using the `--kata-config` option: + +```bash +$ kata-runtime --kata-config=/some/where/configuration.toml ... +``` + +The runtime will log the full path to the configuration file it is using. See +the [logging](#logging) section for further details. + +To see details of your systems runtime environment (including the location of +the configuration file being used), run: + +```bash +$ kata-runtime kata-env +``` + +## Logging + +For detailed information and analysis on obtaining logs for other system +components, see the documentation for the +[`kata-log-parser`](https://github.com/kata-containers/tests/tree/master/cmd/log-parser) +tool. + +For runtime logs, see the following sections for the CRI-O and containerd shimv2 based runtimes. + +### Kata OCI + +The Kata OCI runtime (including when used with CRI-O), provides `--log=` and `--log-format=` options. +However, the runtime also always logs to the system log (`syslog` or `journald`). + +To view runtime log output: + +```bash +$ sudo journalctl -t kata-runtime +``` + +### Kata containerd shimv2 + +The Kata containerd shimv2 runtime logs through `containerd`, and its logs will be sent +to wherever the `containerd` logs are directed. However, the +shimv2 runtime also always logs to the system log (`syslog` or `journald`) under the +identifier name of `kata`. + +To view the `shimv2` runtime log output: + +```bash +$ sudo journalctl -t kata +``` + +## Debugging + +See the +[debugging section of the developer guide](https://github.com/kata-containers/documentation/blob/master/Developer-Guide.md#troubleshoot-kata-containers). + +## Limitations + +See the +[limitations file](https://github.com/kata-containers/documentation/blob/master/Limitations.md) +for further details. + +## Community + +See [the community repository](https://github.com/kata-containers/community). + +### Contact + +See [how to reach the community](https://github.com/kata-containers/community/blob/master/CONTRIBUTING.md#contact). + +## Further information + +See the +[project table of contents](https://github.com/kata-containers/kata-containers) +and the +[documentation repository](https://github.com/kata-containers/documentation). + +## Additional packages + +For details of the other packages contained in this repository, see the +[package documentation](pkg). diff --git a/src/runtime/VERSION b/src/runtime/VERSION new file mode 100644 index 000000000..46d0bdd02 --- /dev/null +++ b/src/runtime/VERSION @@ -0,0 +1 @@ +1.11.0-rc0 diff --git a/src/runtime/arch/amd64-options.mk b/src/runtime/arch/amd64-options.mk new file mode 100644 index 000000000..7bfc5ff97 --- /dev/null +++ b/src/runtime/arch/amd64-options.mk @@ -0,0 +1,27 @@ +# Copyright (c) 2018-2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Intel x86-64 settings + +MACHINETYPE := pc +KERNELPARAMS := +MACHINEACCELERATORS := + +QEMUCMD := qemu-system-x86_64 + +# Qemu experimental with virtiofs +QEMUVIRTIOFSCMD := qemu-virtiofs-system-x86_64 + +# Firecracker binary name +FCCMD := firecracker +# Firecracker's jailer binary name +FCJAILERCMD := jailer + +#ACRN binary name +ACRNCMD := acrn-dm +ACRNCTLCMD := acrnctl + +# cloud-hypervisor binary name +CLHCMD := cloud-hypervisor diff --git a/src/runtime/arch/arm64-options.mk b/src/runtime/arch/arm64-options.mk new file mode 100644 index 000000000..f8c62c371 --- /dev/null +++ b/src/runtime/arch/arm64-options.mk @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +# ARM 64 settings + +MACHINETYPE := virt +KERNELPARAMS := +MACHINEACCELERATORS := + +QEMUCMD := qemu-system-aarch64 + +# Firecracker binary name +FCCMD := firecracker +# Firecracker's jailer binary name +FCJAILERCMD := jailer diff --git a/src/runtime/arch/ppc64le-options.mk b/src/runtime/arch/ppc64le-options.mk new file mode 100644 index 000000000..a309ca51d --- /dev/null +++ b/src/runtime/arch/ppc64le-options.mk @@ -0,0 +1,12 @@ +# Copyright (c) 2018 IBM +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Power ppc64le settings + +MACHINETYPE := pseries +KERNELPARAMS := +MACHINEACCELERATORS := +KERNELTYPE := uncompressed #This architecture must use an uncompressed kernel. +QEMUCMD := qemu-system-ppc64le diff --git a/src/runtime/arch/s390x-options.mk b/src/runtime/arch/s390x-options.mk new file mode 100644 index 000000000..3256e4628 --- /dev/null +++ b/src/runtime/arch/s390x-options.mk @@ -0,0 +1,12 @@ +# Copyright (c) 2018 IBM +# +# SPDX-License-Identifier: Apache-2.0 +# + +# s390x settings + +MACHINETYPE := s390-ccw-virtio +KERNELPARAMS := +MACHINEACCELERATORS := + +QEMUCMD := qemu-system-s390x diff --git a/src/runtime/cli/config-generated.go.in b/src/runtime/cli/config-generated.go.in new file mode 100644 index 000000000..f0a9571b8 --- /dev/null +++ b/src/runtime/cli/config-generated.go.in @@ -0,0 +1,49 @@ +// +// Copyright (c) 2018-2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// +// WARNING: This file is auto-generated - DO NOT EDIT! +// +// Note that some variables are "var" to allow them to be modified +// by the tests. +package main + +import ( + "fmt" +) + +// name is the name of the runtime +const name = "@RUNTIME_NAME@" + +// name of the project +const project = "@PROJECT_NAME@" + +// prefix used to denote non-standard CLI commands and options. +const projectPrefix = "@PROJECT_TYPE@" + +// original URL for this project +const projectURL = "@PROJECT_URL@" + +const defaultRootDirectory = "@PKGRUNDIR@" + +// commit is the git commit the runtime is compiled from. +var commit = "@COMMIT@" + +// version is the runtime version. +var version = "@VERSION@" + +// project-specific command names +var envCmd = fmt.Sprintf("%s-env", projectPrefix) +var checkCmd = fmt.Sprintf("%s-check", projectPrefix) + +// project-specific option names +var configFilePathOption = fmt.Sprintf("%s-config", projectPrefix) +var showConfigPathsOption = fmt.Sprintf("%s-show-default-config-paths", projectPrefix) + +// Default config file used by stateless systems. +var defaultRuntimeConfiguration = "@CONFIG_PATH@" + +// Alternate config file that takes precedence over +// defaultRuntimeConfiguration. +var defaultSysConfRuntimeConfiguration = "@SYSCONFIG@" diff --git a/src/runtime/cli/config/configuration-acrn.toml.in b/src/runtime/cli/config/configuration-acrn.toml.in new file mode 100644 index 000000000..d1c581243 --- /dev/null +++ b/src/runtime/cli/config/configuration-acrn.toml.in @@ -0,0 +1,237 @@ +# Copyright (c) 2017-2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +# XXX: WARNING: this file is auto-generated. +# XXX: +# XXX: Source file: "@CONFIG_ACRN_IN@" +# XXX: Project: +# XXX: Name: @PROJECT_NAME@ +# XXX: Type: @PROJECT_TYPE@ + +[hypervisor.acrn] +path = "@ACRNPATH@" +ctlpath = "@ACRNCTLPATH@" +kernel = "@KERNELPATH_ACRN@" +image = "@IMAGEPATH@" + +# Optional space-separated list of options to pass to the guest kernel. +# For example, use `kernel_params = "vsyscall=emulate"` if you are having +# trouble running pre-2.15 glibc. +# +# WARNING: - any parameter specified here will take priority over the default +# parameter value of the same name used to start the virtual machine. +# Do not set values here unless you understand the impact of doing so as you +# may stop the virtual machine from booting. +# To see the list of default parameters, enable hypervisor debug, create a +# container and look for 'default-kernel-parameters' log entries. +kernel_params = "@KERNELPARAMS@" + +# Path to the firmware. +# If you want that acrn uses the default firmware leave this option empty +firmware = "@FIRMWAREPATH@" + +# Default maximum number of vCPUs per SB/VM: +# unspecified or == 0 --> will be set to the actual number of physical cores or to the maximum number +# of vCPUs supported by KVM if that number is exceeded +# > 0 <= number of physical cores --> will be set to the specified number +# > number of physical cores --> will be set to the actual number of physical cores or to the maximum number +# of vCPUs supported by KVM if that number is exceeded +# WARNING: Depending of the architecture, the maximum number of vCPUs supported by KVM is used when +# the actual number of physical cores is greater than it. +# WARNING: Be aware that this value impacts the virtual machine's memory footprint and CPU +# the hotplug functionality. For example, `default_maxvcpus = 240` specifies that until 240 vCPUs +# can be added to a SB/VM, but the memory footprint will be big. Another example, with +# `default_maxvcpus = 8` the memory footprint will be small, but 8 will be the maximum number of +# vCPUs supported by the SB/VM. In general, we recommend that you do not edit this variable, +# unless you know what are you doing. +default_maxvcpus = @DEFMAXVCPUS_ACRN@ + +# Bridges can be used to hot plug devices. +# Limitations: +# * Currently only pci bridges are supported +# * Until 30 devices per bridge can be hot plugged. +# * Until 5 PCI bridges can be cold plugged per VM. +# This limitation could be a bug in the kernel +# Default number of bridges per SB/VM: +# unspecified or 0 --> will be set to @DEFBRIDGES@ +# > 1 <= 5 --> will be set to the specified number +# > 5 --> will be set to 5 +default_bridges = @DEFBRIDGES@ + +# Default memory size in MiB for SB/VM. +# If unspecified then it will be set @DEFMEMSZ@ MiB. +default_memory = @DEFMEMSZ@ + +# Block storage driver to be used for the hypervisor in case the container +# rootfs is backed by a block device. ACRN only supports virtio-blk. +block_device_driver = "@DEFBLOCKSTORAGEDRIVER_ACRN@" + +# This option changes the default hypervisor and kernel parameters +# to enable debug output where available. This extra output is added +# to the proxy logs, but only when proxy debug is also enabled. +# +# Default false +#enable_debug = true + +# Disable the customizations done in the runtime when it detects +# that it is running on top a VMM. This will result in the runtime +# behaving as it would when running on bare metal. +# +#disable_nesting_checks = true + +# If host doesn't support vhost_net, set to true. Thus we won't create vhost fds for nics. +# Default false +#disable_vhost_net = true + +# Path to OCI hook binaries in the *guest rootfs*. +# This does not affect host-side hooks which must instead be added to +# the OCI spec passed to the runtime. +# +# You can create a rootfs with hooks by customizing the osbuilder scripts: +# https://github.com/kata-containers/osbuilder +# +# Hooks must be stored in a subdirectory of guest_hook_path according to their +# hook type, i.e. "guest_hook_path/{prestart,postart,poststop}". +# The agent will scan these directories for executable files and add them, in +# lexicographical order, to the lifecycle of the guest container. +# Hooks are executed in the runtime namespace of the guest. See the official documentation: +# https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#posix-platform-hooks +# Warnings will be logged if any error is encountered will scanning for hooks, +# but it will not abort container execution. +#guest_hook_path = "/usr/share/oci/hooks" + +[proxy.@PROJECT_TYPE@] +path = "@PROXYPATH@" + +# If enabled, proxy messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +[shim.@PROJECT_TYPE@] +path = "@SHIMPATH@" + +# If enabled, shim messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +# If enabled, the shim will create opentracing.io traces and spans. +# (See https://www.jaegertracing.io/docs/getting-started). +# +# Note: By default, the shim runs in a separate network namespace. Therefore, +# to allow it to send trace details to the Jaeger agent running on the host, +# it is necessary to set 'disable_new_netns=true' so that it runs in the host +# network namespace. +# +# (default: disabled) +#enable_tracing = true + +[agent.@PROJECT_TYPE@] +# If enabled, make the agent display debug-level messages. +# (default: disabled) +#enable_debug = true + +# Enable agent tracing. +# +# If enabled, the default trace mode is "dynamic" and the +# default trace type is "isolated". The trace mode and type are set +# explicity with the `trace_type=` and `trace_mode=` options. +# +# Notes: +# +# - Tracing is ONLY enabled when `enable_tracing` is set: explicitly +# setting `trace_mode=` and/or `trace_type=` without setting `enable_tracing` +# will NOT activate agent tracing. +# +# - See https://github.com/kata-containers/agent/blob/master/TRACING.md for +# full details. +# +# (default: disabled) +#enable_tracing = true +# +#trace_mode = "dynamic" +#trace_type = "isolated" + +[netmon] +# If enabled, the network monitoring process gets started when the +# sandbox is created. This allows for the detection of some additional +# network being added to the existing network namespace, after the +# sandbox has been created. +# (default: disabled) +#enable_netmon = true + +# Specify the path to the netmon binary. +path = "@NETMONPATH@" + +# If enabled, netmon messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +[runtime] +# If enabled, the runtime will log additional debug messages to the +# system log +# (default: disabled) +#enable_debug = true +# +# Internetworking model +# Determines how the VM should be connected to the +# the container network interface +# Options: +# +# - bridged (Deprecated) +# Uses a linux bridge to interconnect the container interface to +# the VM. Works for most cases except macvlan and ipvlan. +# ***NOTE: This feature has been deprecated with plans to remove this +# feature in the future. Please use other network models listed below. +# +# +# - macvtap +# Used when the Container network interface can be bridged using +# macvtap. +# +# - none +# Used when customize network. Only creates a tap device. No veth pair. +# +# - tcfilter +# Uses tc filter rules to redirect traffic from the network interface +# provided by plugin to a tap interface connected to the VM. +# +internetworking_model="@DEFNETWORKMODEL_ACRN@" + +# disable guest seccomp +# Determines whether container seccomp profiles are passed to the virtual +# machine and applied by the kata agent. If set to true, seccomp is not applied +# within the guest +# (default: true) +disable_guest_seccomp=@DEFDISABLEGUESTSECCOMP@ + +# If enabled, the runtime will create opentracing.io traces and spans. +# (See https://www.jaegertracing.io/docs/getting-started). +# (default: disabled) +#enable_tracing = true + +# If enabled, the runtime will not create a network namespace for shim and hypervisor processes. +# This option may have some potential impacts to your host. It should only be used when you know what you're doing. +# `disable_new_netns` conflicts with `enable_netmon` +# `disable_new_netns` conflicts with `internetworking_model=bridged` and `internetworking_model=macvtap`. It works only +# with `internetworking_model=none`. The tap device will be in the host network namespace and can connect to a bridge +# (like OVS) directly. +# If you are using docker, `disable_new_netns` only works with `docker run --net=none` +# (default: false) +#disable_new_netns = true + +# if enabled, the runtime will add all the kata processes inside one dedicated cgroup. +# The container cgroups in the host are not created, just one single cgroup per sandbox. +# The runtime caller is free to restrict or collect cgroup stats of the overall Kata sandbox. +# The sandbox cgroup path is the parent cgroup of a container with the PodSandbox annotation. +# The sandbox cgroup is constrained if there is no container type annotation. +# See: https://godoc.org/github.com/kata-containers/runtime/virtcontainers#ContainerType +sandbox_cgroup_only=@DEFSANDBOXCGROUPONLY@ + +# Enabled experimental feature list, format: ["a", "b"]. +# Experimental features are features not stable enough for production, +# they may break compatibility, and are prepared for a big version bump. +# Supported experimental features: +# (default: []) +experimental=@DEFAULTEXPFEATURES@ diff --git a/src/runtime/cli/config/configuration-clh.toml.in b/src/runtime/cli/config/configuration-clh.toml.in new file mode 100644 index 000000000..00185ea2e --- /dev/null +++ b/src/runtime/cli/config/configuration-clh.toml.in @@ -0,0 +1,213 @@ +# Copyright (c) 2019 Ericsson Eurolab Deutschland GmbH +# +# SPDX-License-Identifier: Apache-2.0 +# + +# XXX: WARNING: this file is auto-generated. +# XXX: +# XXX: Source file: "@CONFIG_CLH_IN@" +# XXX: Project: +# XXX: Name: @PROJECT_NAME@ +# XXX: Type: @PROJECT_TYPE@ + +[hypervisor.clh] +path = "@CLHPATH@" +kernel = "@KERNELPATH_CLH@" +image = "@IMAGEPATH@" + +# Optional space-separated list of options to pass to the guest kernel. +# For example, use `kernel_params = "vsyscall=emulate"` if you are having +# trouble running pre-2.15 glibc. +# +# WARNING: - any parameter specified here will take priority over the default +# parameter value of the same name used to start the virtual machine. +# Do not set values here unless you understand the impact of doing so as you +# may stop the virtual machine from booting. +# To see the list of default parameters, enable hypervisor debug, create a +# container and look for 'default-kernel-parameters' log entries. +kernel_params = "@KERNELPARAMS@" + +# Default number of vCPUs per SB/VM: +# unspecified or 0 --> will be set to @DEFVCPUS@ +# < 0 --> will be set to the actual number of physical cores +# > 0 <= number of physical cores --> will be set to the specified number +# > number of physical cores --> will be set to the actual number of physical cores +default_vcpus = 1 + +# Default maximum number of vCPUs per SB/VM: +# unspecified or == 0 --> will be set to the actual number of physical cores or to the maximum number +# of vCPUs supported by KVM if that number is exceeded +# > 0 <= number of physical cores --> will be set to the specified number +# > number of physical cores --> will be set to the actual number of physical cores or to the maximum number +# of vCPUs supported by KVM if that number is exceeded +# WARNING: Depending of the architecture, the maximum number of vCPUs supported by KVM is used when +# the actual number of physical cores is greater than it. +# WARNING: Be aware that this value impacts the virtual machine's memory footprint and CPU +# the hotplug functionality. For example, `default_maxvcpus = 240` specifies that until 240 vCPUs +# can be added to a SB/VM, but the memory footprint will be big. Another example, with +# `default_maxvcpus = 8` the memory footprint will be small, but 8 will be the maximum number of +# vCPUs supported by the SB/VM. In general, we recommend that you do not edit this variable, +# unless you know what are you doing. +default_maxvcpus = @DEFMAXVCPUS@ + +# Default memory size in MiB for SB/VM. +# If unspecified then it will be set @DEFMEMSZ@ MiB. +default_memory = @DEFMEMSZ@ + +# Default memory slots per SB/VM. +# If unspecified then it will be set @DEFMEMSLOTS@. +# This is will determine the times that memory will be hotadded to sandbox/VM. +#memory_slots = @DEFMEMSLOTS@ + +# Path to vhost-user-fs daemon. +virtio_fs_daemon = "@DEFVIRTIOFSDAEMON@" + +# Default size of DAX cache in MiB +virtio_fs_cache_size = @DEFVIRTIOFSCACHESIZE@ + +# cloud-hypervisor prefers virtiofs caching (dax) for performance reasons +virtio_fs_cache = "always" + +# This option changes the default hypervisor and kernel parameters +# to enable debug output where available. This extra output is added +# to the proxy logs, but only when proxy debug is also enabled. +# +# Default false +#enable_debug = true + +[proxy.@PROJECT_TYPE@] +path = "@PROXYPATH@" + +# If enabled, proxy messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +[shim.@PROJECT_TYPE@] +path = "@SHIMPATH@" + +# If enabled, shim messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +# If enabled, the shim will create opentracing.io traces and spans. +# (See https://www.jaegertracing.io/docs/getting-started). +# +# Note: By default, the shim runs in a separate network namespace. Therefore, +# to allow it to send trace details to the Jaeger agent running on the host, +# it is necessary to set 'disable_new_netns=true' so that it runs in the host +# network namespace. +# +# (default: disabled) +#enable_tracing = true + + +[agent.@PROJECT_TYPE@] +# If enabled, make the agent display debug-level messages. +# (default: disabled) +#enable_debug = true + +# Enable agent tracing. +# +# If enabled, the default trace mode is "dynamic" and the +# default trace type is "isolated". The trace mode and type are set +# explicity with the `trace_type=` and `trace_mode=` options. +# +# Notes: +# +# - Tracing is ONLY enabled when `enable_tracing` is set: explicitly +# setting `trace_mode=` and/or `trace_type=` without setting `enable_tracing` +# will NOT activate agent tracing. +# +# - See https://github.com/kata-containers/agent/blob/master/TRACING.md for +# full details. +# +# (default: disabled) +#enable_tracing = true +# +#trace_mode = "dynamic" +#trace_type = "isolated" + + +[netmon] +# If enabled, the network monitoring process gets started when the +# sandbox is created. This allows for the detection of some additional +# network being added to the existing network namespace, after the +# sandbox has been created. +# (default: disabled) +#enable_netmon = true + +# Specify the path to the netmon binary. +path = "@NETMONPATH@" + +# If enabled, netmon messages will be sent to the system log +# (default: disabled) +#enable_debug = true + + +[runtime] +# If enabled, the runtime will log additional debug messages to the +# system log +# (default: disabled) +#enable_debug = true +# +# Internetworking model +# Determines how the VM should be connected to the +# the container network interface +# Options: +# +# - bridged (Deprecated) +# Uses a linux bridge to interconnect the container interface to +# the VM. Works for most cases except macvlan and ipvlan. +# ***NOTE: This feature has been deprecated with plans to remove this +# feature in the future. Please use other network models listed below. +# +# +# - macvtap +# Used when the Container network interface can be bridged using +# macvtap. +# +# - none +# Used when customize network. Only creates a tap device. No veth pair. +# +# - tcfilter +# Uses tc filter rules to redirect traffic from the network interface +# provided by plugin to a tap interface connected to the VM. +# +internetworking_model="@DEFNETWORKMODEL_CLH@" + +# disable guest seccomp +# Determines whether container seccomp profiles are passed to the virtual +# machine and applied by the kata agent. If set to true, seccomp is not applied +# within the guest +# (default: true) +disable_guest_seccomp=@DEFDISABLEGUESTSECCOMP@ + +# If enabled, the runtime will create opentracing.io traces and spans. +# (See https://www.jaegertracing.io/docs/getting-started). +# (default: disabled) +#enable_tracing = true + +# If enabled, the runtime will not create a network namespace for shim and hypervisor processes. +# This option may have some potential impacts to your host. It should only be used when you know what you're doing. +# `disable_new_netns` conflicts with `enable_netmon` +# `disable_new_netns` conflicts with `internetworking_model=bridged` and `internetworking_model=macvtap`. It works only +# with `internetworking_model=none`. The tap device will be in the host network namespace and can connect to a bridge +# (like OVS) directly. +# If you are using docker, `disable_new_netns` only works with `docker run --net=none` +# (default: false) +#disable_new_netns = true + +# if enabled, the runtime will add all the kata processes inside one dedicated cgroup. +# The container cgroups in the host are not created, just one single cgroup per sandbox. +# The runtime caller is free to restrict or collect cgroup stats of the overall Kata sandbox. +# The sandbox cgroup path is the parent cgroup of a container with the PodSandbox annotation. +# The sandbox cgroup is constrained if there is no container type annotation. +# See: https://godoc.org/github.com/kata-containers/runtime/virtcontainers#ContainerType +sandbox_cgroup_only=@DEFSANDBOXCGROUPONLY@ + +# Enabled experimental feature list, format: ["a", "b"]. +# Experimental features are features not stable enough for production, +# they may break compatibility, and are prepared for a big version bump. +# Supported experimental features: +# (default: []) +experimental=@DEFAULTEXPFEATURES@ diff --git a/src/runtime/cli/config/configuration-fc.toml.in b/src/runtime/cli/config/configuration-fc.toml.in new file mode 100644 index 000000000..c9850d974 --- /dev/null +++ b/src/runtime/cli/config/configuration-fc.toml.in @@ -0,0 +1,339 @@ +# Copyright (c) 2017-2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +# XXX: WARNING: this file is auto-generated. +# XXX: +# XXX: Source file: "@CONFIG_FC_IN@" +# XXX: Project: +# XXX: Name: @PROJECT_NAME@ +# XXX: Type: @PROJECT_TYPE@ + +[hypervisor.firecracker] +path = "@FCPATH@" +# Path for the jailer specific to firecracker +# If the jailer path is not set kata will launch firecracker +# without a jail. If the jailer is set firecracker will be +# launched in a jailed enviornment created by the jailer +# This is disabled by default as additional setup is required +# for this feature today. +#jailer_path = "@FCJAILERPATH@" +kernel = "@KERNELPATH_FC@" +image = "@IMAGEPATH@" + +# Optional space-separated list of options to pass to the guest kernel. +# For example, use `kernel_params = "vsyscall=emulate"` if you are having +# trouble running pre-2.15 glibc. +# +# WARNING: - any parameter specified here will take priority over the default +# parameter value of the same name used to start the virtual machine. +# Do not set values here unless you understand the impact of doing so as you +# may stop the virtual machine from booting. +# To see the list of default parameters, enable hypervisor debug, create a +# container and look for 'default-kernel-parameters' log entries. +kernel_params = "@KERNELPARAMS@" + +# Default number of vCPUs per SB/VM: +# unspecified or 0 --> will be set to @DEFVCPUS@ +# < 0 --> will be set to the actual number of physical cores +# > 0 <= number of physical cores --> will be set to the specified number +# > number of physical cores --> will be set to the actual number of physical cores +default_vcpus = 1 + +# Default maximum number of vCPUs per SB/VM: +# unspecified or == 0 --> will be set to the actual number of physical cores or to the maximum number +# of vCPUs supported by KVM if that number is exceeded +# > 0 <= number of physical cores --> will be set to the specified number +# > number of physical cores --> will be set to the actual number of physical cores or to the maximum number +# of vCPUs supported by KVM if that number is exceeded +# WARNING: Depending of the architecture, the maximum number of vCPUs supported by KVM is used when +# the actual number of physical cores is greater than it. +# WARNING: Be aware that this value impacts the virtual machine's memory footprint and CPU +# the hotplug functionality. For example, `default_maxvcpus = 240` specifies that until 240 vCPUs +# can be added to a SB/VM, but the memory footprint will be big. Another example, with +# `default_maxvcpus = 8` the memory footprint will be small, but 8 will be the maximum number of +# vCPUs supported by the SB/VM. In general, we recommend that you do not edit this variable, +# unless you know what are you doing. +default_maxvcpus = @DEFMAXVCPUS@ + +# Bridges can be used to hot plug devices. +# Limitations: +# * Currently only pci bridges are supported +# * Until 30 devices per bridge can be hot plugged. +# * Until 5 PCI bridges can be cold plugged per VM. +# This limitation could be a bug in the kernel +# Default number of bridges per SB/VM: +# unspecified or 0 --> will be set to @DEFBRIDGES@ +# > 1 <= 5 --> will be set to the specified number +# > 5 --> will be set to 5 +default_bridges = @DEFBRIDGES@ + +# Default memory size in MiB for SB/VM. +# If unspecified then it will be set @DEFMEMSZ@ MiB. +default_memory = @DEFMEMSZ@ +# +# Default memory slots per SB/VM. +# If unspecified then it will be set @DEFMEMSLOTS@. +# This is will determine the times that memory will be hotadded to sandbox/VM. +#memory_slots = @DEFMEMSLOTS@ + +# The size in MiB will be plused to max memory of hypervisor. +# It is the memory address space for the NVDIMM devie. +# If set block storage driver (block_device_driver) to "nvdimm", +# should set memory_offset to the size of block device. +# Default 0 +#memory_offset = 0 + +# Disable block device from being used for a container's rootfs. +# In case of a storage driver like devicemapper where a container's +# root file system is backed by a block device, the block device is passed +# directly to the hypervisor for performance reasons. +# This flag prevents the block device from being passed to the hypervisor, +# 9pfs is used instead to pass the rootfs. +disable_block_device_use = @DEFDISABLEBLOCK@ + +# Block storage driver to be used for the hypervisor in case the container +# rootfs is backed by a block device. This is virtio-scsi, virtio-blk +# or nvdimm. +block_device_driver = "@DEFBLOCKSTORAGEDRIVER_FC@" + +# Specifies cache-related options will be set to block devices or not. +# Default false +#block_device_cache_set = true + +# Specifies cache-related options for block devices. +# Denotes whether use of O_DIRECT (bypass the host page cache) is enabled. +# Default false +#block_device_cache_direct = true + +# Specifies cache-related options for block devices. +# Denotes whether flush requests for the device are ignored. +# Default false +#block_device_cache_noflush = true + +# Enable pre allocation of VM RAM, default false +# Enabling this will result in lower container density +# as all of the memory will be allocated and locked +# This is useful when you want to reserve all the memory +# upfront or in the cases where you want memory latencies +# to be very predictable +# Default false +#enable_mem_prealloc = true + +# Enable huge pages for VM RAM, default false +# Enabling this will result in the VM memory +# being allocated using huge pages. +# This is useful when you want to use vhost-user network +# stacks within the container. This will automatically +# result in memory pre allocation +#enable_hugepages = true + +# Enable swap of vm memory. Default false. +# The behaviour is undefined if mem_prealloc is also set to true +#enable_swap = true + +# This option changes the default hypervisor and kernel parameters +# to enable debug output where available. This extra output is added +# to the proxy logs, but only when proxy debug is also enabled. +# +# Default false +#enable_debug = true + +# Disable the customizations done in the runtime when it detects +# that it is running on top a VMM. This will result in the runtime +# behaving as it would when running on bare metal. +# +#disable_nesting_checks = true + +# This is the msize used for 9p shares. It is the number of bytes +# used for 9p packet payload. +#msize_9p = @DEFMSIZE9P@ + +# If true and vsocks are supported, use vsocks to communicate directly +# with the agent (no proxy is started). +# Default true +use_vsock = true + +# VFIO devices are hotplugged on a bridge by default. +# Enable hotplugging on root bus. This may be required for devices with +# a large PCI bar, as this is a current limitation with hotplugging on +# a bridge. This value is valid for "pc" machine type. +# Default false +#hotplug_vfio_on_root_bus = true + +# +# Default entropy source. +# The path to a host source of entropy (including a real hardware RNG) +# /dev/urandom and /dev/random are two main options. +# Be aware that /dev/random is a blocking source of entropy. If the host +# runs out of entropy, the VMs boot time will increase leading to get startup +# timeouts. +# The source of entropy /dev/urandom is non-blocking and provides a +# generally acceptable source of entropy. It should work well for pretty much +# all practical purposes. +#entropy_source= "@DEFENTROPYSOURCE@" + +# Path to OCI hook binaries in the *guest rootfs*. +# This does not affect host-side hooks which must instead be added to +# the OCI spec passed to the runtime. +# +# You can create a rootfs with hooks by customizing the osbuilder scripts: +# https://github.com/kata-containers/osbuilder +# +# Hooks must be stored in a subdirectory of guest_hook_path according to their +# hook type, i.e. "guest_hook_path/{prestart,postart,poststop}". +# The agent will scan these directories for executable files and add them, in +# lexicographical order, to the lifecycle of the guest container. +# Hooks are executed in the runtime namespace of the guest. See the official documentation: +# https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#posix-platform-hooks +# Warnings will be logged if any error is encountered will scanning for hooks, +# but it will not abort container execution. +#guest_hook_path = "/usr/share/oci/hooks" + +[factory] +# VM templating support. Once enabled, new VMs are created from template +# using vm cloning. They will share the same initial kernel, initramfs and +# agent memory by mapping it readonly. It helps speeding up new container +# creation and saves a lot of memory if there are many kata containers running +# on the same host. +# +# When disabled, new VMs are created from scratch. +# +# Note: Requires "initrd=" to be set ("image=" is not supported). +# +# Default false +#enable_template = true + +[shim.@PROJECT_TYPE@] +path = "@SHIMPATH@" + +# If enabled, shim messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +# If enabled, the shim will create opentracing.io traces and spans. +# (See https://www.jaegertracing.io/docs/getting-started). +# +# Note: By default, the shim runs in a separate network namespace. Therefore, +# to allow it to send trace details to the Jaeger agent running on the host, +# it is necessary to set 'disable_new_netns=true' so that it runs in the host +# network namespace. +# +# (default: disabled) +#enable_tracing = true + +[agent.@PROJECT_TYPE@] +# If enabled, make the agent display debug-level messages. +# (default: disabled) +#enable_debug = true + +# Enable agent tracing. +# +# If enabled, the default trace mode is "dynamic" and the +# default trace type is "isolated". The trace mode and type are set +# explicity with the `trace_type=` and `trace_mode=` options. +# +# Notes: +# +# - Tracing is ONLY enabled when `enable_tracing` is set: explicitly +# setting `trace_mode=` and/or `trace_type=` without setting `enable_tracing` +# will NOT activate agent tracing. +# +# - See https://github.com/kata-containers/agent/blob/master/TRACING.md for +# full details. +# +# (default: disabled) +#enable_tracing = true +# +#trace_mode = "dynamic" +#trace_type = "isolated" + +# Comma separated list of kernel modules and their parameters. +# These modules will be loaded in the guest kernel using modprobe(8). +# The following example can be used to load two kernel modules with parameters +# - kernel_modules=["e1000e InterruptThrottleRate=3000,3000,3000 EEE=1", "i915 enable_ppgtt=0"] +# The first word is considered as the module name and the rest as its parameters. +# Container will not be started when: +# * A kernel module is specified and the modprobe command is not installed in the guest +# or it fails loading the module. +# * The module is not available in the guest or it doesn't met the guest kernel +# requirements, like architecture and version. +# +kernel_modules=[] + +[netmon] +# If enabled, the network monitoring process gets started when the +# sandbox is created. This allows for the detection of some additional +# network being added to the existing network namespace, after the +# sandbox has been created. +# (default: disabled) +#enable_netmon = true + +# Specify the path to the netmon binary. +path = "@NETMONPATH@" + +# If enabled, netmon messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +[runtime] +# If enabled, the runtime will log additional debug messages to the +# system log +# (default: disabled) +#enable_debug = true +# +# Internetworking model +# Determines how the VM should be connected to the +# the container network interface +# Options: +# +# - macvtap +# Used when the Container network interface can be bridged using +# macvtap. +# +# - none +# Used when customize network. Only creates a tap device. No veth pair. +# +# - tcfilter +# Uses tc filter rules to redirect traffic from the network interface +# provided by plugin to a tap interface connected to the VM. +# +internetworking_model="@DEFNETWORKMODEL_FC@" + +# disable guest seccomp +# Determines whether container seccomp profiles are passed to the virtual +# machine and applied by the kata agent. If set to true, seccomp is not applied +# within the guest +# (default: true) +disable_guest_seccomp=@DEFDISABLEGUESTSECCOMP@ + +# If enabled, the runtime will create opentracing.io traces and spans. +# (See https://www.jaegertracing.io/docs/getting-started). +# (default: disabled) +#enable_tracing = true + +# If enabled, the runtime will not create a network namespace for shim and hypervisor processes. +# This option may have some potential impacts to your host. It should only be used when you know what you're doing. +# `disable_new_netns` conflicts with `enable_netmon` +# `disable_new_netns` conflicts with `internetworking_model=tcfilter` and `internetworking_model=macvtap`. It works only +# with `internetworking_model=none`. The tap device will be in the host network namespace and can connect to a bridge +# (like OVS) directly. +# If you are using docker, `disable_new_netns` only works with `docker run --net=none` +# (default: false) +#disable_new_netns = true + +# if enable, the runtime will add all the kata processes inside one dedicated cgroup. +# The container cgroups in the host are not created, just one single cgroup per sandbox. +# The runtime caller is free to restrict or collect cgroup stats of the overall Kata sandbox. +# The sandbox cgroup path is the parent cgroup of a container with the PodSandbox annotation. +# The sandbox cgroup is constrained if there is no container type annotation. +# See: https://godoc.org/github.com/kata-containers/runtime/virtcontainers#ContainerType +sandbox_cgroup_only=@DEFSANDBOXCGROUPONLY@ + +# Enabled experimental feature list, format: ["a", "b"]. +# Experimental features are features not stable enough for production, +# they may break compatibility, and are prepared for a big version bump. +# Supported experimental features: +# (default: []) +experimental=@DEFAULTEXPFEATURES@ diff --git a/src/runtime/cli/config/configuration-qemu-virtiofs.toml.in b/src/runtime/cli/config/configuration-qemu-virtiofs.toml.in new file mode 100644 index 000000000..46a5ff0ca --- /dev/null +++ b/src/runtime/cli/config/configuration-qemu-virtiofs.toml.in @@ -0,0 +1,452 @@ +# Copyright (c) 2017-2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +# XXX: WARNING: this file is auto-generated. +# XXX: +# XXX: Source file: "@CONFIG_QEMU_VIRTIOFS_IN@" +# XXX: Project: +# XXX: Name: @PROJECT_NAME@ +# XXX: Type: @PROJECT_TYPE@ + +[hypervisor.qemu] +path = "@QEMUVIRTIOFSPATH@" +kernel = "@KERNELVIRTIOFSPATH@" +image = "@IMAGEPATH@" +machine_type = "@MACHINETYPE@" + +# Optional space-separated list of options to pass to the guest kernel. +# For example, use `kernel_params = "vsyscall=emulate"` if you are having +# trouble running pre-2.15 glibc. +# +# WARNING: - any parameter specified here will take priority over the default +# parameter value of the same name used to start the virtual machine. +# Do not set values here unless you understand the impact of doing so as you +# may stop the virtual machine from booting. +# To see the list of default parameters, enable hypervisor debug, create a +# container and look for 'default-kernel-parameters' log entries. +kernel_params = "@KERNELPARAMS@" + +# Path to the firmware. +# If you want that qemu uses the default firmware leave this option empty +firmware = "@FIRMWAREPATH@" + +# Machine accelerators +# comma-separated list of machine accelerators to pass to the hypervisor. +# For example, `machine_accelerators = "nosmm,nosmbus,nosata,nopit,static-prt,nofw"` +machine_accelerators="@MACHINEACCELERATORS@" + +# Default number of vCPUs per SB/VM: +# unspecified or 0 --> will be set to @DEFVCPUS@ +# < 0 --> will be set to the actual number of physical cores +# > 0 <= number of physical cores --> will be set to the specified number +# > number of physical cores --> will be set to the actual number of physical cores +default_vcpus = 1 + +# Default maximum number of vCPUs per SB/VM: +# unspecified or == 0 --> will be set to the actual number of physical cores or to the maximum number +# of vCPUs supported by KVM if that number is exceeded +# > 0 <= number of physical cores --> will be set to the specified number +# > number of physical cores --> will be set to the actual number of physical cores or to the maximum number +# of vCPUs supported by KVM if that number is exceeded +# WARNING: Depending of the architecture, the maximum number of vCPUs supported by KVM is used when +# the actual number of physical cores is greater than it. +# WARNING: Be aware that this value impacts the virtual machine's memory footprint and CPU +# the hotplug functionality. For example, `default_maxvcpus = 240` specifies that until 240 vCPUs +# can be added to a SB/VM, but the memory footprint will be big. Another example, with +# `default_maxvcpus = 8` the memory footprint will be small, but 8 will be the maximum number of +# vCPUs supported by the SB/VM. In general, we recommend that you do not edit this variable, +# unless you know what are you doing. +default_maxvcpus = @DEFMAXVCPUS@ + +# Bridges can be used to hot plug devices. +# Limitations: +# * Currently only pci bridges are supported +# * Until 30 devices per bridge can be hot plugged. +# * Until 5 PCI bridges can be cold plugged per VM. +# This limitation could be a bug in qemu or in the kernel +# Default number of bridges per SB/VM: +# unspecified or 0 --> will be set to @DEFBRIDGES@ +# > 1 <= 5 --> will be set to the specified number +# > 5 --> will be set to 5 +default_bridges = @DEFBRIDGES@ + +# Default memory size in MiB for SB/VM. +# If unspecified then it will be set @DEFMEMSZ@ MiB. +default_memory = @DEFMEMSZ@ +# +# Default memory slots per SB/VM. +# If unspecified then it will be set @DEFMEMSLOTS@. +# This is will determine the times that memory will be hotadded to sandbox/VM. +#memory_slots = @DEFMEMSLOTS@ + +# The size in MiB will be plused to max memory of hypervisor. +# It is the memory address space for the NVDIMM devie. +# If set block storage driver (block_device_driver) to "nvdimm", +# should set memory_offset to the size of block device. +# Default 0 +#memory_offset = 0 + +# Disable block device from being used for a container's rootfs. +# In case of a storage driver like devicemapper where a container's +# root file system is backed by a block device, the block device is passed +# directly to the hypervisor for performance reasons. +# This flag prevents the block device from being passed to the hypervisor, +# 9pfs is used instead to pass the rootfs. +disable_block_device_use = @DEFDISABLEBLOCK@ + +# Shared file system type: +# - virtio-fs (default) +# - virtio-9p +shared_fs = "@DEFSHAREDFS_QEMU_VIRTIOFS@" + +# Path to vhost-user-fs daemon. +virtio_fs_daemon = "@DEFVIRTIOFSDAEMON@" + +# Default size of DAX cache in MiB +virtio_fs_cache_size = @DEFVIRTIOFSCACHESIZE@ + +# Extra args for virtiofsd daemon +# +# Format example: +# ["-o", "arg1=xxx,arg2", "-o", "hello world", "--arg3=yyy"] +# +# see `virtiofsd -h` for possible options. +virtio_fs_extra_args = @DEFVIRTIOFSEXTRAARGS@ + +# Cache mode: +# +# - none +# Metadata, data, and pathname lookup are not cached in guest. They are +# always fetched from host and any changes are immediately pushed to host. +# +# - auto +# Metadata and pathname lookup cache expires after a configured amount of +# time (default is 1 second). Data is cached while the file is open (close +# to open consistency). +# +# - always +# Metadata, data, and pathname lookup are cached in guest and never expire. +virtio_fs_cache = "@DEFVIRTIOFSCACHE@" + +# Block storage driver to be used for the hypervisor in case the container +# rootfs is backed by a block device. This is virtio-scsi, virtio-blk +# or nvdimm. +block_device_driver = "@DEFBLOCKSTORAGEDRIVER_QEMU@" + +# Specifies cache-related options will be set to block devices or not. +# Default false +#block_device_cache_set = true + +# Specifies cache-related options for block devices. +# Denotes whether use of O_DIRECT (bypass the host page cache) is enabled. +# Default false +#block_device_cache_direct = true + +# Specifies cache-related options for block devices. +# Denotes whether flush requests for the device are ignored. +# Default false +#block_device_cache_noflush = true + +# Enable iothreads (data-plane) to be used. This causes IO to be +# handled in a separate IO thread. This is currently only implemented +# for SCSI. +# +enable_iothreads = @DEFENABLEIOTHREADS@ + +# Enable pre allocation of VM RAM, default false +# Enabling this will result in lower container density +# as all of the memory will be allocated and locked +# This is useful when you want to reserve all the memory +# upfront or in the cases where you want memory latencies +# to be very predictable +# Default false +#enable_mem_prealloc = true + +# Enable huge pages for VM RAM, default false +# Enabling this will result in the VM memory +# being allocated using huge pages. +# This is useful when you want to use vhost-user network +# stacks within the container. This will automatically +# result in memory pre allocation +#enable_hugepages = true + +# Enable vhost-user storage device, default false +# Enabling this will result in some Linux reserved block type +# major range 240-254 being chosen to represent vhost-user devices. +enable_vhost_user_store = @DEFENABLEVHOSTUSERSTORE@ + +# The base directory specifically used for vhost-user devices. +# Its sub-path "block" is used for block devices; "block/sockets" is +# where we expect vhost-user sockets to live; "block/devices" is where +# simulated block device nodes for vhost-user devices to live. +vhost_user_store_path = "@DEFVHOSTUSERSTOREPATH@" + +# Enable file based guest memory support. The default is an empty string which +# will disable this feature. In the case of virtio-fs, this is enabled +# automatically and '/dev/shm' is used as the backing folder. +# This option will be ignored if VM templating is enabled. +#file_mem_backend = "" + +# Enable swap of vm memory. Default false. +# The behaviour is undefined if mem_prealloc is also set to true +#enable_swap = true + +# This option changes the default hypervisor and kernel parameters +# to enable debug output where available. This extra output is added +# to the proxy logs, but only when proxy debug is also enabled. +# +# Default false +#enable_debug = true + +# Disable the customizations done in the runtime when it detects +# that it is running on top a VMM. This will result in the runtime +# behaving as it would when running on bare metal. +# +#disable_nesting_checks = true + +# This is the msize used for 9p shares. It is the number of bytes +# used for 9p packet payload. +#msize_9p = @DEFMSIZE9P@ + +# If true and vsocks are supported, use vsocks to communicate directly +# with the agent and no proxy is started, otherwise use unix +# sockets and start a proxy to communicate with the agent. +# Default false +#use_vsock = true + +# If false and nvdimm is supported, use nvdimm device to plug guest image. +# Otherwise virtio-block device is used. +# Default false +#disable_image_nvdimm = true + +# VFIO devices are hotplugged on a bridge by default. +# Enable hotplugging on root bus. This may be required for devices with +# a large PCI bar, as this is a current limitation with hotplugging on +# a bridge. This value is valid for "pc" machine type. +# Default false +#hotplug_vfio_on_root_bus = true + +# If vhost-net backend for virtio-net is not desired, set to true. Default is false, which trades off +# security (vhost-net runs ring0) for network I/O performance. +#disable_vhost_net = true + +# +# Default entropy source. +# The path to a host source of entropy (including a real hardware RNG) +# /dev/urandom and /dev/random are two main options. +# Be aware that /dev/random is a blocking source of entropy. If the host +# runs out of entropy, the VMs boot time will increase leading to get startup +# timeouts. +# The source of entropy /dev/urandom is non-blocking and provides a +# generally acceptable source of entropy. It should work well for pretty much +# all practical purposes. +#entropy_source= "@DEFENTROPYSOURCE@" + +# Path to OCI hook binaries in the *guest rootfs*. +# This does not affect host-side hooks which must instead be added to +# the OCI spec passed to the runtime. +# +# You can create a rootfs with hooks by customizing the osbuilder scripts: +# https://github.com/kata-containers/osbuilder +# +# Hooks must be stored in a subdirectory of guest_hook_path according to their +# hook type, i.e. "guest_hook_path/{prestart,postart,poststop}". +# The agent will scan these directories for executable files and add them, in +# lexicographical order, to the lifecycle of the guest container. +# Hooks are executed in the runtime namespace of the guest. See the official documentation: +# https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#posix-platform-hooks +# Warnings will be logged if any error is encountered will scanning for hooks, +# but it will not abort container execution. +#guest_hook_path = "/usr/share/oci/hooks" + +[factory] +# VM templating support. Once enabled, new VMs are created from template +# using vm cloning. They will share the same initial kernel, initramfs and +# agent memory by mapping it readonly. It helps speeding up new container +# creation and saves a lot of memory if there are many kata containers running +# on the same host. +# +# When disabled, new VMs are created from scratch. +# +# Note: Requires "initrd=" to be set ("image=" is not supported). +# +# Default false +#enable_template = true + +# Specifies the path of template. +# +# Default "/run/vc/vm/template" +#template_path = "/run/vc/vm/template" + +# The number of caches of VMCache: +# unspecified or == 0 --> VMCache is disabled +# > 0 --> will be set to the specified number +# +# VMCache is a function that creates VMs as caches before using it. +# It helps speed up new container creation. +# The function consists of a server and some clients communicating +# through Unix socket. The protocol is gRPC in protocols/cache/cache.proto. +# The VMCache server will create some VMs and cache them by factory cache. +# It will convert the VM to gRPC format and transport it when gets +# requestion from clients. +# Factory grpccache is the VMCache client. It will request gRPC format +# VM and convert it back to a VM. If VMCache function is enabled, +# kata-runtime will request VM from factory grpccache when it creates +# a new sandbox. +# +# Default 0 +#vm_cache_number = 0 + +# Specify the address of the Unix socket that is used by VMCache. +# +# Default /var/run/kata-containers/cache.sock +#vm_cache_endpoint = "/var/run/kata-containers/cache.sock" + +[proxy.@PROJECT_TYPE@] +path = "@PROXYPATH@" + +# If enabled, proxy messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +[shim.@PROJECT_TYPE@] +path = "@SHIMPATH@" + +# If enabled, shim messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +# If enabled, the shim will create opentracing.io traces and spans. +# (See https://www.jaegertracing.io/docs/getting-started). +# +# Note: By default, the shim runs in a separate network namespace. Therefore, +# to allow it to send trace details to the Jaeger agent running on the host, +# it is necessary to set 'disable_new_netns=true' so that it runs in the host +# network namespace. +# +# (default: disabled) +#enable_tracing = true + +[agent.@PROJECT_TYPE@] +# If enabled, make the agent display debug-level messages. +# (default: disabled) +#enable_debug = true + +# Enable agent tracing. +# +# If enabled, the default trace mode is "dynamic" and the +# default trace type is "isolated". The trace mode and type are set +# explicity with the `trace_type=` and `trace_mode=` options. +# +# Notes: +# +# - Tracing is ONLY enabled when `enable_tracing` is set: explicitly +# setting `trace_mode=` and/or `trace_type=` without setting `enable_tracing` +# will NOT activate agent tracing. +# +# - See https://github.com/kata-containers/agent/blob/master/TRACING.md for +# full details. +# +# (default: disabled) +#enable_tracing = true +# +#trace_mode = "dynamic" +#trace_type = "isolated" + +# Comma separated list of kernel modules and their parameters. +# These modules will be loaded in the guest kernel using modprobe(8). +# The following example can be used to load two kernel modules with parameters +# - kernel_modules=["e1000e InterruptThrottleRate=3000,3000,3000 EEE=1", "i915 enable_ppgtt=0"] +# The first word is considered as the module name and the rest as its parameters. +# Container will not be started when: +# * A kernel module is specified and the modprobe command is not installed in the guest +# or it fails loading the module. +# * The module is not available in the guest or it doesn't met the guest kernel +# requirements, like architecture and version. +# +kernel_modules=[] + + +[netmon] +# If enabled, the network monitoring process gets started when the +# sandbox is created. This allows for the detection of some additional +# network being added to the existing network namespace, after the +# sandbox has been created. +# (default: disabled) +#enable_netmon = true + +# Specify the path to the netmon binary. +path = "@NETMONPATH@" + +# If enabled, netmon messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +[runtime] +# If enabled, the runtime will log additional debug messages to the +# system log +# (default: disabled) +#enable_debug = true +# +# Internetworking model +# Determines how the VM should be connected to the +# the container network interface +# Options: +# +# - bridged (Deprecated) +# Uses a linux bridge to interconnect the container interface to +# the VM. Works for most cases except macvlan and ipvlan. +# ***NOTE: This feature has been deprecated with plans to remove this +# feature in the future. Please use other network models listed below. +# +# - macvtap +# Used when the Container network interface can be bridged using +# macvtap. +# +# - none +# Used when customize network. Only creates a tap device. No veth pair. +# +# - tcfilter +# Uses tc filter rules to redirect traffic from the network interface +# provided by plugin to a tap interface connected to the VM. +# +internetworking_model="@DEFNETWORKMODEL_QEMU@" + +# disable guest seccomp +# Determines whether container seccomp profiles are passed to the virtual +# machine and applied by the kata agent. If set to true, seccomp is not applied +# within the guest +# (default: true) +disable_guest_seccomp=@DEFDISABLEGUESTSECCOMP@ + +# If enabled, the runtime will create opentracing.io traces and spans. +# (See https://www.jaegertracing.io/docs/getting-started). +# (default: disabled) +#enable_tracing = true + +# If enabled, the runtime will not create a network namespace for shim and hypervisor processes. +# This option may have some potential impacts to your host. It should only be used when you know what you're doing. +# `disable_new_netns` conflicts with `enable_netmon` +# `disable_new_netns` conflicts with `internetworking_model=bridged` and `internetworking_model=macvtap`. It works only +# with `internetworking_model=none`. The tap device will be in the host network namespace and can connect to a bridge +# (like OVS) directly. +# If you are using docker, `disable_new_netns` only works with `docker run --net=none` +# (default: false) +#disable_new_netns = true + +# if enabled, the runtime will add all the kata processes inside one dedicated cgroup. +# The container cgroups in the host are not created, just one single cgroup per sandbox. +# The runtime caller is free to restrict or collect cgroup stats of the overall Kata sandbox. +# The sandbox cgroup path is the parent cgroup of a container with the PodSandbox annotation. +# The sandbox cgroup is constrained if there is no container type annotation. +# See: https://godoc.org/github.com/kata-containers/runtime/virtcontainers#ContainerType +sandbox_cgroup_only=@DEFSANDBOXCGROUPONLY@ + +# Enabled experimental feature list, format: ["a", "b"]. +# Experimental features are features not stable enough for production, +# they may break compatibility, and are prepared for a big version bump. +# Supported experimental features: +# (default: []) +experimental=@DEFAULTEXPFEATURES@ diff --git a/src/runtime/cli/config/configuration-qemu.toml.in b/src/runtime/cli/config/configuration-qemu.toml.in new file mode 100644 index 000000000..46ce7d9b5 --- /dev/null +++ b/src/runtime/cli/config/configuration-qemu.toml.in @@ -0,0 +1,460 @@ +# Copyright (c) 2017-2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +# XXX: WARNING: this file is auto-generated. +# XXX: +# XXX: Source file: "@CONFIG_QEMU_IN@" +# XXX: Project: +# XXX: Name: @PROJECT_NAME@ +# XXX: Type: @PROJECT_TYPE@ + +[hypervisor.qemu] +path = "@QEMUPATH@" +kernel = "@KERNELPATH@" +initrd = "@INITRDPATH@" +image = "@IMAGEPATH@" +machine_type = "@MACHINETYPE@" + +# Optional space-separated list of options to pass to the guest kernel. +# For example, use `kernel_params = "vsyscall=emulate"` if you are having +# trouble running pre-2.15 glibc. +# +# WARNING: - any parameter specified here will take priority over the default +# parameter value of the same name used to start the virtual machine. +# Do not set values here unless you understand the impact of doing so as you +# may stop the virtual machine from booting. +# To see the list of default parameters, enable hypervisor debug, create a +# container and look for 'default-kernel-parameters' log entries. +kernel_params = "@KERNELPARAMS@" + +# Path to the firmware. +# If you want that qemu uses the default firmware leave this option empty +firmware = "@FIRMWAREPATH@" + +# Machine accelerators +# comma-separated list of machine accelerators to pass to the hypervisor. +# For example, `machine_accelerators = "nosmm,nosmbus,nosata,nopit,static-prt,nofw"` +machine_accelerators="@MACHINEACCELERATORS@" + +# Default number of vCPUs per SB/VM: +# unspecified or 0 --> will be set to @DEFVCPUS@ +# < 0 --> will be set to the actual number of physical cores +# > 0 <= number of physical cores --> will be set to the specified number +# > number of physical cores --> will be set to the actual number of physical cores +default_vcpus = 1 + +# Default maximum number of vCPUs per SB/VM: +# unspecified or == 0 --> will be set to the actual number of physical cores or to the maximum number +# of vCPUs supported by KVM if that number is exceeded +# > 0 <= number of physical cores --> will be set to the specified number +# > number of physical cores --> will be set to the actual number of physical cores or to the maximum number +# of vCPUs supported by KVM if that number is exceeded +# WARNING: Depending of the architecture, the maximum number of vCPUs supported by KVM is used when +# the actual number of physical cores is greater than it. +# WARNING: Be aware that this value impacts the virtual machine's memory footprint and CPU +# the hotplug functionality. For example, `default_maxvcpus = 240` specifies that until 240 vCPUs +# can be added to a SB/VM, but the memory footprint will be big. Another example, with +# `default_maxvcpus = 8` the memory footprint will be small, but 8 will be the maximum number of +# vCPUs supported by the SB/VM. In general, we recommend that you do not edit this variable, +# unless you know what are you doing. +default_maxvcpus = @DEFMAXVCPUS@ + +# Bridges can be used to hot plug devices. +# Limitations: +# * Currently only pci bridges are supported +# * Until 30 devices per bridge can be hot plugged. +# * Until 5 PCI bridges can be cold plugged per VM. +# This limitation could be a bug in qemu or in the kernel +# Default number of bridges per SB/VM: +# unspecified or 0 --> will be set to @DEFBRIDGES@ +# > 1 <= 5 --> will be set to the specified number +# > 5 --> will be set to 5 +default_bridges = @DEFBRIDGES@ + +# Default memory size in MiB for SB/VM. +# If unspecified then it will be set @DEFMEMSZ@ MiB. +default_memory = @DEFMEMSZ@ +# +# Default memory slots per SB/VM. +# If unspecified then it will be set @DEFMEMSLOTS@. +# This is will determine the times that memory will be hotadded to sandbox/VM. +#memory_slots = @DEFMEMSLOTS@ + +# The size in MiB will be plused to max memory of hypervisor. +# It is the memory address space for the NVDIMM devie. +# If set block storage driver (block_device_driver) to "nvdimm", +# should set memory_offset to the size of block device. +# Default 0 +#memory_offset = 0 + +# Specifies virtio-mem will be enabled or not. +# Please note that this option should be used with the command +# "echo 1 > /proc/sys/vm/overcommit_memory". +# Default false +#enable_virtio_mem = true + +# Disable block device from being used for a container's rootfs. +# In case of a storage driver like devicemapper where a container's +# root file system is backed by a block device, the block device is passed +# directly to the hypervisor for performance reasons. +# This flag prevents the block device from being passed to the hypervisor, +# 9pfs is used instead to pass the rootfs. +disable_block_device_use = @DEFDISABLEBLOCK@ + +# Shared file system type: +# - virtio-9p (default) +# - virtio-fs +shared_fs = "@DEFSHAREDFS@" + +# Path to vhost-user-fs daemon. +virtio_fs_daemon = "@DEFVIRTIOFSDAEMON@" + +# Default size of DAX cache in MiB +virtio_fs_cache_size = @DEFVIRTIOFSCACHESIZE@ + +# Extra args for virtiofsd daemon +# +# Format example: +# ["-o", "arg1=xxx,arg2", "-o", "hello world", "--arg3=yyy"] +# +# see `virtiofsd -h` for possible options. +virtio_fs_extra_args = @DEFVIRTIOFSEXTRAARGS@ + +# Cache mode: +# +# - none +# Metadata, data, and pathname lookup are not cached in guest. They are +# always fetched from host and any changes are immediately pushed to host. +# +# - auto +# Metadata and pathname lookup cache expires after a configured amount of +# time (default is 1 second). Data is cached while the file is open (close +# to open consistency). +# +# - always +# Metadata, data, and pathname lookup are cached in guest and never expire. +virtio_fs_cache = "@DEFVIRTIOFSCACHE@" + +# Block storage driver to be used for the hypervisor in case the container +# rootfs is backed by a block device. This is virtio-scsi, virtio-blk +# or nvdimm. +block_device_driver = "@DEFBLOCKSTORAGEDRIVER_QEMU@" + +# Specifies cache-related options will be set to block devices or not. +# Default false +#block_device_cache_set = true + +# Specifies cache-related options for block devices. +# Denotes whether use of O_DIRECT (bypass the host page cache) is enabled. +# Default false +#block_device_cache_direct = true + +# Specifies cache-related options for block devices. +# Denotes whether flush requests for the device are ignored. +# Default false +#block_device_cache_noflush = true + +# Enable iothreads (data-plane) to be used. This causes IO to be +# handled in a separate IO thread. This is currently only implemented +# for SCSI. +# +enable_iothreads = @DEFENABLEIOTHREADS@ + +# Enable pre allocation of VM RAM, default false +# Enabling this will result in lower container density +# as all of the memory will be allocated and locked +# This is useful when you want to reserve all the memory +# upfront or in the cases where you want memory latencies +# to be very predictable +# Default false +#enable_mem_prealloc = true + +# Enable huge pages for VM RAM, default false +# Enabling this will result in the VM memory +# being allocated using huge pages. +# This is useful when you want to use vhost-user network +# stacks within the container. This will automatically +# result in memory pre allocation +#enable_hugepages = true + +# Enable vhost-user storage device, default false +# Enabling this will result in some Linux reserved block type +# major range 240-254 being chosen to represent vhost-user devices. +enable_vhost_user_store = @DEFENABLEVHOSTUSERSTORE@ + +# The base directory specifically used for vhost-user devices. +# Its sub-path "block" is used for block devices; "block/sockets" is +# where we expect vhost-user sockets to live; "block/devices" is where +# simulated block device nodes for vhost-user devices to live. +vhost_user_store_path = "@DEFVHOSTUSERSTOREPATH@" + +# Enable file based guest memory support. The default is an empty string which +# will disable this feature. In the case of virtio-fs, this is enabled +# automatically and '/dev/shm' is used as the backing folder. +# This option will be ignored if VM templating is enabled. +#file_mem_backend = "" + +# Enable swap of vm memory. Default false. +# The behaviour is undefined if mem_prealloc is also set to true +#enable_swap = true + +# This option changes the default hypervisor and kernel parameters +# to enable debug output where available. This extra output is added +# to the proxy logs, but only when proxy debug is also enabled. +# +# Default false +#enable_debug = true + +# Disable the customizations done in the runtime when it detects +# that it is running on top a VMM. This will result in the runtime +# behaving as it would when running on bare metal. +# +#disable_nesting_checks = true + +# This is the msize used for 9p shares. It is the number of bytes +# used for 9p packet payload. +#msize_9p = @DEFMSIZE9P@ + +# If true and vsocks are supported, use vsocks to communicate directly +# with the agent and no proxy is started, otherwise use unix +# sockets and start a proxy to communicate with the agent. +# Default false +#use_vsock = true + +# If false and nvdimm is supported, use nvdimm device to plug guest image. +# Otherwise virtio-block device is used. +# Default is false +#disable_image_nvdimm = true + +# VFIO devices are hotplugged on a bridge by default. +# Enable hotplugging on root bus. This may be required for devices with +# a large PCI bar, as this is a current limitation with hotplugging on +# a bridge. This value is valid for "pc" machine type. +# Default false +#hotplug_vfio_on_root_bus = true + +# Before hot plugging a PCIe device, you need to add a pcie_root_port device. +# Use this parameter when using some large PCI bar devices, such as Nvidia GPU +# The value means the number of pcie_root_port +# This value is valid when hotplug_vfio_on_root_bus is true and machine_type is "q35" +# Default 0 +#pcie_root_port = 2 + +# If vhost-net backend for virtio-net is not desired, set to true. Default is false, which trades off +# security (vhost-net runs ring0) for network I/O performance. +#disable_vhost_net = true + +# +# Default entropy source. +# The path to a host source of entropy (including a real hardware RNG) +# /dev/urandom and /dev/random are two main options. +# Be aware that /dev/random is a blocking source of entropy. If the host +# runs out of entropy, the VMs boot time will increase leading to get startup +# timeouts. +# The source of entropy /dev/urandom is non-blocking and provides a +# generally acceptable source of entropy. It should work well for pretty much +# all practical purposes. +#entropy_source= "@DEFENTROPYSOURCE@" + +# Path to OCI hook binaries in the *guest rootfs*. +# This does not affect host-side hooks which must instead be added to +# the OCI spec passed to the runtime. +# +# You can create a rootfs with hooks by customizing the osbuilder scripts: +# https://github.com/kata-containers/osbuilder +# +# Hooks must be stored in a subdirectory of guest_hook_path according to their +# hook type, i.e. "guest_hook_path/{prestart,postart,poststop}". +# The agent will scan these directories for executable files and add them, in +# lexicographical order, to the lifecycle of the guest container. +# Hooks are executed in the runtime namespace of the guest. See the official documentation: +# https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#posix-platform-hooks +# Warnings will be logged if any error is encountered will scanning for hooks, +# but it will not abort container execution. +#guest_hook_path = "/usr/share/oci/hooks" + +[factory] +# VM templating support. Once enabled, new VMs are created from template +# using vm cloning. They will share the same initial kernel, initramfs and +# agent memory by mapping it readonly. It helps speeding up new container +# creation and saves a lot of memory if there are many kata containers running +# on the same host. +# +# When disabled, new VMs are created from scratch. +# +# Note: Requires "initrd=" to be set ("image=" is not supported). +# +# Default false +#enable_template = true + +# Specifies the path of template. +# +# Default "/run/vc/vm/template" +#template_path = "/run/vc/vm/template" + +# The number of caches of VMCache: +# unspecified or == 0 --> VMCache is disabled +# > 0 --> will be set to the specified number +# +# VMCache is a function that creates VMs as caches before using it. +# It helps speed up new container creation. +# The function consists of a server and some clients communicating +# through Unix socket. The protocol is gRPC in protocols/cache/cache.proto. +# The VMCache server will create some VMs and cache them by factory cache. +# It will convert the VM to gRPC format and transport it when gets +# requestion from clients. +# Factory grpccache is the VMCache client. It will request gRPC format +# VM and convert it back to a VM. If VMCache function is enabled, +# kata-runtime will request VM from factory grpccache when it creates +# a new sandbox. +# +# Default 0 +#vm_cache_number = 0 + +# Specify the address of the Unix socket that is used by VMCache. +# +# Default /var/run/kata-containers/cache.sock +#vm_cache_endpoint = "/var/run/kata-containers/cache.sock" + +[proxy.@PROJECT_TYPE@] +path = "@PROXYPATH@" + +# If enabled, proxy messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +[shim.@PROJECT_TYPE@] +path = "@SHIMPATH@" + +# If enabled, shim messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +# If enabled, the shim will create opentracing.io traces and spans. +# (See https://www.jaegertracing.io/docs/getting-started). +# +# Note: By default, the shim runs in a separate network namespace. Therefore, +# to allow it to send trace details to the Jaeger agent running on the host, +# it is necessary to set 'disable_new_netns=true' so that it runs in the host +# network namespace. +# +# (default: disabled) +#enable_tracing = true + +[agent.@PROJECT_TYPE@] +# If enabled, make the agent display debug-level messages. +# (default: disabled) +#enable_debug = true + +# Enable agent tracing. +# +# If enabled, the default trace mode is "dynamic" and the +# default trace type is "isolated". The trace mode and type are set +# explicity with the `trace_type=` and `trace_mode=` options. +# +# Notes: +# +# - Tracing is ONLY enabled when `enable_tracing` is set: explicitly +# setting `trace_mode=` and/or `trace_type=` without setting `enable_tracing` +# will NOT activate agent tracing. +# +# - See https://github.com/kata-containers/agent/blob/master/TRACING.md for +# full details. +# +# (default: disabled) +#enable_tracing = true +# +#trace_mode = "dynamic" +#trace_type = "isolated" + +# Comma separated list of kernel modules and their parameters. +# These modules will be loaded in the guest kernel using modprobe(8). +# The following example can be used to load two kernel modules with parameters +# - kernel_modules=["e1000e InterruptThrottleRate=3000,3000,3000 EEE=1", "i915 enable_ppgtt=0"] +# The first word is considered as the module name and the rest as its parameters. +# Container will not be started when: +# * A kernel module is specified and the modprobe command is not installed in the guest +# or it fails loading the module. +# * The module is not available in the guest or it doesn't met the guest kernel +# requirements, like architecture and version. +# +kernel_modules=[] + + +[netmon] +# If enabled, the network monitoring process gets started when the +# sandbox is created. This allows for the detection of some additional +# network being added to the existing network namespace, after the +# sandbox has been created. +# (default: disabled) +#enable_netmon = true + +# Specify the path to the netmon binary. +path = "@NETMONPATH@" + +# If enabled, netmon messages will be sent to the system log +# (default: disabled) +#enable_debug = true + +[runtime] +# If enabled, the runtime will log additional debug messages to the +# system log +# (default: disabled) +#enable_debug = true +# +# Internetworking model +# Determines how the VM should be connected to the +# the container network interface +# Options: +# +# - macvtap +# Used when the Container network interface can be bridged using +# macvtap. +# +# - none +# Used when customize network. Only creates a tap device. No veth pair. +# +# - tcfilter +# Uses tc filter rules to redirect traffic from the network interface +# provided by plugin to a tap interface connected to the VM. +# +internetworking_model="@DEFNETWORKMODEL_QEMU@" + +# disable guest seccomp +# Determines whether container seccomp profiles are passed to the virtual +# machine and applied by the kata agent. If set to true, seccomp is not applied +# within the guest +# (default: true) +disable_guest_seccomp=@DEFDISABLEGUESTSECCOMP@ + +# If enabled, the runtime will create opentracing.io traces and spans. +# (See https://www.jaegertracing.io/docs/getting-started). +# (default: disabled) +#enable_tracing = true + +# If enabled, the runtime will not create a network namespace for shim and hypervisor processes. +# This option may have some potential impacts to your host. It should only be used when you know what you're doing. +# `disable_new_netns` conflicts with `enable_netmon` +# `disable_new_netns` conflicts with `internetworking_model=tcfilter` and `internetworking_model=macvtap`. It works only +# with `internetworking_model=none`. The tap device will be in the host network namespace and can connect to a bridge +# (like OVS) directly. +# If you are using docker, `disable_new_netns` only works with `docker run --net=none` +# (default: false) +#disable_new_netns = true + +# if enabled, the runtime will add all the kata processes inside one dedicated cgroup. +# The container cgroups in the host are not created, just one single cgroup per sandbox. +# The runtime caller is free to restrict or collect cgroup stats of the overall Kata sandbox. +# The sandbox cgroup path is the parent cgroup of a container with the PodSandbox annotation. +# The sandbox cgroup is constrained if there is no container type annotation. +# See: https://godoc.org/github.com/kata-containers/runtime/virtcontainers#ContainerType +sandbox_cgroup_only=@DEFSANDBOXCGROUPONLY@ + +# Enabled experimental feature list, format: ["a", "b"]. +# Experimental features are features not stable enough for production, +# they may break compatibility, and are prepared for a big version bump. +# Supported experimental features: +# (default: []) +experimental=@DEFAULTEXPFEATURES@ diff --git a/src/runtime/cli/console.go b/src/runtime/cli/console.go new file mode 100644 index 000000000..c1555c9f4 --- /dev/null +++ b/src/runtime/cli/console.go @@ -0,0 +1,134 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "io" + "os" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +var ptmxPath = "/dev/ptmx" + +// Console represents a pseudo TTY. +type Console struct { + io.ReadWriteCloser + + master *os.File + slavePath string +} + +// isTerminal returns true if fd is a terminal, else false +func isTerminal(fd uintptr) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, syscall.TCGETS, uintptr(unsafe.Pointer(&termios))) + return err == 0 +} + +// ConsoleFromFile creates a console from a file +func ConsoleFromFile(f *os.File) *Console { + return &Console{ + master: f, + } +} + +// NewConsole returns an initialized console that can be used within a container by copying bytes +// from the master side to the slave that is attached as the tty for the container's init process. +func newConsole() (*Console, error) { + master, err := os.OpenFile(ptmxPath, unix.O_RDWR|unix.O_NOCTTY|unix.O_CLOEXEC, 0) + if err != nil { + return nil, err + } + if err := saneTerminal(master); err != nil { + return nil, err + } + console, err := ptsname(master) + if err != nil { + return nil, err + } + if err := unlockpt(master); err != nil { + return nil, err + } + return &Console{ + slavePath: console, + master: master, + }, nil +} + +// File returns master +func (c *Console) File() *os.File { + return c.master +} + +// Path to slave +func (c *Console) Path() string { + return c.slavePath +} + +// Read from master +func (c *Console) Read(b []byte) (int, error) { + return c.master.Read(b) +} + +// Write to master +func (c *Console) Write(b []byte) (int, error) { + return c.master.Write(b) +} + +// Close master +func (c *Console) Close() error { + if m := c.master; m != nil { + return m.Close() + } + return nil +} + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + var u int32 + if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))); err != 0 { + return err + } + return nil +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + var u uint32 + if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCGPTN, uintptr(unsafe.Pointer(&u))); err != 0 { + return "", err + } + return fmt.Sprintf("/dev/pts/%d", u), nil +} + +// saneTerminal sets the necessary tty_ioctl(4)s to ensure that a pty pair +// created by us acts normally. In particular, a not-very-well-known default of +// Linux unix98 ptys is that they have +onlcr by default. While this isn't a +// problem for terminal emulators, because we relay data from the terminal we +// also relay that funky line discipline. +func saneTerminal(terminal *os.File) error { + // Go doesn't have a wrapper for any of the termios ioctls. + var termios unix.Termios + + if _, _, err := unix.Syscall(unix.SYS_IOCTL, terminal.Fd(), unix.TCGETS, uintptr(unsafe.Pointer(&termios))); err != 0 { + return fmt.Errorf("ioctl(tty, tcgets): %s", err.Error()) + } + + // Set -onlcr so we don't have to deal with \r. + termios.Oflag &^= unix.ONLCR + + if _, _, err := unix.Syscall(unix.SYS_IOCTL, terminal.Fd(), unix.TCSETS, uintptr(unsafe.Pointer(&termios))); err != 0 { + return fmt.Errorf("ioctl(tty, tcsets): %s", err.Error()) + } + + return nil +} diff --git a/src/runtime/cli/console_test.go b/src/runtime/cli/console_test.go new file mode 100644 index 000000000..951c9c24c --- /dev/null +++ b/src/runtime/cli/console_test.go @@ -0,0 +1,122 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConsoleFromFile(t *testing.T) { + assert := assert.New(t) + + console := ConsoleFromFile(os.Stdout) + + assert.NotNil(console.File(), "console file is nil") +} + +func TestNewConsole(t *testing.T) { + assert := assert.New(t) + + console, err := newConsole() + assert.NoError(err, "failed to create a new console: %s", err) + defer console.Close() + + assert.NotEmpty(console.Path(), "console path is empty") + + assert.NotNil(console.File(), "console file is nil") +} + +func TestIsTerminal(t *testing.T) { + assert := assert.New(t) + + var fd uintptr = 4 + assert.False(isTerminal(fd), "Fd %d is not a terminal", fd) + + console, err := newConsole() + assert.NoError(err, "failed to create a new console: %s", err) + defer console.Close() + + fd = console.File().Fd() + assert.True(isTerminal(fd), "Fd %d is a terminal", fd) +} + +func TestReadWrite(t *testing.T) { + assert := assert.New(t) + + // write operation + f, err := ioutil.TempFile(os.TempDir(), ".tty") + assert.NoError(err, "failed to create a temporal file") + defer os.Remove(f.Name()) + + console := ConsoleFromFile(f) + assert.NotNil(console) + defer console.Close() + + msgWrite := "hello" + l, err := console.Write([]byte(msgWrite)) + assert.NoError(err, "failed to write message: %s", msgWrite) + assert.Equal(len(msgWrite), l) + + console.master.Sync() + console.master.Seek(0, 0) + + // Read operation + msgRead := make([]byte, len(msgWrite)) + l, err = console.Read(msgRead) + assert.NoError(err, "failed to read message: %s", msgWrite) + assert.Equal(len(msgWrite), l) + assert.Equal(msgWrite, string(msgRead)) +} + +func TestNewConsoleFail(t *testing.T) { + assert := assert.New(t) + + orgPtmxPath := ptmxPath + defer func() { ptmxPath = orgPtmxPath }() + + // OpenFile failure + ptmxPath = "/this/file/does/not/exist" + c, err := newConsole() + assert.Error(err) + assert.Nil(c) + + // saneTerminal failure + f, err := ioutil.TempFile("", "") + assert.NoError(err) + assert.NoError(f.Close()) + defer os.Remove(f.Name()) + ptmxPath = f.Name() + c, err = newConsole() + assert.Error(err) + assert.Nil(c) +} + +func TestConsoleClose(t *testing.T) { + assert := assert.New(t) + + // nil master + c := &Console{} + assert.NoError(c.Close()) + + f, err := ioutil.TempFile("", "") + assert.NoError(err) + defer os.Remove(f.Name()) + + c.master = f + assert.NoError(c.Close()) +} + +func TestConsolePtsnameFail(t *testing.T) { + assert := assert.New(t) + + pts, err := ptsname(nil) + assert.Error(err) + assert.Empty(pts) +} diff --git a/src/runtime/cli/containerd-shim-kata-v2/main.go b/src/runtime/cli/containerd-shim-kata-v2/main.go new file mode 100644 index 000000000..03ce932ee --- /dev/null +++ b/src/runtime/cli/containerd-shim-kata-v2/main.go @@ -0,0 +1,20 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "github.com/containerd/containerd/runtime/v2/shim" + "github.com/kata-containers/runtime/containerd-shim-v2" +) + +func shimConfig(config *shim.Config) { + config.NoReaper = true + config.NoSubreaper = true +} + +func main() { + shim.Run("io.containerd.kata.v2", containerdshim.New, shimConfig) +} diff --git a/src/runtime/cli/create.go b/src/runtime/cli/create.go new file mode 100644 index 000000000..02cb2c536 --- /dev/null +++ b/src/runtime/cli/create.go @@ -0,0 +1,185 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "errors" + "fmt" + "os" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/urfave/cli" +) + +var createCLICommand = cli.Command{ + Name: "create", + Usage: "Create a container", + ArgsUsage: ` + + 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 "` + specConfig + `" 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.`, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "bundle, b", + Value: "", + Usage: `path to the root of the bundle directory, defaults to the current directory`, + }, + cli.StringFlag{ + Name: "console", + Value: "", + Usage: "path to a pseudo terminal", + }, + cli.StringFlag{ + Name: "console-socket", + Value: "", + Usage: "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal", + }, + cli.StringFlag{ + Name: "pid-file", + Value: "", + Usage: "specify the file to write the process id to", + }, + cli.BoolFlag{ + Name: "no-pivot", + Usage: "warning: this flag is meaningless to kata-runtime, just defined in order to be compatible with docker in ramdisk", + }, + }, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig) + if !ok { + return errors.New("invalid runtime config") + } + + console, err := setupConsole(context.String("console"), context.String("console-socket")) + if err != nil { + return err + } + + return create(ctx, context.Args().First(), + context.String("bundle"), + console, + context.String("pid-file"), + true, + context.Bool("systemd-cgroup"), + runtimeConfig, + ) + }, +} + +func create(ctx context.Context, containerID, bundlePath, console, pidFilePath string, detach, systemdCgroup bool, + runtimeConfig oci.RuntimeConfig) error { + var err error + + span, ctx := katautils.Trace(ctx, "create") + defer span.Finish() + + kataLog = kataLog.WithField("container", containerID) + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + + if bundlePath == "" { + cwd, err := os.Getwd() + if err != nil { + return err + } + + kataLog.WithField("directory", cwd).Debug("Defaulting bundle path to current directory") + + bundlePath = cwd + } + + // Checks the MUST and MUST NOT from OCI runtime specification + if bundlePath, err = validCreateParams(ctx, containerID, bundlePath); err != nil { + return err + } + + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + if err != nil { + return err + } + + containerType, err := oci.ContainerType(ociSpec) + if err != nil { + return err + } + + katautils.HandleFactory(ctx, vci, &runtimeConfig) + + disableOutput := noNeedForOutput(detach, ociSpec.Process.Terminal) + + //rootfs has been mounted by containerd shim + rootFs := vc.RootFs{Mounted: true} + + var process vc.Process + switch containerType { + case vc.PodSandbox: + _, process, err = katautils.CreateSandbox(ctx, vci, ociSpec, runtimeConfig, rootFs, containerID, bundlePath, console, disableOutput, systemdCgroup, false) + if err != nil { + return err + } + case vc.PodContainer: + process, err = katautils.CreateContainer(ctx, vci, nil, ociSpec, rootFs, containerID, bundlePath, console, disableOutput, false) + if err != nil { + return err + } + } + + // Creation of PID file has to be the last thing done in the create + // because containerd considers the create complete after this file + // is created. + return createPIDFile(ctx, pidFilePath, process.Pid) +} + +func createPIDFile(ctx context.Context, pidFilePath string, pid int) error { + span, _ := katautils.Trace(ctx, "createPIDFile") + defer span.Finish() + + if pidFilePath == "" { + // runtime should not fail since pid file is optional + return nil + } + + if err := os.RemoveAll(pidFilePath); err != nil { + return err + } + + f, err := os.Create(pidFilePath) + if err != nil { + return err + } + defer f.Close() + + pidStr := fmt.Sprintf("%d", pid) + + n, err := f.WriteString(pidStr) + if err != nil { + return err + } + + if n < len(pidStr) { + return fmt.Errorf("Could not write pid to '%s': only %d bytes written out of %d", pidFilePath, n, len(pidStr)) + } + + return nil +} diff --git a/src/runtime/cli/create_test.go b/src/runtime/cli/create_test.go new file mode 100644 index 000000000..41741507e --- /dev/null +++ b/src/runtime/cli/create_test.go @@ -0,0 +1,724 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "testing" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +const ( + testPID = 100 + testConsole = "/dev/pts/999" + testContainerTypeAnnotation = "io.kubernetes.cri-o.ContainerType" + testContainerTypeSandbox = "sandbox" + testContainerTypeContainer = "container" +) + +var ( + testStrPID = fmt.Sprintf("%d", testPID) + ctrsMapTreePath = "/var/run/kata-containers/containers-mapping" +) + +func TestCreatePIDFileSuccessful(t *testing.T) { + pidDirPath, err := ioutil.TempDir(testDir, "pid-path-") + if err != nil { + t.Fatalf("Could not create temporary PID directory: %s", err) + } + + pidFilePath := filepath.Join(pidDirPath, "pid-file-path") + if err := createPIDFile(context.Background(), pidFilePath, testPID); err != nil { + t.Fatal(err) + } + + fileBytes, err := ioutil.ReadFile(pidFilePath) + if err != nil { + os.RemoveAll(pidFilePath) + t.Fatalf("Could not read %q: %s", pidFilePath, err) + } + + if string(fileBytes) != testStrPID { + os.RemoveAll(pidFilePath) + t.Fatalf("PID %s read from %q different from expected PID %s", string(fileBytes), pidFilePath, testStrPID) + } + + os.RemoveAll(pidFilePath) +} + +func TestCreatePIDFileEmptyPathSuccessful(t *testing.T) { + file := "" + if err := createPIDFile(context.Background(), file, testPID); err != nil { + t.Fatalf("This test should not fail (pidFilePath %q, pid %d)", file, testPID) + } +} + +func TestCreatePIDFileUnableToRemove(t *testing.T) { + if tc.NotValid(ktu.NeedNonRoot()) { + // The os.FileMode(0000) trick doesn't work for root. + t.Skip(ktu.TestDisabledNeedNonRoot) + } + + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + subdir := filepath.Join(tmpdir, "dir") + file := filepath.Join(subdir, "pidfile") + + // stop non-root user from removing the directory later + err = os.MkdirAll(subdir, os.FileMode(0000)) + assert.NoError(err) + + err = createPIDFile(context.Background(), file, testPID) + assert.Error(err) + + // let it be deleted + os.Chmod(subdir, testDirMode) +} + +func TestCreatePIDFileUnableToCreate(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + subdir := filepath.Join(tmpdir, "dir") + file := filepath.Join(subdir, "pidfile") + + err = createPIDFile(context.Background(), file, testPID) + + // subdir doesn't exist + assert.Error(err) + os.Chmod(subdir, testDirMode) +} + +func TestCreateCLIFunctionNoRuntimeConfig(t *testing.T) { + assert := assert.New(t) + + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + ctx.App.Metadata["foo"] = "bar" + + fn, ok := createCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err := fn(ctx) + + // no runtime config in the Metadata + assert.Error(err) +} + +func TestCreateCLIFunctionSetupConsoleFail(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + subdir := filepath.Join(tmpdir, "dir") + + // does not exist + consoleSocketPath := filepath.Join(subdir, "console") + + set := flag.NewFlagSet("", 0) + + set.String("console-socket", consoleSocketPath, "") + + ctx := createCLIContext(set) + ctx.App.Name = "foo" + + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + + fn, ok := createCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + + // failed to setup console + assert.Error(err) +} + +func TestCreateCLIFunctionCreateFail(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + set := flag.NewFlagSet("", 0) + + set.String("console-socket", "", "") + + ctx := createCLIContext(set) + ctx.App.Name = "foo" + + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + + fn, ok := createCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + + // create() failed + assert.Error(err) +} + +func TestCreateInvalidArgs(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + MockContainers: []*vcmock.Container{ + {MockID: testContainerID}, + {MockID: testContainerID}, + {MockID: testContainerID}, + }, + } + + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + return sandbox, nil + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + katautils.SetCtrsMapTreePath(ctrsMapTreePath) + + defer func() { + testingImpl.CreateSandboxFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + type testData struct { + containerID string + bundlePath string + console string + pidFilePath string + detach bool + systemdCgroup bool + runtimeConfig oci.RuntimeConfig + } + + data := []testData{ + {"", "", "", "", false, false, oci.RuntimeConfig{}}, + {"", "", "", "", true, true, oci.RuntimeConfig{}}, + {"foo", "", "", "", true, false, oci.RuntimeConfig{}}, + {testContainerID, bundlePath, testConsole, pidFilePath, false, false, runtimeConfig}, + {testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig}, + } + + for i, d := range data { + err := create(context.Background(), d.containerID, d.bundlePath, d.console, d.pidFilePath, d.detach, d.systemdCgroup, d.runtimeConfig) + assert.Errorf(err, "test %d (%+v)", i, d) + } +} + +func TestCreateInvalidConfigJSON(t *testing.T) { + assert := assert.New(t) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + katautils.SetCtrsMapTreePath(ctrsMapTreePath) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + f, err := os.OpenFile(ociConfigFile, os.O_APPEND|os.O_WRONLY, testFileMode) + assert.NoError(err) + + // invalidate the JSON + _, err = f.WriteString("{") + assert.NoError(err) + f.Close() + + for detach := range []bool{true, false} { + err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig) + assert.Errorf(err, "%+v", detach) + assert.False(vcmock.IsMockError(err)) + os.RemoveAll(path) + } +} + +func TestCreateInvalidContainerType(t *testing.T) { + assert := assert.New(t) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + katautils.SetCtrsMapTreePath(ctrsMapTreePath) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + // Force an invalid container type + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = "I-am-not-a-valid-container-type" + + // rewrite the file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + for detach := range []bool{true, false} { + err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig) + assert.Errorf(err, "%+v", detach) + assert.False(vcmock.IsMockError(err)) + os.RemoveAll(path) + } +} + +func TestCreateContainerInvalid(t *testing.T) { + assert := assert.New(t) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + katautils.SetCtrsMapTreePath(ctrsMapTreePath) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + + assert.NoError(err) + + // Force createContainer() to be called. + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer + + // rewrite the file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + for detach := range []bool{true, false} { + err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig) + assert.Errorf(err, "%+v", detach) + assert.False(vcmock.IsMockError(err)) + os.RemoveAll(path) + } +} + +func TestCreateProcessCgroupsPathSuccessful(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + MockContainers: []*vcmock.Container{ + {MockID: testContainerID}, + }, + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + katautils.SetCtrsMapTreePath(ctrsMapTreePath) + + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.CreateSandboxFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + // Force sandbox-type container + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeSandbox + + // Set a limit to ensure processCgroupsPath() considers the + // cgroup part of the spec + limit := int64(1024 * 1024) + spec.Linux.Resources.Memory = &specs.LinuxMemory{ + Limit: &limit, + } + + // Set an absolute path + spec.Linux.CgroupsPath = "/this/is/a/cgroup/path" + + var mounts []specs.Mount + foundMount := false + + // Replace the standard cgroup destination with a temporary one. + for _, mount := range spec.Mounts { + if mount.Type == "cgroup" { + foundMount = true + cgroupDir, err := ioutil.TempDir("", "cgroup") + assert.NoError(err) + + defer os.RemoveAll(cgroupDir) + mount.Destination = cgroupDir + } + + mounts = append(mounts, mount) + } + + assert.True(foundMount) + + // Replace mounts with the newly created one. + spec.Mounts = mounts + + // Rewrite the file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + err = create(context.Background(), testContainerID, "", testConsole, pidFilePath, false, true, runtimeConfig) + assert.Error(err, "bundle path not set") + + re := regexp.MustCompile("config.json.*no such file or directory") + matches := re.FindAllStringSubmatch(err.Error(), -1) + assert.NotEmpty(matches) + + for _, detach := range []bool{true, false} { + err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, detach, true, runtimeConfig) + assert.NoError(err, "detached: %+v", detach) + os.RemoveAll(path) + } +} + +func TestCreateCreateCgroupsFilesFail(t *testing.T) { + if tc.NotValid(ktu.NeedNonRoot()) { + // The os.FileMode(0000) trick doesn't work for root. + t.Skip(ktu.TestDisabledNeedNonRoot) + } + + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + MockContainers: []*vcmock.Container{ + {MockID: testContainerID}, + }, + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + katautils.SetCtrsMapTreePath(ctrsMapTreePath) + + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.CreateSandboxFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + // Force sandbox-type container + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeSandbox + + // Set a limit to ensure processCgroupsPath() considers the + // cgroup part of the spec + limit := int64(1024 * 1024) + spec.Linux.Resources.Memory = &specs.LinuxMemory{ + Limit: &limit, + } + + // Override + cgroupsDirPath = filepath.Join(tmpdir, "cgroups") + err = os.MkdirAll(cgroupsDirPath, testDirMode) + assert.NoError(err) + + // Set a relative path + spec.Linux.CgroupsPath = "./a/relative/path" + + dir := filepath.Join(cgroupsDirPath, "memory") + + // Stop directory from being created + err = os.MkdirAll(dir, os.FileMode(0000)) + assert.NoError(err) + + // Rewrite the file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + for detach := range []bool{true, false} { + err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig) + assert.Errorf(err, "%+v", detach) + assert.False(vcmock.IsMockError(err)) + os.RemoveAll(path) + } +} + +func TestCreateCreateCreatePidFileFail(t *testing.T) { + if tc.NotValid(ktu.NeedNonRoot()) { + // The os.FileMode(0000) trick doesn't work for root. + t.Skip(ktu.TestDisabledNeedNonRoot) + } + + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + MockContainers: []*vcmock.Container{ + {MockID: testContainerID}, + }, + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + katautils.SetCtrsMapTreePath(ctrsMapTreePath) + + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.CreateSandboxFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidDir := filepath.Join(tmpdir, "pid") + pidFilePath := filepath.Join(pidDir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + // Force sandbox-type container + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeSandbox + + // Set a limit to ensure processCgroupsPath() considers the + // cgroup part of the spec + limit := int64(1024 * 1024) + spec.Linux.Resources.Memory = &specs.LinuxMemory{ + Limit: &limit, + } + + // Rewrite the file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + // stop the pidfile from being created + err = os.MkdirAll(pidDir, os.FileMode(0000)) + assert.NoError(err) + + for detach := range []bool{true, false} { + err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig) + assert.Errorf(err, "%+v", detach) + assert.False(vcmock.IsMockError(err)) + os.RemoveAll(path) + } +} + +func TestCreate(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + MockContainers: []*vcmock.Container{ + {MockID: testContainerID}, + }, + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + katautils.SetCtrsMapTreePath(ctrsMapTreePath) + + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.CreateSandboxFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + pidFilePath := filepath.Join(tmpdir, "pidfile.txt") + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + // Force sandbox-type container + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeSandbox + + // Set a limit to ensure processCgroupsPath() considers the + // cgroup part of the spec + limit := int64(1024 * 1024) + spec.Linux.Resources.Memory = &specs.LinuxMemory{ + Limit: &limit, + } + + // Rewrite the file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + for detach := range []bool{true, false} { + err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, true, runtimeConfig) + assert.NoError(err, "%+v", detach) + os.RemoveAll(path) + } +} diff --git a/src/runtime/cli/delete.go b/src/runtime/cli/delete.go new file mode 100644 index 000000000..c2ce52a46 --- /dev/null +++ b/src/runtime/cli/delete.go @@ -0,0 +1,189 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "fmt" + "os" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnot "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var deleteCLICommand = cli.Command{ + Name: "delete", + Usage: "Delete any resources held by one or more containers", + ArgsUsage: ` [container-id...] + + is the name for the instance of the container. + +EXAMPLE: + If the container id is "ubuntu01" and ` + name + ` list currently shows the + status of "ubuntu01" as "stopped" the following will delete resources held + for "ubuntu01" removing "ubuntu01" from the ` + name + ` list of containers: + + # ` + name + ` delete ubuntu01`, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "force, f", + Usage: "Forcibly deletes the container if it is still running (uses SIGKILL)", + }, + }, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + args := context.Args() + if !args.Present() { + return fmt.Errorf("Missing container ID, should at least provide one") + } + + force := context.Bool("force") + for _, cID := range []string(args) { + if err := delete(ctx, cID, force); err != nil { + return err + } + } + + return nil + }, +} + +func delete(ctx context.Context, containerID string, force bool) error { + span, ctx := katautils.Trace(ctx, "delete") + defer span.Finish() + + kataLog = kataLog.WithField("container", containerID) + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + + // Checks the MUST and MUST NOT from OCI runtime specification + status, sandboxID, err := getExistingContainerInfo(ctx, containerID) + if err != nil { + if force { + kataLog.Warnf("Failed to get container, force will not fail: %s", err) + return nil + } + return err + } + + containerID = status.ID + + kataLog = kataLog.WithFields(logrus.Fields{ + "container": containerID, + "sandbox": sandboxID, + }) + + setExternalLoggers(ctx, kataLog) + + span.SetTag("container", containerID) + span.SetTag("sandbox", sandboxID) + + containerType, err := oci.GetContainerType(status.Annotations) + if err != nil { + return err + } + + // Retrieve OCI spec configuration. + ociSpec, err := oci.GetOCIConfig(status) + if err != nil { + return err + } + + forceStop := false + if oci.StateToOCIState(status.State.State) == oci.StateRunning { + if !force { + return fmt.Errorf("Container still running, should be stopped") + } + + forceStop = true + } + + switch containerType { + case vc.PodSandbox: + if err := deleteSandbox(ctx, sandboxID, force); err != nil { + return err + } + case vc.PodContainer: + if err := deleteContainer(ctx, sandboxID, containerID, forceStop); err != nil { + return err + } + default: + return fmt.Errorf("Invalid container type found") + } + + // Run post-stop OCI hooks. + if err := katautils.PostStopHooks(ctx, ociSpec, sandboxID, status.Annotations[vcAnnot.BundlePathKey]); err != nil { + return err + } + + return katautils.DelContainerIDMapping(ctx, containerID) +} + +func deleteSandbox(ctx context.Context, sandboxID string, force bool) error { + span, _ := katautils.Trace(ctx, "deleteSandbox") + defer span.Finish() + + status, err := vci.StatusSandbox(ctx, sandboxID) + if err != nil { + return err + } + + if oci.StateToOCIState(status.State.State) != oci.StateStopped { + if _, err := vci.StopSandbox(ctx, sandboxID, force); err != nil { + return err + } + } + + if _, err := vci.DeleteSandbox(ctx, sandboxID); err != nil { + return err + } + + return nil +} + +func deleteContainer(ctx context.Context, sandboxID, containerID string, forceStop bool) error { + span, _ := katautils.Trace(ctx, "deleteContainer") + defer span.Finish() + + if forceStop { + if _, err := vci.StopContainer(ctx, sandboxID, containerID); err != nil { + return err + } + } + + if _, err := vci.DeleteContainer(ctx, sandboxID, containerID); err != nil { + return err + } + + return nil +} + +func removeCgroupsPath(ctx context.Context, containerID string, cgroupsPathList []string) error { + span, _ := katautils.Trace(ctx, "removeCgroupsPath") + defer span.Finish() + + if len(cgroupsPathList) == 0 { + kataLog.WithField("container", containerID).Info("Cgroups files not removed because cgroupsPath was empty") + return nil + } + + for _, cgroupsPath := range cgroupsPathList { + if err := os.RemoveAll(cgroupsPath); err != nil { + return err + } + } + + return nil +} diff --git a/src/runtime/cli/delete_test.go b/src/runtime/cli/delete_test.go new file mode 100644 index 000000000..a2455deed --- /dev/null +++ b/src/runtime/cli/delete_test.go @@ -0,0 +1,626 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/kata-containers/runtime/virtcontainers/types" +) + +func testRemoveCgroupsPathSuccessful(t *testing.T, cgroupsPathList []string) { + if err := removeCgroupsPath(context.Background(), "foo", cgroupsPathList); err != nil { + t.Fatalf("This test should succeed (cgroupsPathList = %v): %s", cgroupsPathList, err) + } +} + +func TestRemoveCgroupsPathEmptyPathSuccessful(t *testing.T) { + testRemoveCgroupsPathSuccessful(t, []string{}) +} + +func TestRemoveCgroupsPathNonEmptyPathSuccessful(t *testing.T) { + cgroupsPath, err := ioutil.TempDir(testDir, "cgroups-path-") + if err != nil { + t.Fatalf("Could not create temporary cgroups directory: %s", err) + } + + if err := os.MkdirAll(cgroupsPath, testDirMode); err != nil { + t.Fatalf("CgroupsPath directory %q could not be created: %s", cgroupsPath, err) + } + + testRemoveCgroupsPathSuccessful(t, []string{cgroupsPath}) + + if _, err := os.Stat(cgroupsPath); err == nil { + t.Fatalf("CgroupsPath directory %q should have been removed: %s", cgroupsPath, err) + } +} + +func TestDeleteInvalidContainer(t *testing.T) { + assert := assert.New(t) + + // Missing container id + err := delete(context.Background(), "", false) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + // Container missing in ListSandbox + err = delete(context.Background(), testContainerID, false) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + // Force to delete missing container + err = delete(context.Background(), "non-existing-test", true) + assert.NoError(err) +} + +func TestDeleteMissingContainerTypeAnnotation(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{}, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + err = delete(context.Background(), sandbox.ID(), false) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestDeleteInvalidConfig(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + }, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + err = delete(context.Background(), sandbox.ID(), false) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func testConfigSetup(t *testing.T) (rootPath string, bundlePath string) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + + bundlePath = filepath.Join(tmpdir, "bundle") + err = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = createOCIConfig(bundlePath) + assert.NoError(err) + + return tmpdir, bundlePath +} + +func TestDeleteSandbox(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + }, + State: types.ContainerState{ + State: "ready", + }, + Spec: &ociSpec, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + err = delete(context.Background(), sandbox.ID(), false) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + testingImpl.StatusSandboxFunc = func(ctx context.Context, sandboxID string) (vc.SandboxStatus, error) { + return vc.SandboxStatus{ + ID: sandbox.ID(), + State: types.SandboxState{ + State: types.StateReady, + }, + }, nil + } + + defer func() { + testingImpl.StatusSandboxFunc = nil + }() + + err = delete(context.Background(), sandbox.ID(), false) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + testingImpl.StopSandboxFunc = func(ctx context.Context, sandboxID string, force bool) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.StopSandboxFunc = nil + }() + + err = delete(context.Background(), sandbox.ID(), false) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.DeleteSandboxFunc = nil + }() + + err = delete(context.Background(), sandbox.ID(), false) + assert.Nil(err) +} + +func TestDeleteInvalidContainerType(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: "InvalidType", + }, + State: types.ContainerState{ + State: "created", + }, + Spec: &ociSpec, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + // Delete an invalid container type + err = delete(context.Background(), sandbox.ID(), false) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestDeleteSandboxRunning(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + }, + State: types.ContainerState{ + State: "running", + }, + Spec: &ociSpec, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + // Delete on a running sandbox should fail + err = delete(context.Background(), sandbox.ID(), false) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + testingImpl.StatusSandboxFunc = func(ctx context.Context, sandboxID string) (vc.SandboxStatus, error) { + return vc.SandboxStatus{ + ID: sandbox.ID(), + State: types.SandboxState{ + State: types.StateRunning, + }, + }, nil + } + + testingImpl.StopSandboxFunc = func(ctx context.Context, sandboxID string, force bool) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.StatusSandboxFunc = nil + testingImpl.StopSandboxFunc = nil + }() + + // Force delete a running sandbox + err = delete(context.Background(), sandbox.ID(), true) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.DeleteSandboxFunc = nil + }() + + err = delete(context.Background(), sandbox.ID(), true) + assert.Nil(err) +} + +func TestDeleteRunningContainer(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: testContainerID, + MockSandbox: sandbox, + }, + } + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + path, err := createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.MockContainers[0].ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + State: types.ContainerState{ + State: "running", + }, + Spec: &ociSpec, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + // Delete on a running container should fail. + err = delete(context.Background(), sandbox.MockContainers[0].ID(), false) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + path, err = createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + // force delete + err = delete(context.Background(), sandbox.MockContainers[0].ID(), true) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + testingImpl.StopContainerFunc = testStopContainerFuncReturnNil + defer func() { + testingImpl.StopContainerFunc = nil + }() + + path, err = createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + err = delete(context.Background(), sandbox.MockContainers[0].ID(), true) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + return &vcmock.Container{}, nil + } + + defer func() { + testingImpl.DeleteContainerFunc = nil + }() + + path, err = createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + err = delete(context.Background(), sandbox.MockContainers[0].ID(), true) + assert.Nil(err) +} + +func TestDeleteContainer(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: testContainerID, + MockSandbox: sandbox, + }, + } + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + path, err := createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.MockContainers[0].ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + State: types.ContainerState{ + State: "ready", + }, + Spec: &ociSpec, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + err = delete(context.Background(), sandbox.MockContainers[0].ID(), false) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + path, err = createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StopContainerFunc = testStopContainerFuncReturnNil + defer func() { + testingImpl.StopContainerFunc = nil + }() + + err = delete(context.Background(), sandbox.MockContainers[0].ID(), false) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + return &vcmock.Container{}, nil + } + + defer func() { + testingImpl.DeleteContainerFunc = nil + }() + + path, err = createTempContainerIDMapping(sandbox.MockContainers[0].ID(), sandbox.MockContainers[0].ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + err = delete(context.Background(), sandbox.MockContainers[0].ID(), false) + assert.Nil(err) +} + +func TestDeleteCLIFunction(t *testing.T) { + assert := assert.New(t) + + flagSet := &flag.FlagSet{} + ctx := createCLIContext(flagSet) + + fn, ok := deleteCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + // no container id in the Metadata + err := fn(ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + path, err := createTempContainerIDMapping("xyz", "xyz") + assert.NoError(err) + defer os.RemoveAll(path) + + flagSet = flag.NewFlagSet("container-id", flag.ContinueOnError) + flagSet.Parse([]string{"xyz"}) + ctx = createCLIContext(flagSet) + + err = fn(ctx) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) +} + +func TestDeleteCLIFunctionSuccess(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: testContainerID, + MockSandbox: sandbox, + }, + } + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + }, + State: types.ContainerState{ + State: "ready", + }, + Spec: &ociSpec, + }, nil + } + + testingImpl.StatusSandboxFunc = func(ctx context.Context, sandboxID string) (vc.SandboxStatus, error) { + return vc.SandboxStatus{ + ID: sandbox.ID(), + State: types.SandboxState{ + State: types.StateReady, + }, + }, nil + } + + testingImpl.StopSandboxFunc = func(ctx context.Context, sandboxID string, force bool) (vc.VCSandbox, error) { + return sandbox, nil + } + + testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + testingImpl.StopSandboxFunc = nil + testingImpl.DeleteSandboxFunc = nil + }() + + flagSet := &flag.FlagSet{} + ctx := createCLIContext(flagSet) + + fn, ok := deleteCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + flagSet = flag.NewFlagSet("container-id", flag.ContinueOnError) + flagSet.Parse([]string{sandbox.ID()}) + ctx = createCLIContext(flagSet) + assert.NotNil(ctx) + + err = fn(ctx) + assert.NoError(err) +} + +func TestRemoveCGroupsPath(t *testing.T) { + if tc.NotValid(ktu.NeedNonRoot()) { + t.Skip(ktu.TestDisabledNeedNonRoot) + } + + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + dir := filepath.Join(tmpdir, "dir") + + err = os.Mkdir(dir, testDirMode) + assert.NoError(err) + + // make directory unreadable by non-root user + err = os.Chmod(tmpdir, 0000) + assert.NoError(err) + defer func() { + _ = os.Chmod(tmpdir, 0755) + }() + + err = removeCgroupsPath(context.Background(), "foo", []string{dir}) + assert.Error(err) +} diff --git a/src/runtime/cli/events.go b/src/runtime/cli/events.go new file mode 100644 index 000000000..9e9200a6f --- /dev/null +++ b/src/runtime/cli/events.go @@ -0,0 +1,295 @@ +// Copyright (c) 2014,2015,2016,2017 Docker, Inc. +// Copyright (c) 2018 Huawei Corporation. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "encoding/json" + "fmt" + "os" + "sync" + "time" + + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/types" + + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +type event struct { + Type string `json:"type"` + ID string `json:"id"` + Data interface{} `json:"data,omitempty"` +} + +// stats is the runc specific stats structure for stability when encoding and decoding stats. +type stats struct { + CPU cpu `json:"cpu"` + Memory memory `json:"memory"` + Pids pids `json:"pids"` + Blkio blkio `json:"blkio"` + Hugetlb map[string]hugetlb `json:"hugetlb"` + IntelRdt intelRdt `json:"intel_rdt"` +} + +type hugetlb struct { + Usage uint64 `json:"usage,omitempty"` + Max uint64 `json:"max,omitempty"` + Failcnt uint64 `json:"failcnt"` +} + +type blkioEntry struct { + Major uint64 `json:"major,omitempty"` + Minor uint64 `json:"minor,omitempty"` + Op string `json:"op,omitempty"` + Value uint64 `json:"value,omitempty"` +} + +type blkio struct { + IoServiceBytesRecursive []blkioEntry `json:"ioServiceBytesRecursive,omitempty"` + IoServicedRecursive []blkioEntry `json:"ioServicedRecursive,omitempty"` + IoQueuedRecursive []blkioEntry `json:"ioQueueRecursive,omitempty"` + IoServiceTimeRecursive []blkioEntry `json:"ioServiceTimeRecursive,omitempty"` + IoWaitTimeRecursive []blkioEntry `json:"ioWaitTimeRecursive,omitempty"` + IoMergedRecursive []blkioEntry `json:"ioMergedRecursive,omitempty"` + IoTimeRecursive []blkioEntry `json:"ioTimeRecursive,omitempty"` + SectorsRecursive []blkioEntry `json:"sectorsRecursive,omitempty"` +} + +type pids struct { + Current uint64 `json:"current,omitempty"` + Limit uint64 `json:"limit,omitempty"` +} + +type throttling struct { + Periods uint64 `json:"periods,omitempty"` + ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"` + ThrottledTime uint64 `json:"throttledTime,omitempty"` +} + +type cpuUsage struct { + // Units: nanoseconds. + Total uint64 `json:"total,omitempty"` + Percpu []uint64 `json:"percpu,omitempty"` + Kernel uint64 `json:"kernel"` + User uint64 `json:"user"` +} + +type cpu struct { + Usage cpuUsage `json:"usage,omitempty"` + Throttling throttling `json:"throttling,omitempty"` +} + +type memoryEntry struct { + Limit uint64 `json:"limit"` + Usage uint64 `json:"usage,omitempty"` + Max uint64 `json:"max,omitempty"` + Failcnt uint64 `json:"failcnt"` +} + +type memory struct { + Cache uint64 `json:"cache,omitempty"` + Usage memoryEntry `json:"usage,omitempty"` + Swap memoryEntry `json:"swap,omitempty"` + Kernel memoryEntry `json:"kernel,omitempty"` + KernelTCP memoryEntry `json:"kernelTCP,omitempty"` + Raw map[string]uint64 `json:"raw,omitempty"` +} + +type l3CacheInfo struct { + CbmMask string `json:"cbm_mask,omitempty"` + MinCbmBits uint64 `json:"min_cbm_bits,omitempty"` + NumClosids uint64 `json:"num_closids,omitempty"` +} + +type intelRdt struct { + // The read-only L3 cache information + L3CacheInfo *l3CacheInfo `json:"l3_cache_info,omitempty"` + + // The read-only L3 cache schema in root + L3CacheSchemaRoot string `json:"l3_cache_schema_root,omitempty"` + + // The L3 cache schema in 'container_id' group + L3CacheSchema string `json:"l3_cache_schema,omitempty"` +} + +var eventsCLICommand = cli.Command{ + Name: "events", + Usage: "display container events such as OOM notifications, cpu, memory, and IO usage statistics", + ArgsUsage: ` + +Where "" 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.`, + Flags: []cli.Flag{ + cli.DurationFlag{ + Name: "interval", + Value: 5 * time.Second, + Usage: "set the stats collection interval", + }, + cli.BoolFlag{ + Name: "stats", + Usage: "display the container's stats then exit", + }, + }, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + span, _ := katautils.Trace(ctx, "events") + defer span.Finish() + + containerID := context.Args().First() + if containerID == "" { + return fmt.Errorf("container id cannot be empty") + } + + kataLog = kataLog.WithField("container", containerID) + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + + duration := context.Duration("interval") + if duration <= 0 { + return fmt.Errorf("duration interval must be greater than 0") + } + + status, sandboxID, err := getExistingContainerInfo(ctx, containerID) + if err != nil { + return err + } + + containerID = status.ID + + kataLog = kataLog.WithFields(logrus.Fields{ + "container": containerID, + "sandbox": sandboxID, + }) + + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + span.SetTag("sandbox", sandboxID) + + if status.State.State == types.StateStopped { + return fmt.Errorf("container with id %s is not running", status.ID) + } + + var ( + events = make(chan *event, 1024) + group = &sync.WaitGroup{} + ) + group.Add(1) + + go func() { + defer group.Done() + enc := json.NewEncoder(os.Stdout) + for e := range events { + if err := enc.Encode(e); err != nil { + logrus.Error(err) + } + } + }() + + if context.Bool("stats") { + s, err := vci.StatsContainer(ctx, sandboxID, containerID) + if err != nil { + return err + } + events <- &event{Type: "stats", ID: status.ID, Data: convertVirtcontainerStats(&s)} + close(events) + group.Wait() + return nil + } + + go func() { + for range time.Tick(context.Duration("interval")) { + s, err := vci.StatsContainer(ctx, sandboxID, containerID) + if err != nil { + logrus.Error(err) + continue + } + events <- &event{Type: "stats", ID: status.ID, Data: convertVirtcontainerStats(&s)} + } + }() + + group.Wait() + return nil + }, +} + +func convertVirtcontainerStats(containerStats *vc.ContainerStats) *stats { + cg := containerStats.CgroupStats + if cg == nil { + return nil + } + var s stats + s.Pids.Current = cg.PidsStats.Current + s.Pids.Limit = cg.PidsStats.Limit + + s.CPU.Usage.Kernel = cg.CPUStats.CPUUsage.UsageInKernelmode + s.CPU.Usage.User = cg.CPUStats.CPUUsage.UsageInUsermode + s.CPU.Usage.Total = cg.CPUStats.CPUUsage.TotalUsage + s.CPU.Usage.Percpu = cg.CPUStats.CPUUsage.PercpuUsage + s.CPU.Throttling.Periods = cg.CPUStats.ThrottlingData.Periods + s.CPU.Throttling.ThrottledPeriods = cg.CPUStats.ThrottlingData.ThrottledPeriods + s.CPU.Throttling.ThrottledTime = cg.CPUStats.ThrottlingData.ThrottledTime + + s.Memory.Cache = cg.MemoryStats.Cache + s.Memory.Kernel = convertMemoryEntry(cg.MemoryStats.KernelUsage) + s.Memory.KernelTCP = convertMemoryEntry(cg.MemoryStats.KernelTCPUsage) + s.Memory.Swap = convertMemoryEntry(cg.MemoryStats.SwapUsage) + s.Memory.Usage = convertMemoryEntry(cg.MemoryStats.Usage) + s.Memory.Raw = cg.MemoryStats.Stats + + s.Blkio.IoServiceBytesRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceBytesRecursive) + s.Blkio.IoServicedRecursive = convertBlkioEntry(cg.BlkioStats.IoServicedRecursive) + s.Blkio.IoQueuedRecursive = convertBlkioEntry(cg.BlkioStats.IoQueuedRecursive) + s.Blkio.IoServiceTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceTimeRecursive) + s.Blkio.IoWaitTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoWaitTimeRecursive) + s.Blkio.IoMergedRecursive = convertBlkioEntry(cg.BlkioStats.IoMergedRecursive) + s.Blkio.IoTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoTimeRecursive) + s.Blkio.SectorsRecursive = convertBlkioEntry(cg.BlkioStats.SectorsRecursive) + + s.Hugetlb = make(map[string]hugetlb) + for k, v := range cg.HugetlbStats { + s.Hugetlb[k] = convertHugtlb(v) + } + + return &s +} + +func convertHugtlb(c vc.HugetlbStats) hugetlb { + return hugetlb{ + Usage: c.Usage, + Max: c.MaxUsage, + Failcnt: c.Failcnt, + } +} + +func convertMemoryEntry(c vc.MemoryData) memoryEntry { + return memoryEntry{ + Limit: c.Limit, + Usage: c.Usage, + Max: c.MaxUsage, + Failcnt: c.Failcnt, + } +} + +func convertBlkioEntry(c []vc.BlkioStatEntry) []blkioEntry { + var out []blkioEntry + for _, e := range c { + out = append(out, blkioEntry{ + Major: e.Major, + Minor: e.Minor, + Op: e.Op, + Value: e.Value, + }) + } + return out +} diff --git a/src/runtime/cli/events_test.go b/src/runtime/cli/events_test.go new file mode 100644 index 000000000..f54aaa0af --- /dev/null +++ b/src/runtime/cli/events_test.go @@ -0,0 +1,144 @@ +// Copyright (c) 2018 Huawei Corporation. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "os" + "testing" + "time" + + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/kata-containers/runtime/virtcontainers/types" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +func TestEventsCliAction(t *testing.T) { + assert := assert.New(t) + + // get Action function + actionFunc, ok := eventsCLICommand.Action.(func(ctx *cli.Context) error) + assert.True(ok) + + flagSet := flag.NewFlagSet("events", flag.ContinueOnError) + + // create a new fake context + ctx := createCLIContext(flagSet) + + err := actionFunc(ctx) + assert.Error(err, "Missing container ID") +} + +func TestEventsCLIFailure(t *testing.T) { + assert := assert.New(t) + + flagSet := flag.NewFlagSet("events", flag.ContinueOnError) + ctx := createCLIContext(flagSet) + + actionFunc, ok := eventsCLICommand.Action.(func(ctx *cli.Context) error) + assert.True(ok) + + // missing container ID + err := actionFunc(ctx) + assert.Error(err) + + // interval is negative + flagSet.Parse([]string{testContainerID}) + flagSet.Duration("interval", (-1)*time.Second, "") + ctx = createCLIContext(flagSet) + err = actionFunc(ctx) + assert.Error(err) + + // interval is zero + flagSet = flag.NewFlagSet("events", flag.ContinueOnError) + flagSet.Parse([]string{testContainerID}) + flagSet.Duration("interval", 0*time.Second, "") + ctx = createCLIContext(flagSet) + err = actionFunc(ctx) + assert.Error(err) + + // not running + sandbox := &vcmock.Sandbox{ + MockID: testContainerID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: sandbox.ID(), + MockSandbox: sandbox, + }, + } + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + err = actionFunc(ctx) + assert.Error(err) +} + +func TestEventsCLISuccessful(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testContainerID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: sandbox.ID(), + MockSandbox: sandbox, + }, + } + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + State: types.ContainerState{ + State: types.StateRunning, + }, + }, nil + } + + testingImpl.StatsContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStats, error) { + return vc.ContainerStats{}, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + testingImpl.StatsContainerFunc = nil + }() + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + actionFunc, ok := eventsCLICommand.Action.(func(ctx *cli.Context) error) + assert.True(ok) + + flagSet := flag.NewFlagSet("events", flag.ContinueOnError) + flagSet.Parse([]string{testContainerID}) + flagSet.Duration("interval", 5*time.Second, "") + flagSet.Bool("stats", true, "") + ctx := createCLIContext(flagSet) + err = actionFunc(ctx) + assert.NoError(err) +} diff --git a/src/runtime/cli/exec.go b/src/runtime/cli/exec.go new file mode 100644 index 000000000..8ed80065e --- /dev/null +++ b/src/runtime/cli/exec.go @@ -0,0 +1,291 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "syscall" + + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/kata-containers/runtime/virtcontainers/types" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/urfave/cli" +) + +type execParams struct { + ociProcess specs.Process + cID string + pidFile string + console string + consoleSock string + processLabel string + detach bool + noSubreaper bool +} + +var execCLICommand = cli.Command{ + Name: "exec", + Usage: "Execute new process inside the container", + ArgsUsage: ` [command options] || -p process.json + + is the name for the instance of the container and + is the command to be executed in the container. can't be empty + unless a "-p" flag provided. + +EXAMPLE: + If the container is configured to run the linux ps command the following + will output a list of processes running in the container: + + # ` + name + ` ps`, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "console", + Usage: "path to a pseudo terminal", + }, + cli.StringFlag{ + Name: "console-socket", + Value: "", + Usage: "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal", + }, + cli.StringFlag{ + Name: "cwd", + Usage: "current working directory in the container", + }, + cli.StringSliceFlag{ + Name: "env, e", + Usage: "set environment variables", + }, + cli.BoolFlag{ + Name: "tty, t", + Usage: "allocate a pseudo-TTY", + }, + cli.StringFlag{ + Name: "user, u", + Usage: "UID (format: [:])", + }, + cli.StringFlag{ + Name: "process, p", + Usage: "path to the process.json", + }, + cli.BoolFlag{ + Name: "detach,d", + Usage: "detach from the container's process", + }, + cli.StringFlag{ + Name: "pid-file", + Value: "", + Usage: "specify the file to write the process id to", + }, + cli.StringFlag{ + Name: "process-label", + Usage: "set the asm process label for the process commonly used with selinux", + }, + cli.StringFlag{ + Name: "apparmor", + Usage: "set the apparmor profile for the process", + }, + cli.BoolFlag{ + Name: "no-new-privs", + Usage: "set the no new privileges value for the process", + }, + cli.StringSliceFlag{ + Name: "cap, c", + Value: &cli.StringSlice{}, + Usage: "add a capability to the bounding set for the process", + }, + cli.BoolFlag{ + Name: "no-subreaper", + Usage: "disable the use of the subreaper used to reap reparented processes", + Hidden: true, + }, + }, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + return execute(ctx, context) + }, +} + +func generateExecParams(context *cli.Context, specProcess *specs.Process) (execParams, error) { + ctxArgs := context.Args() + + params := execParams{ + cID: ctxArgs.First(), + pidFile: context.String("pid-file"), + console: context.String("console"), + consoleSock: context.String("console-socket"), + detach: context.Bool("detach"), + processLabel: context.String("process-label"), + noSubreaper: context.Bool("no-subreaper"), + } + + if context.String("process") != "" { + var ociProcess specs.Process + + fileContent, err := ioutil.ReadFile(context.String("process")) + if err != nil { + return execParams{}, err + } + + if err := json.Unmarshal(fileContent, &ociProcess); err != nil { + return execParams{}, err + } + + params.ociProcess = ociProcess + } else { + params.ociProcess = *specProcess + + // Override terminal + if context.IsSet("tty") { + params.ociProcess.Terminal = context.Bool("tty") + } + + // Override user + if context.String("user") != "" { + params.ociProcess.User = specs.User{ + // This field is a Windows-only field + // according to the specification. However, it + // is abused here to allow the username + // specified in the OCI runtime configuration + // file to be overridden by a CLI request. + Username: context.String("user"), + } + } + + // Override env + params.ociProcess.Env = append(params.ociProcess.Env, context.StringSlice("env")...) + + // Override cwd + if context.String("cwd") != "" { + params.ociProcess.Cwd = context.String("cwd") + } + + // Override no-new-privs + if context.IsSet("no-new-privs") { + params.ociProcess.NoNewPrivileges = context.Bool("no-new-privs") + } + + // Override apparmor + if context.String("apparmor") != "" { + params.ociProcess.ApparmorProfile = context.String("apparmor") + } + + params.ociProcess.Args = ctxArgs.Tail() + } + + return params, nil +} + +func execute(ctx context.Context, context *cli.Context) error { + span, ctx := katautils.Trace(ctx, "execute") + defer span.Finish() + + containerID := context.Args().First() + + kataLog = kataLog.WithField("container", containerID) + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + + status, sandboxID, err := getExistingContainerInfo(ctx, containerID) + if err != nil { + return err + } + + kataLog = kataLog.WithField("sandbox", sandboxID) + setExternalLoggers(ctx, kataLog) + span.SetTag("sandbox", sandboxID) + + // Retrieve OCI spec configuration. + ociSpec, err := oci.GetOCIConfig(status) + if err != nil { + return err + } + + params, err := generateExecParams(context, ociSpec.Process) + if err != nil { + return err + } + + params.cID = status.ID + containerID = params.cID + + kataLog = kataLog.WithField("container", containerID) + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + + // container MUST be ready or running. + if status.State.State != types.StateReady && + status.State.State != types.StateRunning { + return fmt.Errorf("Container %s is not ready or running", + params.cID) + } + + envVars, err := oci.EnvVars(params.ociProcess.Env) + if err != nil { + return err + } + + consolePath, err := setupConsole(params.console, params.consoleSock) + if err != nil { + return err + } + + user := fmt.Sprintf("%d:%d", params.ociProcess.User.UID, params.ociProcess.User.GID) + + if params.ociProcess.User.Username != "" { + user = params.ociProcess.User.Username + } + + cmd := types.Cmd{ + Args: params.ociProcess.Args, + Envs: envVars, + WorkDir: params.ociProcess.Cwd, + User: user, + Interactive: params.ociProcess.Terminal, + Console: consolePath, + Detach: noNeedForOutput(params.detach, params.ociProcess.Terminal), + } + + _, _, process, err := vci.EnterContainer(ctx, sandboxID, params.cID, cmd) + if err != nil { + return err + } + + // Creation of PID file has to be the last thing done in the exec + // because containerd considers the exec to have finished starting + // after this file is created. + if err := createPIDFile(ctx, params.pidFile, process.Pid); err != nil { + return err + } + + if params.detach { + return nil + } + + p, err := os.FindProcess(process.Pid) + if err != nil { + return err + } + + ps, err := p.Wait() + if err != nil { + return fmt.Errorf("Process state %s, container info %+v: %v", + ps.String(), status, err) + } + + // Exit code has to be forwarded in this case. + return cli.NewExitError("", ps.Sys().(syscall.WaitStatus).ExitStatus()) +} diff --git a/src/runtime/cli/exec_test.go b/src/runtime/cli/exec_test.go new file mode 100644 index 000000000..37da2faa8 --- /dev/null +++ b/src/runtime/cli/exec_test.go @@ -0,0 +1,787 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "testing" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" + + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/kata-containers/runtime/virtcontainers/types" +) + +func TestExecCLIFunction(t *testing.T) { + assert := assert.New(t) + + flagSet := &flag.FlagSet{} + ctx := createCLIContext(flagSet) + + fn, ok := startCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + // no container-id in the Metadata + err := fn(ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + path, err := createTempContainerIDMapping("xyz", "xyz") + assert.NoError(err) + defer os.RemoveAll(path) + + // pass container-id + flagSet = flag.NewFlagSet("container-id", flag.ContinueOnError) + flagSet.Parse([]string{"xyz"}) + ctx = createCLIContext(flagSet) + + err = fn(ctx) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) +} + +func TestExecuteErrors(t *testing.T) { + assert := assert.New(t) + + flagSet := flag.NewFlagSet("", 0) + ctx := createCLIContext(flagSet) + + // missing container id + err := execute(context.Background(), ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + // StatusSandbox error + flagSet.Parse([]string{testContainerID}) + err = execute(context.Background(), ctx) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + // Config path missing in annotations + annotations := map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + } + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, types.ContainerState{}, annotations, &specs.Spec{Process: &specs.Process{}}), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + err = execute(context.Background(), ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + // Container state undefined + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + annotations = map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + } + + containerState := types.ContainerState{} + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, containerState, annotations, &ociSpec), nil + } + + err = execute(context.Background(), ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + // Container paused + containerState = types.ContainerState{ + State: types.StatePaused, + } + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, containerState, annotations, &ociSpec), nil + } + + err = execute(context.Background(), ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + // Container stopped + containerState = types.ContainerState{ + State: types.StateStopped, + } + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, containerState, annotations, &ociSpec), nil + } + + err = execute(context.Background(), ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestExecuteErrorReadingProcessJson(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + // non-existent path + processPath := filepath.Join(tmpdir, "process.json") + + flagSet := flag.NewFlagSet("", 0) + flagSet.String("process", processPath, "") + flagSet.Parse([]string{testContainerID}) + ctx := createCLIContext(flagSet) + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + annotations := map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + } + + state := types.ContainerState{ + State: types.StateRunning, + } + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + // Note: flags can only be tested with the CLI command function + fn, ok := execCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestExecuteErrorOpeningConsole(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + consoleSock := filepath.Join(tmpdir, "console-sock") + + flagSet := flag.NewFlagSet("", 0) + flagSet.String("console-socket", consoleSock, "") + flagSet.Parse([]string{testContainerID}) + ctx := createCLIContext(flagSet) + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + annotations := map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + } + + state := types.ContainerState{ + State: types.StateRunning, + } + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + // Note: flags can only be tested with the CLI command function + fn, ok := execCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func testExecParamsSetup(t *testing.T, pidFilePath, consolePath string, detach bool) *flag.FlagSet { + flagSet := flag.NewFlagSet("", 0) + + flagSet.String("pid-file", pidFilePath, "") + flagSet.String("console", consolePath, "") + flagSet.String("console-socket", "", "") + flagSet.Bool("detach", detach, "") + flagSet.String("process-label", "testlabel", "") + flagSet.Bool("no-subreaper", false, "") + + return flagSet +} + +func TestExecuteWithFlags(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + pidFilePath := filepath.Join(tmpdir, "pid") + consolePath := "/dev/ptmx" + + flagSet := testExecParamsSetup(t, pidFilePath, consolePath, false) + flagSet.String("user", "root", "") + flagSet.String("cwd", "/home/root", "") + flagSet.String("apparmor", "/tmp/profile", "") + flagSet.Bool("no-new-privs", false, "") + + flagSet.Parse([]string{testContainerID, "/tmp/foo"}) + ctx := createCLIContext(flagSet) + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + annotations := map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + } + + state := types.ContainerState{ + State: types.StateRunning, + } + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + fn, ok := execCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + // EnterContainer error + err = fn(ctx) + assert.Error(err) + + assert.True(vcmock.IsMockError(err)) + + testingImpl.EnterContainerFunc = func(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (vc.VCSandbox, vc.VCContainer, *vc.Process, error) { + return &vcmock.Sandbox{}, &vcmock.Container{}, &vc.Process{}, nil + } + + defer func() { + testingImpl.EnterContainerFunc = nil + os.Remove(pidFilePath) + }() + + // Process not running error + err = fn(ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + os.Remove(pidFilePath) + + // Process ran and exited successfully + testingImpl.EnterContainerFunc = func(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (vc.VCSandbox, vc.VCContainer, *vc.Process, error) { + // create a fake container process + workload := []string{"cat", "/dev/null"} + command := exec.Command(workload[0], workload[1:]...) + err := command.Start() + assert.NoError(err, "Unable to start process %v: %s", workload, err) + + vcProcess := vc.Process{} + vcProcess.Pid = command.Process.Pid + return &vcmock.Sandbox{}, &vcmock.Container{}, &vcProcess, nil + } + + defer func() { + testingImpl.EnterContainerFunc = nil + os.Remove(pidFilePath) + }() + + // Should get an exit code when run in non-detached mode. + err = fn(ctx) + _, ok = err.(*cli.ExitError) + assert.True(ok, true, "Exit code not received for fake workload process") +} + +func TestExecuteWithFlagsDetached(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + pidFilePath := filepath.Join(tmpdir, "pid") + consolePath := "/dev/ptmx" + detach := true + + flagSet := testExecParamsSetup(t, pidFilePath, consolePath, detach) + flagSet.Parse([]string{testContainerID, "/tmp/foo"}) + ctx := createCLIContext(flagSet) + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + annotations := map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + } + + state := types.ContainerState{ + State: types.StateRunning, + } + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + testingImpl.EnterContainerFunc = func(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (vc.VCSandbox, vc.VCContainer, *vc.Process, error) { + // create a fake container process + workload := []string{"cat", "/dev/null"} + command := exec.Command(workload[0], workload[1:]...) + err := command.Start() + assert.NoError(err, "Unable to start process %v: %s", workload, err) + + vcProcess := vc.Process{} + vcProcess.Pid = command.Process.Pid + return &vcmock.Sandbox{}, &vcmock.Container{}, &vcProcess, nil + } + + defer func() { + testingImpl.EnterContainerFunc = nil + os.Remove(pidFilePath) + }() + + fn, ok := execCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + assert.NoError(err) +} + +func TestExecuteWithInvalidProcessJson(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + pidFilePath := filepath.Join(tmpdir, "pid") + consolePath := "/dev/ptmx" + detach := false + + flagSet := testExecParamsSetup(t, pidFilePath, consolePath, detach) + + processPath := filepath.Join(tmpdir, "process.json") + flagSet.String("process", processPath, "") + + f, err := os.OpenFile(processPath, os.O_RDWR|os.O_CREATE, testFileMode) + assert.NoError(err) + + // invalidate the JSON + _, err = f.WriteString("{") + assert.NoError(err) + f.Close() + + defer os.Remove(processPath) + + flagSet.Parse([]string{testContainerID}) + ctx := createCLIContext(flagSet) + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + annotations := map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + } + + state := types.ContainerState{ + State: types.StateRunning, + } + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + fn, ok := execCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestExecuteWithValidProcessJson(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + pidFilePath := filepath.Join(tmpdir, "pid") + consolePath := "/dev/ptmx" + + flagSet := testExecParamsSetup(t, pidFilePath, consolePath, false) + + processPath := filepath.Join(tmpdir, "process.json") + flagSet.String("process", processPath, "") + + flagSet.Parse([]string{testContainerID, "/tmp/foo"}) + ctx := createCLIContext(flagSet) + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + annotations := map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + } + + state := types.ContainerState{ + State: types.StateRunning, + } + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + processJSON := `{ + "consoleSize": { + "height": 15, + "width": 15 + }, + "terminal": true, + "user": { + "uid": 0, + "gid": 0 + }, + "args": [ + "sh" + ], + "env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm" + ], + "cwd": "/" + }` + + f, err := os.OpenFile(processPath, os.O_RDWR|os.O_CREATE, testFileMode) + assert.NoError(err) + + _, err = f.WriteString(processJSON) + assert.NoError(err) + f.Close() + + defer os.Remove(processPath) + + workload := []string{"cat", "/dev/null"} + + testingImpl.EnterContainerFunc = func(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (vc.VCSandbox, vc.VCContainer, *vc.Process, error) { + // create a fake container process + command := exec.Command(workload[0], workload[1:]...) + err := command.Start() + assert.NoError(err, "Unable to start process %v: %s", workload, err) + + vcProcess := vc.Process{} + vcProcess.Pid = command.Process.Pid + + return &vcmock.Sandbox{}, &vcmock.Container{}, &vcProcess, nil + } + + defer func() { + testingImpl.EnterContainerFunc = nil + os.Remove(pidFilePath) + }() + + fn, ok := execCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + exitErr, ok := err.(*cli.ExitError) + assert.True(ok, true, "Exit code not received for fake workload process") + assert.Equal(exitErr.ExitCode(), 0, "Exit code should have been 0 for fake workload %s", workload) +} + +func TestExecuteWithEmptyEnvironmentValue(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + pidFilePath := filepath.Join(tmpdir, "pid") + consolePath := "/dev/ptmx" + + flagSet := testExecParamsSetup(t, pidFilePath, consolePath, false) + + processPath := filepath.Join(tmpdir, "process.json") + + flagSet.String("process", processPath, "") + flagSet.Parse([]string{testContainerID}) + ctx := createCLIContext(flagSet) + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + annotations := map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + } + + state := types.ContainerState{ + State: types.StateRunning, + } + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &ociSpec), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + processJSON := `{ + "consoleSize": { + "height": 15, + "width": 15 + }, + "terminal": true, + "user": { + "uid": 0, + "gid": 0 + }, + "args": [ + "sh" + ], + "env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=" + ], + "cwd": "/" + + }` + + f, err := os.OpenFile(processPath, os.O_RDWR|os.O_CREATE, testFileMode) + assert.NoError(err) + + _, err = f.WriteString(processJSON) + assert.NoError(err) + f.Close() + + defer os.Remove(processPath) + + workload := []string{"cat", "/dev/null"} + + testingImpl.EnterContainerFunc = func(ctx context.Context, sandboxID, containerID string, cmd types.Cmd) (vc.VCSandbox, vc.VCContainer, *vc.Process, error) { + // create a fake container process + command := exec.Command(workload[0], workload[1:]...) + err := command.Start() + assert.NoError(err, "Unable to start process %v: %s", workload, err) + + vcProcess := vc.Process{} + vcProcess.Pid = command.Process.Pid + + return &vcmock.Sandbox{}, &vcmock.Container{}, &vcProcess, nil + } + + defer func() { + testingImpl.EnterContainerFunc = nil + os.Remove(pidFilePath) + }() + + fn, ok := execCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + // vcAnnotations.EnvVars error due to incorrect environment + err = fn(ctx) + exitErr, ok := err.(*cli.ExitError) + assert.True(ok, true, "Exit code not received for fake workload process") + assert.Equal(exitErr.ExitCode(), 0, "Exit code should have been 0 for empty environment variable value") +} + +func TestGenerateExecParams(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + pidFilePath := filepath.Join(tmpdir, "pid") + consolePath := "/dev/ptmx" + consoleSocket := "/tmp/console-sock" + processLabel := "testlabel" + user := "root" + cwd := "cwd" + apparmor := "apparmorProf" + + flagSet := flag.NewFlagSet("", 0) + flagSet.String("pid-file", pidFilePath, "") + flagSet.String("console", consolePath, "") + flagSet.String("console-socket", consoleSocket, "") + flagSet.String("process-label", processLabel, "") + + flagSet.String("user", user, "") + flagSet.String("cwd", cwd, "") + flagSet.String("apparmor", apparmor, "") + + ctx := createCLIContext(flagSet) + process := &specs.Process{} + params, err := generateExecParams(ctx, process) + assert.NoError(err) + + assert.Equal(params.pidFile, pidFilePath) + assert.Equal(params.console, consolePath) + assert.Equal(params.consoleSock, consoleSocket) + assert.Equal(params.processLabel, processLabel) + assert.Equal(params.noSubreaper, false) + assert.Equal(params.detach, false) + + assert.Equal(params.ociProcess.Terminal, false) + assert.Equal(params.ociProcess.User.UID, uint32(0)) + assert.Equal(params.ociProcess.User.GID, uint32(0)) + assert.Equal(params.ociProcess.Cwd, cwd) + assert.Equal(params.ociProcess.ApparmorProfile, apparmor) +} + +func TestGenerateExecParamsWithProcessJsonFile(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + pidFilePath := filepath.Join(tmpdir, "pid") + consolePath := "/dev/ptmx" + consoleSocket := "/tmp/console-sock" + detach := true + processLabel := "testlabel" + + flagSet := flag.NewFlagSet("", 0) + flagSet.String("pid-file", pidFilePath, "") + flagSet.String("console", consolePath, "") + flagSet.String("console-socket", consoleSocket, "") + flagSet.Bool("detach", detach, "") + flagSet.String("process-label", processLabel, "") + + processPath := filepath.Join(tmpdir, "process.json") + flagSet.String("process", processPath, "") + + flagSet.Parse([]string{testContainerID}) + ctx := createCLIContext(flagSet) + + processJSON := `{ + "consoleSize": { + "height": 15, + "width": 15 + }, + "terminal": true, + "user": { + "uid": 0, + "gid": 0 + }, + "args": [ + "sh" + ], + "env": [ + "TERM=xterm", + "foo=bar" + ], + "cwd": "/" + }` + + f, err := os.OpenFile(processPath, os.O_RDWR|os.O_CREATE, testFileMode) + assert.NoError(err) + + _, err = f.WriteString(processJSON) + assert.NoError(err) + f.Close() + + defer os.Remove(processPath) + + process := &specs.Process{} + params, err := generateExecParams(ctx, process) + assert.NoError(err) + + assert.Equal(params.pidFile, pidFilePath) + assert.Equal(params.console, consolePath) + assert.Equal(params.consoleSock, consoleSocket) + assert.Equal(params.processLabel, processLabel) + assert.Equal(params.noSubreaper, false) + assert.Equal(params.detach, detach) + + assert.Equal(params.ociProcess.Terminal, true) + assert.Equal(params.ociProcess.ConsoleSize.Height, uint(15)) + assert.Equal(params.ociProcess.ConsoleSize.Width, uint(15)) + assert.Equal(params.ociProcess.User.UID, uint32(0)) + assert.Equal(params.ociProcess.User.GID, uint32(0)) + assert.Equal(params.ociProcess.Cwd, "/") + assert.Equal(params.ociProcess.Env[0], "TERM=xterm") + assert.Equal(params.ociProcess.Env[1], "foo=bar") +} diff --git a/src/runtime/cli/exit.go b/src/runtime/cli/exit.go new file mode 100644 index 000000000..4a95ebe0d --- /dev/null +++ b/src/runtime/cli/exit.go @@ -0,0 +1,28 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import "os" + +var atexitFuncs []func() + +var exitFunc = os.Exit + +// atexit registers a function f that will be run when exit is called. The +// handlers so registered will be called the in reverse order of their +// registration. +func atexit(f func()) { + atexitFuncs = append(atexitFuncs, f) +} + +// exit calls all atexit handlers before exiting the process with status. +func exit(status int) { + for i := len(atexitFuncs) - 1; i >= 0; i-- { + f := atexitFuncs[i] + f() + } + exitFunc(status) +} diff --git a/src/runtime/cli/exit_test.go b/src/runtime/cli/exit_test.go new file mode 100644 index 000000000..59913762b --- /dev/null +++ b/src/runtime/cli/exit_test.go @@ -0,0 +1,42 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +var testFoo string + +func testFunc() { + testFoo = "bar" +} + +func TestExit(t *testing.T) { + assert := assert.New(t) + + var testExitStatus int + exitFunc = func(status int) { + testExitStatus = status + } + + defer func() { + exitFunc = os.Exit + }() + + // test with no atexit functions added. + exit(1) + assert.Equal(testExitStatus, 1) + + // test with a function added to the atexit list. + atexit(testFunc) + exit(0) + assert.Equal(testFoo, "bar") + assert.Equal(testExitStatus, 0) +} diff --git a/src/runtime/cli/factory.go b/src/runtime/cli/factory.go new file mode 100644 index 000000000..7155c93ae --- /dev/null +++ b/src/runtime/cli/factory.go @@ -0,0 +1,335 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "fmt" + "net" + "os" + "os/signal" + "path/filepath" + "syscall" + "time" + + "github.com/gogo/protobuf/types" + pb "github.com/kata-containers/runtime/protocols/cache" + vc "github.com/kata-containers/runtime/virtcontainers" + vf "github.com/kata-containers/runtime/virtcontainers/factory" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/pkg/errors" + "github.com/urfave/cli" + "golang.org/x/sys/unix" + "google.golang.org/grpc" +) + +var factorySubCmds = []cli.Command{ + initFactoryCommand, + destroyFactoryCommand, + statusFactoryCommand, +} + +var factoryCLICommand = cli.Command{ + Name: "factory", + Usage: "manage vm factory", + Subcommands: factorySubCmds, + Action: func(context *cli.Context) { + cli.ShowSubcommandHelp(context) + }, +} + +type cacheServer struct { + rpc *grpc.Server + factory vc.Factory + done chan struct{} +} + +var jsonVMConfig *pb.GrpcVMConfig + +// Config requests base factory config and convert it to gRPC protocol. +func (s *cacheServer) Config(ctx context.Context, empty *types.Empty) (*pb.GrpcVMConfig, error) { + if jsonVMConfig == nil { + config := s.factory.Config() + + var err error + jsonVMConfig, err = config.ToGrpc() + if err != nil { + return nil, err + } + } + + return jsonVMConfig, nil +} + +// GetBaseVM requests a paused VM and convert it to gRPC protocol. +func (s *cacheServer) GetBaseVM(ctx context.Context, empty *types.Empty) (*pb.GrpcVM, error) { + config := s.factory.Config() + + vm, err := s.factory.GetBaseVM(ctx, config) + if err != nil { + return nil, errors.Wrapf(err, "failed to GetBaseVM") + } + + return vm.ToGrpc(config) +} + +func (s *cacheServer) quit() { + s.rpc.GracefulStop() + close(s.done) +} + +// Quit will stop VMCache server after 1 second. +func (s *cacheServer) Quit(ctx context.Context, empty *types.Empty) (*types.Empty, error) { + go func() { + kataLog.Info("VM cache server will stop after 1 second") + time.Sleep(time.Second) + s.quit() + }() + return &types.Empty{}, nil +} + +func (s *cacheServer) Status(ctx context.Context, empty *types.Empty) (*pb.GrpcStatus, error) { + stat := pb.GrpcStatus{ + Pid: int64(os.Getpid()), + Vmstatus: s.factory.GetVMStatus(), + } + return &stat, nil +} + +func getUnixListener(path string) (net.Listener, error) { + err := os.MkdirAll(filepath.Dir(path), 0755) + if err != nil { + return nil, err + } + _, err = os.Stat(path) + if err == nil { + return nil, fmt.Errorf("%s already exist. Please stop running VMCache server and remove %s", path, path) + } else if !os.IsNotExist(err) { + return nil, err + } + l, err := net.Listen("unix", path) + if err != nil { + return nil, err + } + if err = os.Chmod(path, 0600); err != nil { + l.Close() + return nil, err + } + return l, nil +} + +var handledSignals = []os.Signal{ + syscall.SIGTERM, + syscall.SIGINT, + syscall.SIGPIPE, +} + +func handleSignals(s *cacheServer, signals chan os.Signal) { + s.done = make(chan struct{}, 1) + go func() { + for { + sig := <-signals + kataLog.WithField("signal", sig).Debug("received signal") + switch sig { + case unix.SIGPIPE: + continue + default: + s.quit() + return + } + } + }() +} + +var initFactoryCommand = cli.Command{ + Name: "init", + Usage: "initialize a VM factory based on kata-runtime configuration", + Action: func(c *cli.Context) error { + ctx, err := cliContextToContext(c) + if err != nil { + return err + } + + runtimeConfig, ok := c.App.Metadata["runtimeConfig"].(oci.RuntimeConfig) + if !ok { + return errors.New("invalid runtime config") + } + + factoryConfig := vf.Config{ + Template: runtimeConfig.FactoryConfig.Template, + TemplatePath: runtimeConfig.FactoryConfig.TemplatePath, + Cache: runtimeConfig.FactoryConfig.VMCacheNumber, + VMCache: runtimeConfig.FactoryConfig.VMCacheNumber > 0, + VMConfig: vc.VMConfig{ + HypervisorType: runtimeConfig.HypervisorType, + HypervisorConfig: runtimeConfig.HypervisorConfig, + AgentType: runtimeConfig.AgentType, + AgentConfig: runtimeConfig.AgentConfig, + ProxyType: runtimeConfig.ProxyType, + ProxyConfig: runtimeConfig.ProxyConfig, + }, + } + + if runtimeConfig.FactoryConfig.VMCacheNumber > 0 { + f, err := vf.NewFactory(ctx, factoryConfig, false) + if err != nil { + return err + } + defer f.CloseFactory(ctx) + + s := &cacheServer{ + rpc: grpc.NewServer(), + factory: f, + } + pb.RegisterCacheServiceServer(s.rpc, s) + + l, err := getUnixListener(runtimeConfig.FactoryConfig.VMCacheEndpoint) + if err != nil { + return err + } + defer l.Close() + + signals := make(chan os.Signal, 8) + handleSignals(s, signals) + signal.Notify(signals, handledSignals...) + + kataLog.WithField("endpoint", runtimeConfig.FactoryConfig.VMCacheEndpoint).Info("VM cache server start") + s.rpc.Serve(l) + + <-s.done + + kataLog.WithField("endpoint", runtimeConfig.FactoryConfig.VMCacheEndpoint).Info("VM cache server stop") + return nil + } + + if runtimeConfig.FactoryConfig.Template { + kataLog.WithField("factory", factoryConfig).Info("create vm factory") + _, err := vf.NewFactory(ctx, factoryConfig, false) + if err != nil { + kataLog.WithError(err).Error("create vm factory failed") + return err + } + fmt.Fprintln(defaultOutputFile, "vm factory initialized") + } else { + const errstring = "vm factory or VMCache is not enabled" + kataLog.Error(errstring) + fmt.Fprintln(defaultOutputFile, errstring) + } + + return nil + }, +} + +var destroyFactoryCommand = cli.Command{ + Name: "destroy", + Usage: "destroy the VM factory", + Action: func(c *cli.Context) error { + ctx, err := cliContextToContext(c) + if err != nil { + return err + } + + runtimeConfig, ok := c.App.Metadata["runtimeConfig"].(oci.RuntimeConfig) + if !ok { + return errors.New("invalid runtime config") + } + + if runtimeConfig.FactoryConfig.VMCacheNumber > 0 { + conn, err := grpc.Dial(fmt.Sprintf("unix://%s", runtimeConfig.FactoryConfig.VMCacheEndpoint), grpc.WithInsecure()) + if err != nil { + return errors.Wrapf(err, "failed to connect %q", runtimeConfig.FactoryConfig.VMCacheEndpoint) + } + defer conn.Close() + _, err = pb.NewCacheServiceClient(conn).Quit(ctx, &types.Empty{}) + if err != nil { + return errors.Wrapf(err, "failed to call gRPC Quit") + } + // Wait VMCache server stop + time.Sleep(time.Second) + } else if runtimeConfig.FactoryConfig.Template { + factoryConfig := vf.Config{ + Template: true, + TemplatePath: runtimeConfig.FactoryConfig.TemplatePath, + VMConfig: vc.VMConfig{ + HypervisorType: runtimeConfig.HypervisorType, + HypervisorConfig: runtimeConfig.HypervisorConfig, + AgentType: runtimeConfig.AgentType, + AgentConfig: runtimeConfig.AgentConfig, + ProxyType: runtimeConfig.ProxyType, + ProxyConfig: runtimeConfig.ProxyConfig, + }, + } + kataLog.WithField("factory", factoryConfig).Info("load vm factory") + f, err := vf.NewFactory(ctx, factoryConfig, true) + if err != nil { + kataLog.WithError(err).Error("load vm factory failed") + // ignore error + } else { + f.CloseFactory(ctx) + } + } + fmt.Fprintln(defaultOutputFile, "vm factory destroyed") + return nil + }, +} + +var statusFactoryCommand = cli.Command{ + Name: "status", + Usage: "query the status of VM factory", + Action: func(c *cli.Context) error { + ctx, err := cliContextToContext(c) + if err != nil { + return err + } + + runtimeConfig, ok := c.App.Metadata["runtimeConfig"].(oci.RuntimeConfig) + if !ok { + return errors.New("invalid runtime config") + } + + if runtimeConfig.FactoryConfig.VMCacheNumber > 0 { + conn, err := grpc.Dial(fmt.Sprintf("unix://%s", runtimeConfig.FactoryConfig.VMCacheEndpoint), grpc.WithInsecure()) + if err != nil { + fmt.Fprintln(defaultOutputFile, errors.Wrapf(err, "failed to connect %q", runtimeConfig.FactoryConfig.VMCacheEndpoint)) + } else { + defer conn.Close() + status, err := pb.NewCacheServiceClient(conn).Status(ctx, &types.Empty{}) + if err != nil { + fmt.Fprintln(defaultOutputFile, errors.Wrapf(err, "failed to call gRPC Status\n")) + } else { + fmt.Fprintf(defaultOutputFile, "VM cache server pid = %d\n", status.Pid) + for _, vs := range status.Vmstatus { + fmt.Fprintf(defaultOutputFile, "VM pid = %d Cpu = %d Memory = %dMiB\n", vs.Pid, vs.Cpu, vs.Memory) + } + } + } + } + if runtimeConfig.FactoryConfig.Template { + factoryConfig := vf.Config{ + Template: true, + TemplatePath: runtimeConfig.FactoryConfig.TemplatePath, + VMConfig: vc.VMConfig{ + HypervisorType: runtimeConfig.HypervisorType, + HypervisorConfig: runtimeConfig.HypervisorConfig, + AgentType: runtimeConfig.AgentType, + AgentConfig: runtimeConfig.AgentConfig, + ProxyType: runtimeConfig.ProxyType, + ProxyConfig: runtimeConfig.ProxyConfig, + }, + } + kataLog.WithField("factory", factoryConfig).Info("load vm factory") + _, err := vf.NewFactory(ctx, factoryConfig, true) + if err != nil { + fmt.Fprintln(defaultOutputFile, "vm factory is off") + } else { + fmt.Fprintln(defaultOutputFile, "vm factory is on") + } + } else { + fmt.Fprintln(defaultOutputFile, "vm factory not enabled") + } + return nil + }, +} diff --git a/src/runtime/cli/factory_test.go b/src/runtime/cli/factory_test.go new file mode 100644 index 000000000..9e968d40f --- /dev/null +++ b/src/runtime/cli/factory_test.go @@ -0,0 +1,152 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "flag" + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + vc "github.com/kata-containers/runtime/virtcontainers" +) + +const testDisabledAsNonRoot = "Test disabled as requires root privileges" + +func TestFactoryCLIFunctionNoRuntimeConfig(t *testing.T) { + assert := assert.New(t) + + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + ctx.App.Metadata["foo"] = "bar" + + fn, ok := initFactoryCommand.Action.(func(context *cli.Context) error) + assert.True(ok) + err := fn(ctx) + // no runtime config in the Metadata + assert.Error(err) + + fn, ok = destroyFactoryCommand.Action.(func(context *cli.Context) error) + assert.True(ok) + err = fn(ctx) + // no runtime config in the Metadata + assert.Error(err) +} + +func TestFactoryCLIFunctionInit(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + set := flag.NewFlagSet("", 0) + + set.String("console-socket", "", "") + + ctx := createCLIContext(set) + ctx.App.Name = "foo" + + // No template + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + + fn, ok := initFactoryCommand.Action.(func(context *cli.Context) error) + assert.True(ok) + err = fn(ctx) + assert.Nil(err) + + // With template + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(testDisabledAsNonRoot) + } + + runtimeConfig.FactoryConfig.Template = true + runtimeConfig.FactoryConfig.TemplatePath = "/run/vc/vm/template" + runtimeConfig.HypervisorType = vc.MockHypervisor + runtimeConfig.AgentType = vc.NoopAgentType + runtimeConfig.ProxyType = vc.NoopProxyType + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + fn, ok = initFactoryCommand.Action.(func(context *cli.Context) error) + assert.True(ok) + err = fn(ctx) + assert.Nil(err) +} + +func TestFactoryCLIFunctionDestroy(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + set := flag.NewFlagSet("", 0) + + set.String("console-socket", "", "") + + ctx := createCLIContext(set) + ctx.App.Name = "foo" + + // No template + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + fn, ok := destroyFactoryCommand.Action.(func(context *cli.Context) error) + assert.True(ok) + err = fn(ctx) + assert.Nil(err) + + // With template + runtimeConfig.FactoryConfig.Template = true + runtimeConfig.HypervisorType = vc.MockHypervisor + runtimeConfig.AgentType = vc.NoopAgentType + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + fn, ok = destroyFactoryCommand.Action.(func(context *cli.Context) error) + assert.True(ok) + err = fn(ctx) + assert.Nil(err) +} + +func TestFactoryCLIFunctionStatus(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + set := flag.NewFlagSet("", 0) + + set.String("console-socket", "", "") + + ctx := createCLIContext(set) + ctx.App.Name = "foo" + + // No template + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + + fn, ok := statusFactoryCommand.Action.(func(context *cli.Context) error) + assert.True(ok) + err = fn(ctx) + assert.Nil(err) + + // With template + runtimeConfig.FactoryConfig.Template = true + runtimeConfig.HypervisorType = vc.MockHypervisor + runtimeConfig.AgentType = vc.NoopAgentType + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + err = fn(ctx) + assert.Nil(err) +} diff --git a/src/runtime/cli/kata-check.go b/src/runtime/cli/kata-check.go new file mode 100644 index 000000000..5d84a374e --- /dev/null +++ b/src/runtime/cli/kata-check.go @@ -0,0 +1,503 @@ +// Copyright (c) 2017-2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Note: To add a new architecture, implement all identifiers beginning "arch". + +package main + +/* +#include + +const int ioctl_KVM_CREATE_VM = KVM_CREATE_VM; +const int ioctl_KVM_CHECK_EXTENSION = KVM_CHECK_EXTENSION; +*/ +import "C" + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + "syscall" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +type kernelModule struct { + // description + desc string + + // maps parameter names to values + parameters map[string]string + + // if it is definitely required + required bool +} + +// nolint: structcheck, unused, deadcode +type kvmExtension struct { + // description + desc string + + // extension identifier + id int +} + +type vmContainerCapableDetails struct { + cpuInfoFile string + requiredCPUFlags map[string]string + requiredCPUAttribs map[string]string + requiredKernelModules map[string]kernelModule +} + +const ( + moduleParamDir = "parameters" + successMessageCapable = "System is capable of running " + project + successMessageCreate = "System can currently create " + project + successMessageVersion = "Version consistency of " + project + " is verified" + failMessage = "System is not capable of running " + project + kernelPropertyCorrect = "Kernel property value correct" + + // these refer to fields in the procCPUINFO file + genericCPUFlagsTag = "flags" // nolint: varcheck, unused, deadcode + genericCPUVendorField = "vendor_id" // nolint: varcheck, unused, deadcode + genericCPUModelField = "model name" // nolint: varcheck, unused, deadcode +) + +// variables rather than consts to allow tests to modify them +var ( + procCPUInfo = "/proc/cpuinfo" + sysModuleDir = "/sys/module" + modProbeCmd = "modprobe" +) + +// variables rather than consts to allow tests to modify them +var ( + kvmDevice = "/dev/kvm" +) + +// getCPUInfo returns details of the first CPU read from the specified cpuinfo file +func getCPUInfo(cpuInfoFile string) (string, error) { + text, err := katautils.GetFileContents(cpuInfoFile) + if err != nil { + return "", err + } + + cpus := strings.SplitAfter(text, "\n\n") + + trimmed := strings.TrimSpace(cpus[0]) + if trimmed == "" { + return "", fmt.Errorf("Cannot determine CPU details") + } + + return trimmed, nil +} + +// findAnchoredString searches haystack for needle and returns true if found +func findAnchoredString(haystack, needle string) bool { + if haystack == "" || needle == "" { + return false + } + + // Ensure the search string is anchored + pattern := regexp.MustCompile(`\b` + needle + `\b`) + + return pattern.MatchString(haystack) +} + +// getCPUFlags returns the CPU flags from the cpuinfo file specified +func getCPUFlags(cpuinfo string) string { + for _, line := range strings.Split(cpuinfo, "\n") { + if strings.HasPrefix(line, cpuFlagsTag) { + fields := strings.Split(line, ":") + if len(fields) == 2 { + return strings.TrimSpace(fields[1]) + } + } + } + + return "" +} + +// haveKernelModule returns true if the specified module exists +// (either loaded or available to be loaded) +func haveKernelModule(module string) bool { + // First, check to see if the module is already loaded + path := filepath.Join(sysModuleDir, module) + if katautils.FileExists(path) { + return true + } + + // Now, check if the module is unloaded, but available. + // And modprobe it if so. + cmd := exec.Command(modProbeCmd, module) + if output, err := cmd.CombinedOutput(); err != nil { + kataLog.WithField("module", module).WithError(err).Warnf("modprobe insert module failed: %s", string(output)) + return false + } + return true +} + +// checkCPU checks all required CPU attributes modules and returns a count of +// the number of CPU attribute errors (all of which are logged by this +// function). The specified tag is simply used for logging purposes. +func checkCPU(tag, cpuinfo string, attribs map[string]string) (count uint32) { + if cpuinfo == "" { + return 0 + } + + for attrib, desc := range attribs { + fields := logrus.Fields{ + "type": tag, + "name": attrib, + "description": desc, + } + + found := findAnchoredString(cpuinfo, attrib) + if !found { + kataLog.WithFields(fields).Errorf("CPU property not found") + count++ + continue + + } + + kataLog.WithFields(fields).Infof("CPU property found") + } + + return count +} + +func checkCPUFlags(cpuflags string, required map[string]string) uint32 { + return checkCPU("flag", cpuflags, required) +} + +func checkCPUAttribs(cpuinfo string, attribs map[string]string) uint32 { + return checkCPU("attribute", cpuinfo, attribs) +} + +// kernelParamHandler represents a function that allows kernel module +// parameter errors to be ignored for special scenarios. +// +// The function is passed the following parameters: +// +// onVMM - `true` if the host is running under a VMM environment +// fields - A set of fields showing the expected and actual module parameter values. +// msg - The message that would be logged showing the incorrect kernel module +// parameter. +// +// The function must return `true` if the kernel module parameter error should +// be ignored, or `false` if it is a real error. +// +// Note: it is up to the function to add an appropriate log call if the error +// should be ignored. +type kernelParamHandler func(onVMM bool, fields logrus.Fields, msg string) bool + +// checkKernelModules checks all required kernel modules modules and returns a count of +// the number of module errors (all of which are logged by this +// function). Only fatal errors result in an error return. +func checkKernelModules(modules map[string]kernelModule, handler kernelParamHandler) (count uint32, err error) { + onVMM, err := vc.RunningOnVMM(procCPUInfo) + if err != nil { + return 0, err + } + + for module, details := range modules { + fields := logrus.Fields{ + "type": "module", + "name": module, + "description": details.desc, + } + + if !haveKernelModule(module) { + kataLog.WithFields(fields).Error("kernel property not found") + if details.required { + count++ + } + continue + } + + kataLog.WithFields(fields).Infof("kernel property found") + + for param, expected := range details.parameters { + path := filepath.Join(sysModuleDir, module, moduleParamDir, param) + value, err := katautils.GetFileContents(path) + if err != nil { + return 0, err + } + + value = strings.TrimRight(value, "\n\r") + + fields["parameter"] = param + fields["value"] = value + + if value != expected { + fields["expected"] = expected + + msg := "kernel module parameter has unexpected value" + + if handler != nil { + ignoreError := handler(onVMM, fields, msg) + if ignoreError { + continue + } + } + + kataLog.WithFields(fields).Error(msg) + count++ + } + + kataLog.WithFields(fields).Info(kernelPropertyCorrect) + } + } + + return count, nil +} + +// genericHostIsVMContainerCapable checks to see if the host is theoretically capable +// of creating a VM container. +//nolint: unused,deadcode +func genericHostIsVMContainerCapable(details vmContainerCapableDetails) error { + cpuinfo, err := getCPUInfo(details.cpuInfoFile) + if err != nil { + return err + } + + cpuFlags := getCPUFlags(cpuinfo) + if cpuFlags == "" { + return fmt.Errorf("Cannot find CPU flags") + } + + // Keep a track of the error count, but don't error until all tests + // have been performed! + errorCount := uint32(0) + + count := checkCPUAttribs(cpuinfo, details.requiredCPUAttribs) + + errorCount += count + + count = checkCPUFlags(cpuFlags, details.requiredCPUFlags) + + errorCount += count + + count, err = checkKernelModules(details.requiredKernelModules, archKernelParamHandler) + if err != nil { + return err + } + + errorCount += count + + if errorCount == 0 { + return nil + } + + return fmt.Errorf("ERROR: %s", failMessage) +} + +var kataCheckCLICommand = cli.Command{ + Name: checkCmd, + Usage: "tests if system can run " + project, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "verbose, v", + Usage: "display the list of checks performed", + }, + cli.BoolFlag{ + Name: "strict, s", + Usage: "perform strict checking", + }, + }, + + Action: func(context *cli.Context) error { + verbose := context.Bool("verbose") + if verbose { + kataLog.Logger.SetLevel(logrus.InfoLevel) + } + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + span, _ := katautils.Trace(ctx, "kata-check") + defer span.Finish() + + runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig) + if !ok { + return errors.New("kata-check: cannot determine runtime config") + } + + err = setCPUtype(runtimeConfig.HypervisorType) + if err != nil { + return err + } + + details := vmContainerCapableDetails{ + cpuInfoFile: procCPUInfo, + requiredCPUFlags: archRequiredCPUFlags, + requiredCPUAttribs: archRequiredCPUAttribs, + requiredKernelModules: archRequiredKernelModules, + } + + err = hostIsVMContainerCapable(details) + + if err != nil { + return err + } + fmt.Println(successMessageCapable) + + if os.Geteuid() == 0 { + err = archHostCanCreateVMContainer(runtimeConfig.HypervisorType) + if err != nil { + return err + } + + fmt.Println(successMessageCreate) + } + + strict := context.Bool("strict") + if strict { + err = checkVersionConsistencyInComponents(runtimeConfig) + if err != nil { + return err + } + + fmt.Println(successMessageVersion) + } + + return nil + }, +} + +func genericArchKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool { + param, ok := fields["parameter"].(string) + if !ok { + return false + } + + // This option is not required when + // already running under a hypervisor. + if param == "unrestricted_guest" && onVMM { + kataLog.WithFields(fields).Warn(kernelPropertyCorrect) + return true + } + + if param == "nested" { + kataLog.WithFields(fields).Warn(msg) + return true + } + + // don't ignore the error + return false +} + +// genericKvmIsUsable determines if it will be possible to create a full virtual machine +// by creating a minimal VM and then deleting it. +func genericKvmIsUsable() error { + flags := syscall.O_RDWR | syscall.O_CLOEXEC + + f, err := syscall.Open(kvmDevice, flags, 0) + if err != nil { + return err + } + defer syscall.Close(f) + + fieldLogger := kataLog.WithField("check-type", "full") + + fieldLogger.WithField("device", kvmDevice).Info("device available") + + vm, _, errno := syscall.Syscall(syscall.SYS_IOCTL, + uintptr(f), + uintptr(C.ioctl_KVM_CREATE_VM), + 0) + if errno != 0 { + if errno == syscall.EBUSY { + fieldLogger.WithField("reason", "another hypervisor running").Error("cannot create VM") + } + + return errno + } + defer syscall.Close(int(vm)) + + fieldLogger.WithField("feature", "create-vm").Info("feature available") + + return nil +} + +// genericCheckKVMExtension allows to query about the specific kvm extensions +// nolint: unused, deadcode +func genericCheckKVMExtensions(extensions map[string]kvmExtension) (map[string]int, error) { + results := make(map[string]int) + + flags := syscall.O_RDWR | syscall.O_CLOEXEC + kvm, err := syscall.Open(kvmDevice, flags, 0) + if err != nil { + return results, err + } + defer syscall.Close(kvm) + + for name, extension := range extensions { + fields := logrus.Fields{ + "type": "kvm extension", + "name": name, + "description": extension.desc, + "id": extension.id, + } + + ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL, + uintptr(kvm), + uintptr(C.ioctl_KVM_CHECK_EXTENSION), + uintptr(extension.id)) + + // Generally return value(ret) 0 means no and 1 means yes, + // but some extensions may report additional information in the integer return value. + if errno != 0 { + kataLog.WithFields(fields).Error("is not supported") + return results, errno + } + + results[name] = int(ret) + kataLog.WithFields(fields).Info("kvm extension is supported") + } + + return results, nil +} + +// checkVersionConsistencyInComponents checks version consistency in Kata Components. +func checkVersionConsistencyInComponents(config oci.RuntimeConfig) error { + proxyInfo := getProxyInfo(config) + + shimInfo, err := getShimInfo(config) + if err != nil { + return err + } + shimVersionInfo := shimInfo.Version + + runtimeVersionInfo := constructVersionInfo(version) + + // kata-proxy exists + if proxyInfo.Type != string(vc.NoProxyType) { + proxyVersionInfo := proxyInfo.Version + if !versionEqual(proxyVersionInfo, runtimeVersionInfo) || !versionEqual(shimVersionInfo, runtimeVersionInfo) { + return fmt.Errorf("there exists version inconsistency in kata components. kata-proxy: v%d.%d.%d, kata-shim: v%d.%d.%d, kata-runtime: v%d.%d.%d", + proxyVersionInfo.Major, proxyVersionInfo.Minor, proxyVersionInfo.Patch, + shimVersionInfo.Major, shimVersionInfo.Minor, shimVersionInfo.Patch, + runtimeVersionInfo.Major, runtimeVersionInfo.Minor, runtimeVersionInfo.Patch) + } + } else { + if !versionEqual(shimVersionInfo, runtimeVersionInfo) { + return fmt.Errorf("there exists version inconsistency in kata components. kata-shim: v%d.%d.%d, kata-runtime: v%d.%d.%d", + shimVersionInfo.Major, shimVersionInfo.Minor, shimVersionInfo.Patch, + runtimeVersionInfo.Major, runtimeVersionInfo.Minor, runtimeVersionInfo.Patch) + } + } + + return nil +} diff --git a/src/runtime/cli/kata-check_amd64.go b/src/runtime/cli/kata-check_amd64.go new file mode 100644 index 000000000..8376293a8 --- /dev/null +++ b/src/runtime/cli/kata-check_amd64.go @@ -0,0 +1,312 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "io/ioutil" + "strings" + "syscall" + "unsafe" + + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/sirupsen/logrus" +) + +const ( + cpuFlagsTag = genericCPUFlagsTag + archCPUVendorField = genericCPUVendorField + archCPUModelField = genericCPUModelField + archGenuineIntel = "GenuineIntel" + archAuthenticAMD = "AuthenticAMD" + msgKernelVM = "Kernel-based Virtual Machine" + msgKernelVirtio = "Host kernel accelerator for virtio" + msgKernelVirtioNet = "Host kernel accelerator for virtio network" + msgKernelVirtioVhostVsock = "Host Support for Linux VM Sockets" + cpuFlagVMX = "vmx" + cpuFlagLM = "lm" + cpuFlagSVM = "svm" + cpuFlagSSE4_1 = "sse4_1" + kernelModvhm = "vhm_dev" + kernelModvhost = "vhost" + kernelModvhostnet = "vhost_net" + kernelModvhostvsock = "vhost_vsock" + kernelModkvm = "kvm" + kernelModkvmintel = "kvm_intel" + kernelModkvmamd = "kvm_amd" +) + +// CPU types +const ( + cpuTypeIntel = 0 + cpuTypeAMD = 1 + cpuTypeUnknown = -1 +) + +const acrnDevice = "/dev/acrn_vhm" + +// ioctl_ACRN_CREATE_VM is the IOCTL to create VM in ACRN. +// Current Linux mainstream kernel doesn't have support for ACRN. +// Due to this several macros are not defined in Linux headers. +// Until the support is available, directly use the value instead +// of macros. +//https://github.com/kata-containers/runtime/issues/1784 +const ioctl_ACRN_CREATE_VM = 0x43000010 //nolint +const ioctl_ACRN_DESTROY_VM = 0x43000011 //nolint + +type acrn_create_vm struct { //nolint + vmid uint16 //nolint + reserved0 uint16 //nolint + vcpu_num uint16 //nolint + reserved1 uint16 //nolint + uuid [16]uint8 + vm_flag uint64 //nolint + req_buf uint64 //nolint + reserved2 [16]uint8 //nolint +} + +// cpuType save the CPU type +var cpuType int + +// archRequiredCPUFlags maps a CPU flag value to search for and a +// human-readable description of that value. +var archRequiredCPUFlags map[string]string + +// archRequiredCPUAttribs maps a CPU (non-CPU flag) attribute value to search for +// and a human-readable description of that value. +var archRequiredCPUAttribs map[string]string + +// archRequiredKernelModules maps a required module name to a human-readable +// description of the modules functionality and an optional list of +// required module parameters. +var archRequiredKernelModules map[string]kernelModule + +func setCPUtype(hypervisorType vc.HypervisorType) error { + cpuType = getCPUtype() + + if cpuType == cpuTypeUnknown { + return fmt.Errorf("Unknow CPU Type") + } else if cpuType == cpuTypeIntel { + var kvmIntelParams map[string]string + onVMM, err := vc.RunningOnVMM(procCPUInfo) + if err != nil && !onVMM { + kvmIntelParams = map[string]string{ + // "VMX Unrestricted mode support". This is used + // as a heuristic to determine if the system is + // "new enough" to run a Kata Container + // (atleast a Westmere). + "unrestricted_guest": "Y", + } + } + + switch hypervisorType { + case "firecracker": + fallthrough + case "clh": + fallthrough + case "qemu": + archRequiredCPUFlags = map[string]string{ + cpuFlagVMX: "Virtualization support", + cpuFlagLM: "64Bit CPU", + cpuFlagSSE4_1: "SSE4.1", + } + archRequiredCPUAttribs = map[string]string{ + archGenuineIntel: "Intel Architecture CPU", + } + archRequiredKernelModules = map[string]kernelModule{ + kernelModkvm: { + desc: msgKernelVM, + required: true, + }, + kernelModkvmintel: { + desc: "Intel KVM", + parameters: kvmIntelParams, + required: true, + }, + kernelModvhost: { + desc: msgKernelVirtio, + required: true, + }, + kernelModvhostnet: { + desc: msgKernelVirtioNet, + required: true, + }, + kernelModvhostvsock: { + desc: msgKernelVirtioVhostVsock, + required: false, + }, + } + case "acrn": + archRequiredCPUFlags = map[string]string{ + cpuFlagLM: "64Bit CPU", + cpuFlagSSE4_1: "SSE4.1", + } + archRequiredCPUAttribs = map[string]string{ + archGenuineIntel: "Intel Architecture CPU", + } + archRequiredKernelModules = map[string]kernelModule{ + kernelModvhm: { + desc: "Intel ACRN", + required: false, + }, + kernelModvhost: { + desc: msgKernelVirtio, + required: false, + }, + kernelModvhostnet: { + desc: msgKernelVirtioNet, + required: false, + }, + } + default: + return fmt.Errorf("setCPUtype: Unknown hypervisor type %s", hypervisorType) + } + + } else if cpuType == cpuTypeAMD { + archRequiredCPUFlags = map[string]string{ + cpuFlagSVM: "Virtualization support", + cpuFlagLM: "64Bit CPU", + cpuFlagSSE4_1: "SSE4.1", + } + archRequiredCPUAttribs = map[string]string{ + archAuthenticAMD: "AMD Architecture CPU", + } + archRequiredKernelModules = map[string]kernelModule{ + kernelModkvm: { + desc: msgKernelVM, + required: true, + }, + kernelModkvmamd: { + desc: "AMD KVM", + required: true, + }, + kernelModvhost: { + desc: msgKernelVirtio, + required: true, + }, + kernelModvhostnet: { + desc: msgKernelVirtioNet, + required: true, + }, + kernelModvhostvsock: { + desc: msgKernelVirtioVhostVsock, + required: false, + }, + } + } + + return nil +} + +func getCPUtype() int { + content, err := ioutil.ReadFile("/proc/cpuinfo") + if err != nil { + kataLog.WithError(err).Error("failed to read file") + return cpuTypeUnknown + } + str := string(content) + if strings.Contains(str, archGenuineIntel) { + return cpuTypeIntel + } else if strings.Contains(str, archAuthenticAMD) { + return cpuTypeAMD + } else { + return cpuTypeUnknown + } +} + +// kvmIsUsable determines if it will be possible to create a full virtual machine +// by creating a minimal VM and then deleting it. +func kvmIsUsable() error { + return genericKvmIsUsable() +} + +// acrnIsUsable determines if it will be possible to create a full virtual machine +// by creating a minimal VM and then deleting it. +func acrnIsUsable() error { + flags := syscall.O_RDWR | syscall.O_CLOEXEC + + f, err := syscall.Open(acrnDevice, flags, 0) + if err != nil { + return err + } + defer syscall.Close(f) + kataLog.WithField("device", acrnDevice).Info("device available") + + acrnInst := vc.Acrn{} + uuidStr, err := acrnInst.GetNextAvailableUUID() + if err != nil { + return err + } + + uuid, err := acrnInst.GetACRNUUIDBytes(uuidStr) + if err != nil { + return fmt.Errorf("Converting UUID str to bytes failed, Err:%s", err) + } + + var createVM acrn_create_vm + createVM.uuid = uuid + + ret, _, errno := syscall.Syscall(syscall.SYS_IOCTL, + uintptr(f), + uintptr(ioctl_ACRN_CREATE_VM), + uintptr(unsafe.Pointer(&createVM))) + if ret != 0 || errno != 0 { + if errno == syscall.EBUSY { + kataLog.WithField("reason", "another hypervisor running").Error("cannot create VM") + } + kataLog.WithFields(logrus.Fields{ + "ret": ret, + "errno": errno, + }).Info("Create VM Error") + return errno + } + + ret, _, errno = syscall.Syscall(syscall.SYS_IOCTL, + uintptr(f), + uintptr(ioctl_ACRN_DESTROY_VM), + 0) + if ret != 0 || errno != 0 { + kataLog.WithFields(logrus.Fields{ + "ret": ret, + "errno": errno, + }).Info("Destroy VM Error") + return errno + } + + kataLog.WithField("feature", "create-vm").Info("feature available") + + return nil +} + +func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error { + + switch hypervisorType { + case "qemu": + fallthrough + case "clh": + fallthrough + case "firecracker": + return kvmIsUsable() + case "acrn": + return acrnIsUsable() + default: + return fmt.Errorf("archHostCanCreateVMContainer: Unknown hypervisor type %s", hypervisorType) + } +} + +// hostIsVMContainerCapable checks to see if the host is theoretically capable +// of creating a VM container. +func hostIsVMContainerCapable(details vmContainerCapableDetails) error { + return genericHostIsVMContainerCapable(details) +} + +func archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool { + return genericArchKernelParamHandler(onVMM, fields, msg) +} + +func getCPUDetails() (vendor, model string, err error) { + return genericGetCPUDetails() +} diff --git a/src/runtime/cli/kata-check_amd64_test.go b/src/runtime/cli/kata-check_amd64_test.go new file mode 100644 index 000000000..780c32669 --- /dev/null +++ b/src/runtime/cli/kata-check_amd64_test.go @@ -0,0 +1,485 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "testing" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func setupCheckHostIsVMContainerCapable(assert *assert.Assertions, cpuInfoFile string, cpuData []testCPUData, moduleData []testModuleData) { + createModules(assert, cpuInfoFile, moduleData) + + // all the modules files have now been created, so deal with the + // cpuinfo data. + for _, d := range cpuData { + err := makeCPUInfoFile(cpuInfoFile, d.vendorID, d.flags) + assert.NoError(err) + + details := vmContainerCapableDetails{ + cpuInfoFile: cpuInfoFile, + requiredCPUFlags: archRequiredCPUFlags, + requiredCPUAttribs: archRequiredCPUAttribs, + requiredKernelModules: archRequiredKernelModules, + } + + err = hostIsVMContainerCapable(details) + if d.expectError { + assert.Error(err) + } else { + assert.NoError(err) + } + } +} + +func TestCCCheckCLIFunction(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(testDisabledAsNonRoot) + } + + var cpuData []testCPUData + var moduleData []testModuleData + + if cpuType == cpuTypeIntel { + cpuData = []testCPUData{ + {archGenuineIntel, "lm vmx sse4_1", false}, + } + + moduleData = []testModuleData{} + } else if cpuType == cpuTypeAMD { + cpuData = []testCPUData{ + {archAuthenticAMD, "lm svm sse4_1", false}, + } + + moduleData = []testModuleData{} + } + + genericCheckCLIFunction(t, cpuData, moduleData) +} + +func TestCheckCheckKernelModulesNoNesting(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedSysModuleDir := sysModuleDir + savedProcCPUInfo := procCPUInfo + + cpuInfoFile := filepath.Join(dir, "cpuinfo") + + // XXX: override + sysModuleDir = filepath.Join(dir, "sys/module") + procCPUInfo = cpuInfoFile + + defer func() { + sysModuleDir = savedSysModuleDir + procCPUInfo = savedProcCPUInfo + }() + + err = os.MkdirAll(sysModuleDir, testDirMode) + if err != nil { + t.Fatal(err) + } + + requiredModules := map[string]kernelModule{ + "kvm_intel": { + desc: "Intel KVM", + parameters: map[string]string{ + "nested": "Y", + "unrestricted_guest": "Y", + }, + required: true, + }, + } + + actualModuleData := []testModuleData{ + {filepath.Join(sysModuleDir, "kvm"), true, ""}, + {filepath.Join(sysModuleDir, "kvm_intel"), true, ""}, + {filepath.Join(sysModuleDir, "kvm_intel/parameters/unrestricted_guest"), false, "Y"}, + + // XXX: force a warning + {filepath.Join(sysModuleDir, "kvm_intel/parameters/nested"), false, "N"}, + } + + vendor := archGenuineIntel + flags := "vmx lm sse4_1 hypervisor" + + _, err = checkKernelModules(requiredModules, archKernelParamHandler) + // no cpuInfoFile yet + assert.Error(err) + + createModules(assert, cpuInfoFile, actualModuleData) + + err = makeCPUInfoFile(cpuInfoFile, vendor, flags) + assert.NoError(err) + + count, err := checkKernelModules(requiredModules, archKernelParamHandler) + assert.NoError(err) + assert.Equal(count, uint32(0)) + + // create buffer to save logger output + buf := &bytes.Buffer{} + + savedLogOutput := kataLog.Logger.Out + + defer func() { + kataLog.Logger.Out = savedLogOutput + }() + + kataLog.Logger.Out = buf + + count, err = checkKernelModules(requiredModules, archKernelParamHandler) + + assert.NoError(err) + assert.Equal(count, uint32(0)) + + re := regexp.MustCompile(`.*\bnested\b`) + matches := re.FindAllStringSubmatch(buf.String(), -1) + assert.NotEmpty(matches) +} + +func TestCheckCheckKernelModulesNoUnrestrictedGuest(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedSysModuleDir := sysModuleDir + savedProcCPUInfo := procCPUInfo + + cpuInfoFile := filepath.Join(dir, "cpuinfo") + + // XXX: override + sysModuleDir = filepath.Join(dir, "sys/module") + procCPUInfo = cpuInfoFile + + defer func() { + sysModuleDir = savedSysModuleDir + procCPUInfo = savedProcCPUInfo + }() + + err = os.MkdirAll(sysModuleDir, testDirMode) + if err != nil { + t.Fatal(err) + } + + requiredModules := map[string]kernelModule{ + "kvm_intel": { + desc: "Intel KVM", + parameters: map[string]string{ + "nested": "Y", + "unrestricted_guest": "Y", + }, + required: true, + }, + } + + actualModuleData := []testModuleData{ + {filepath.Join(sysModuleDir, "kvm"), true, ""}, + {filepath.Join(sysModuleDir, "kvm_intel"), true, ""}, + {filepath.Join(sysModuleDir, "kvm_intel/parameters/nested"), false, "Y"}, + + // XXX: force a failure on non-VMM systems + {filepath.Join(sysModuleDir, "kvm_intel/parameters/unrestricted_guest"), false, "N"}, + } + + vendor := archGenuineIntel + flags := "vmx lm sse4_1" + + _, err = checkKernelModules(requiredModules, archKernelParamHandler) + // no cpuInfoFile yet + assert.Error(err) + + err = makeCPUInfoFile(cpuInfoFile, vendor, flags) + assert.NoError(err) + + createModules(assert, cpuInfoFile, actualModuleData) + + count, err := checkKernelModules(requiredModules, archKernelParamHandler) + + assert.NoError(err) + // fails due to unrestricted_guest not being available + assert.Equal(count, uint32(1)) + + // pretend test is running under a hypervisor + flags += " hypervisor" + + // recreate + err = makeCPUInfoFile(cpuInfoFile, vendor, flags) + assert.NoError(err) + + // create buffer to save logger output + buf := &bytes.Buffer{} + + savedLogOutput := kataLog.Logger.Out + + defer func() { + kataLog.Logger.Out = savedLogOutput + }() + + kataLog.Logger.Out = buf + + count, err = checkKernelModules(requiredModules, archKernelParamHandler) + + // no error now because running under a hypervisor + assert.NoError(err) + assert.Equal(count, uint32(0)) + + re := regexp.MustCompile(`.*\bunrestricted_guest\b`) + matches := re.FindAllStringSubmatch(buf.String(), -1) + assert.NotEmpty(matches) +} + +func TestCheckHostIsVMContainerCapable(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(testDisabledAsNonRoot) + } + + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedSysModuleDir := sysModuleDir + savedProcCPUInfo := procCPUInfo + + cpuInfoFile := filepath.Join(dir, "cpuinfo") + + // XXX: override + sysModuleDir = filepath.Join(dir, "sys/module") + procCPUInfo = cpuInfoFile + + defer func() { + sysModuleDir = savedSysModuleDir + procCPUInfo = savedProcCPUInfo + }() + + err = os.MkdirAll(sysModuleDir, testDirMode) + if err != nil { + t.Fatal(err) + } + + var cpuData []testCPUData + var moduleData []testModuleData + + if cpuType == cpuTypeIntel { + cpuData = []testCPUData{ + {"", "", true}, + {"Intel", "", true}, + {archGenuineIntel, "", true}, + {archGenuineIntel, "lm", true}, + {archGenuineIntel, "lm vmx", true}, + {archGenuineIntel, "lm vmx sse4_1", false}, + } + + moduleData = []testModuleData{ + {filepath.Join(sysModuleDir, "kvm"), true, ""}, + {filepath.Join(sysModuleDir, "kvm_intel"), true, ""}, + {filepath.Join(sysModuleDir, "kvm_intel/parameters/nested"), false, "Y"}, + {filepath.Join(sysModuleDir, "kvm_intel/parameters/unrestricted_guest"), false, "Y"}, + } + } else if cpuType == cpuTypeAMD { + cpuData = []testCPUData{ + {"", "", true}, + {"AMD", "", true}, + {archAuthenticAMD, "", true}, + {archAuthenticAMD, "lm", true}, + {archAuthenticAMD, "lm svm", true}, + {archAuthenticAMD, "lm svm sse4_1", false}, + } + + moduleData = []testModuleData{ + {filepath.Join(sysModuleDir, "kvm"), true, ""}, + {filepath.Join(sysModuleDir, "kvm_amd"), true, ""}, + {filepath.Join(sysModuleDir, "kvm_amd/parameters/nested"), false, "1"}, + } + } + + setupCheckHostIsVMContainerCapable(assert, cpuInfoFile, cpuData, moduleData) + + // remove the modules to force a failure + err = os.RemoveAll(sysModuleDir) + assert.NoError(err) + + details := vmContainerCapableDetails{ + cpuInfoFile: cpuInfoFile, + requiredCPUFlags: archRequiredCPUFlags, + requiredCPUAttribs: archRequiredCPUAttribs, + requiredKernelModules: archRequiredKernelModules, + } + + err = hostIsVMContainerCapable(details) + assert.Nil(err) +} + +func TestArchKernelParamHandler(t *testing.T) { + assert := assert.New(t) + + type testData struct { + onVMM bool + expectIgnore bool + fields logrus.Fields + msg string + } + + data := []testData{ + {true, false, logrus.Fields{}, ""}, + {false, false, logrus.Fields{}, ""}, + + { + false, + false, + logrus.Fields{ + // wrong type + "parameter": 123, + }, + "foo", + }, + + { + false, + false, + logrus.Fields{ + "parameter": "unrestricted_guest", + }, + "", + }, + + { + true, + true, + logrus.Fields{ + "parameter": "unrestricted_guest", + }, + "", + }, + + { + false, + true, + logrus.Fields{ + "parameter": "nested", + }, + "", + }, + } + + for i, d := range data { + result := archKernelParamHandler(d.onVMM, d.fields, d.msg) + if d.expectIgnore { + assert.True(result, "test %d (%+v)", i, d) + } else { + assert.False(result, "test %d (%+v)", i, d) + } + } +} + +func TestKvmIsUsable(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedKvmDevice := kvmDevice + fakeKVMDevice := filepath.Join(dir, "kvm") + kvmDevice = fakeKVMDevice + + defer func() { + kvmDevice = savedKvmDevice + }() + + err = kvmIsUsable() + assert.Error(err) + + err = createEmptyFile(fakeKVMDevice) + assert.NoError(err) + + err = kvmIsUsable() + assert.Error(err) +} + +func TestGetCPUDetails(t *testing.T) { + const validVendorName = "a vendor" + validVendor := fmt.Sprintf(`%s : %s`, archCPUVendorField, validVendorName) + + const validModelName = "some CPU model" + validModel := fmt.Sprintf(`%s : %s`, archCPUModelField, validModelName) + + validContents := fmt.Sprintf(` +a : b +%s +foo : bar +%s +`, validVendor, validModel) + + data := []testCPUDetail{ + {"", "", "", true}, + {"invalid", "", "", true}, + {archCPUVendorField, "", "", true}, + {validVendor, "", "", true}, + {validModel, "", "", true}, + {validContents, validVendorName, validModelName, false}, + } + genericTestGetCPUDetails(t, validVendor, validModel, validContents, data) +} + +func TestSetCPUtype(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + savedArchRequiredCPUFlags := archRequiredCPUFlags + savedArchRequiredCPUAttribs := archRequiredCPUAttribs + savedArchRequiredKernelModules := archRequiredKernelModules + + defer func() { + archRequiredCPUFlags = savedArchRequiredCPUFlags + archRequiredCPUAttribs = savedArchRequiredCPUAttribs + archRequiredKernelModules = savedArchRequiredKernelModules + }() + + archRequiredCPUFlags = map[string]string{} + archRequiredCPUAttribs = map[string]string{} + archRequiredKernelModules = map[string]kernelModule{} + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + setCPUtype(config.HypervisorType) + + assert.NotEmpty(archRequiredCPUFlags) + assert.NotEmpty(archRequiredCPUAttribs) + assert.NotEmpty(archRequiredKernelModules) + + assert.Equal(archRequiredCPUFlags["vmx"], "Virtualization support") + + _, ok := archRequiredKernelModules["kvm"] + assert.True(ok) +} diff --git a/src/runtime/cli/kata-check_arm64.go b/src/runtime/cli/kata-check_arm64.go new file mode 100644 index 000000000..959f312a4 --- /dev/null +++ b/src/runtime/cli/kata-check_arm64.go @@ -0,0 +1,171 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/sirupsen/logrus" +) + +const ( + cpuFlagsTag = "Features" + archCPUVendorField = "CPU implementer" + archCPUModelField = "CPU architecture" +) + +// archRequiredCPUFlags maps a CPU flag value to search for and a +// human-readable description of that value. +var archRequiredCPUFlags = map[string]string{} + +// archRequiredCPUAttribs maps a CPU (non-CPU flag) attribute value to search for +// and a human-readable description of that value. +var archRequiredCPUAttribs = map[string]string{} + +// archRequiredKernelModules maps a required module name to a human-readable +// description of the modules functionality and an optional list of +// required module parameters. +var archRequiredKernelModules = map[string]kernelModule{ + "kvm": { + desc: "Kernel-based Virtual Machine", + required: true, + }, + "vhost": { + desc: "Host kernel accelerator for virtio", + required: true, + }, + "vhost_net": { + desc: "Host kernel accelerator for virtio network", + required: true, + }, + "vhost_vsock": { + desc: "Host Support for Linux VM Sockets", + required: false, + }, +} + +// archRequiredKVMExtensions maps a required kvm extension to a human-readable +// description of what this extension intends to do and its unique identifier. +var archRequiredKVMExtensions = map[string]kvmExtension{ + "KVM_CAP_ARM_VM_IPA_SIZE": { + desc: "Maximum IPA shift supported by the host", + id: 165, + }, +} + +func setCPUtype(hypervisorType vc.HypervisorType) error { + return nil +} + +// kvmIsUsable determines if it will be possible to create a full virtual machine +// by creating a minimal VM and then deleting it. +func kvmIsUsable() error { + return genericKvmIsUsable() +} + +func checkKVMExtensions() error { + results, err := genericCheckKVMExtensions(archRequiredKVMExtensions) + if err != nil { + return err + } + + // different host supports different maximum IPA limit + ipa := results["KVM_CAP_ARM_VM_IPA_SIZE"] + fields := logrus.Fields{ + "type": "kvm extension", + "name": "KVM_CAP_ARM_VM_IPA_SIZE", + } + + kataLog.WithFields(fields).Infof("IPA limit size: %d bits.", ipa) + + return nil +} + +func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error { + if err := kvmIsUsable(); err != nil { + return err + } + + return checkKVMExtensions() +} + +// hostIsVMContainerCapable checks to see if the host is theoretically capable +// of creating a VM container. +func hostIsVMContainerCapable(details vmContainerCapableDetails) error { + + _, err := getCPUInfo(details.cpuInfoFile) + if err != nil { + return err + } + + count, err := checkKernelModules(details.requiredKernelModules, archKernelParamHandler) + if err != nil { + return err + } + + if count == 0 { + return nil + } + + return fmt.Errorf("ERROR: %s", failMessage) + +} + +func archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool { + return genericArchKernelParamHandler(onVMM, fields, msg) +} + +// The CPU Vendor here for Arm means the CPU core +// IP Implementer. +// normalizeArmVendor maps 'CPU implementer' in /proc/cpuinfo +// to human-readable description of that value. +func normalizeArmVendor(vendor string) string { + + switch vendor { + case "0x41": + vendor = "ARM Limited" + default: + vendor = "3rd Party Limited" + } + + return vendor +} + +// The CPU Model here for Arm means the Instruction set, that is +// the variant number of Arm processor. +// normalizeArmModel maps 'CPU architecture' in /proc/cpuinfo +// to human-readable description of that value. +func normalizeArmModel(model string) string { + switch model { + case "8": + model = "v8" + case "7", "7M", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)": + model = "v7" + case "6", "6TEJ": + model = "v6" + case "5", "5T", "5TE", "5TEJ": + model = "v5" + case "4", "4T": + model = "v4" + case "3": + model = "v3" + default: + model = "unknown" + } + + return model +} + +func getCPUDetails() (string, string, error) { + vendor, model, err := genericGetCPUDetails() + if err == nil { + vendor = normalizeArmVendor(vendor) + model = normalizeArmModel(model) + } + + return vendor, model, err +} diff --git a/src/runtime/cli/kata-check_arm64_test.go b/src/runtime/cli/kata-check_arm64_test.go new file mode 100644 index 000000000..681c25128 --- /dev/null +++ b/src/runtime/cli/kata-check_arm64_test.go @@ -0,0 +1,112 @@ +// Copyright (c) 2018 ARM Limited +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func setupCheckHostIsVMContainerCapable(assert *assert.Assertions, cpuInfoFile string, cpuData []testCPUData, moduleData []testModuleData) { + //For now, Arm64 only deal with module check + _ = cpuData + + createModules(assert, cpuInfoFile, moduleData) + + err := makeCPUInfoFile(cpuInfoFile, "", "") + assert.NoError(err) +} + +func TestCCCheckCLIFunction(t *testing.T) { + var cpuData []testCPUData + moduleData := []testModuleData{ + {filepath.Join(sysModuleDir, "kvm"), true, ""}, + {filepath.Join(sysModuleDir, "vhost"), true, ""}, + {filepath.Join(sysModuleDir, "vhost_net"), true, ""}, + } + + genericCheckCLIFunction(t, cpuData, moduleData) +} + +func TestGetCPUDetails(t *testing.T) { + type testData struct { + contents string + expectedNormalizeVendor string + expectedNormalizeModel string + expectError bool + } + + validVendorName := "0x41" + validNormalizeVendorName := "ARM Limited" + validVendor := fmt.Sprintf(`%s : %s`, archCPUVendorField, validVendorName) + + validModelName := "8" + validNormalizeModelName := "v8" + validModel := fmt.Sprintf(`%s : %s`, archCPUModelField, validModelName) + + validContents := fmt.Sprintf(` +a : b +%s +foo : bar +%s +`, validVendor, validModel) + + data := []testData{ + {"", "", "", true}, + {"invalid", "", "", true}, + {archCPUVendorField, "", "", true}, + {validVendor, "", "", true}, + {validModel, "", "", true}, + {validContents, validNormalizeVendorName, validNormalizeModelName, false}, + } + + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + savedProcCPUInfo := procCPUInfo + + testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo") + + // override + procCPUInfo = testProcCPUInfo + + defer func() { + procCPUInfo = savedProcCPUInfo + }() + + _, _, err = getCPUDetails() + // ENOENT + assert.Error(t, err) + assert.True(t, os.IsNotExist(err)) + + for _, d := range data { + err := createFile(procCPUInfo, d.contents) + assert.NoError(t, err) + + vendor, model, err := getCPUDetails() + + if d.expectError { + assert.Error(t, err, fmt.Sprintf("%+v", d)) + continue + } else { + assert.NoError(t, err, fmt.Sprintf("%+v", d)) + assert.Equal(t, d.expectedNormalizeVendor, vendor) + assert.Equal(t, d.expectedNormalizeModel, model) + } + } +} + +func TestSetCPUtype(t *testing.T) { + testSetCPUTypeGeneric(t) +} diff --git a/src/runtime/cli/kata-check_data_amd64_test.go b/src/runtime/cli/kata-check_data_amd64_test.go new file mode 100644 index 000000000..3e3461679 --- /dev/null +++ b/src/runtime/cli/kata-check_data_amd64_test.go @@ -0,0 +1,63 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +const testCPUInfoTemplate = ` +processor : 0 +vendor_id : {{.VendorID}} +cpu family : 6 +model : 61 +model name : Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz +stepping : 4 +microcode : 0x25 +cpu MHz : 1999.987 +cache size : 4096 KB +physical id : 0 +siblings : 4 +core id : 0 +cpu cores : 2 +apicid : 0 +initial apicid : 0 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : {{.Flags}} +bugs : +bogomips : 5188.36 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +processor : 1 +vendor_id : {{.VendorID}} +cpu family : 6 +model : 61 +model name : Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz +stepping : 4 +microcode : 0x25 +cpu MHz : 1999.987 +cache size : 4096 KB +physical id : 0 +siblings : 4 +core id : 0 +cpu cores : 2 +apicid : 1 +initial apicid : 1 +fpu : yes +fpu_exception : yes +cpuid level : 20 +wp : yes +flags : {{.Flags}} +bugs : +bogomips : 5194.90 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +` diff --git a/src/runtime/cli/kata-check_data_arm64_test.go b/src/runtime/cli/kata-check_data_arm64_test.go new file mode 100644 index 000000000..8b1c76f5c --- /dev/null +++ b/src/runtime/cli/kata-check_data_arm64_test.go @@ -0,0 +1,27 @@ +// Copyright (c) 2018 ARM Limited +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +const testCPUInfoTemplate = ` +processor : 0 +BogoMIPS : 500.00 +Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid +CPU implementer : 0x41 +CPU architecture: 8 +CPU variant : 0x1 +CPU part : 0xd07 +CPU revision : 2 + +processor : 1 +BogoMIPS : 500.00 +Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid +CPU implementer : 0x41 +CPU architecture: 8 +CPU variant : 0x1 +CPU part : 0xd07 +CPU revision : 2 + +` diff --git a/src/runtime/cli/kata-check_data_ppc64le_test.go b/src/runtime/cli/kata-check_data_ppc64le_test.go new file mode 100644 index 000000000..4368be96c --- /dev/null +++ b/src/runtime/cli/kata-check_data_ppc64le_test.go @@ -0,0 +1,24 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "github.com/sirupsen/logrus" + "io/ioutil" +) + +var testCPUInfoTemplate = setTestCPUInfoTemplate() + +func setTestCPUInfoTemplate() string { + + var kataLog *logrus.Entry + content, err := ioutil.ReadFile("/proc/cpuinfo") + + if err != nil { + kataLog.WithError(err).Error("failed to read file /proc/cpuinfo") + } + return string(content) +} diff --git a/src/runtime/cli/kata-check_data_s390x_test.go b/src/runtime/cli/kata-check_data_s390x_test.go new file mode 100644 index 000000000..258f1e1b0 --- /dev/null +++ b/src/runtime/cli/kata-check_data_s390x_test.go @@ -0,0 +1,21 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +const testCPUInfoTemplate = ` +vendor_id : IBM/S390 +# processors : 4 +bogomips per cpu: 20325.00 +max thread id : 0 +features : esan3 zarch stfle msa ldisp eimm dfp edat etf3eh highgprs te vx sie +cache0 : level=1 type=Data scope=Private size=128K line_size=256 associativity=8 +cache1 : level=1 type=Instruction scope=Private size=96K line_size=256 associativity=6 +cache2 : level=2 type=Data scope=Private size=2048K line_size=256 associativity=8 +cache3 : level=2 type=Instruction scope=Private size=2048K line_size=256 associativity=8 +cache4 : level=3 type=Unified scope=Shared size=65536K line_size=256 associativity=16 +cache5 : level=4 type=Unified scope=Shared size=491520K line_size=256 associativity=30 +processor 0: version = FF, identification = FFFFFF, machine = 2964 +` diff --git a/src/runtime/cli/kata-check_generic_test.go b/src/runtime/cli/kata-check_generic_test.go new file mode 100644 index 000000000..144126830 --- /dev/null +++ b/src/runtime/cli/kata-check_generic_test.go @@ -0,0 +1,47 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// +build arm64 ppc64le + +package main + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func testSetCPUTypeGeneric(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + savedArchRequiredCPUFlags := archRequiredCPUFlags + savedArchRequiredCPUAttribs := archRequiredCPUAttribs + savedArchRequiredKernelModules := archRequiredKernelModules + + defer func() { + archRequiredCPUFlags = savedArchRequiredCPUFlags + archRequiredCPUAttribs = savedArchRequiredCPUAttribs + archRequiredKernelModules = savedArchRequiredKernelModules + }() + + assert.Empty(archRequiredCPUFlags) + assert.Empty(archRequiredCPUAttribs) + assert.NotEmpty(archRequiredKernelModules) + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + setCPUtype(config.HypervisorType) + + assert.Equal(archRequiredCPUFlags, savedArchRequiredCPUFlags) + assert.Equal(archRequiredCPUAttribs, savedArchRequiredCPUAttribs) + assert.Equal(archRequiredKernelModules, savedArchRequiredKernelModules) +} diff --git a/src/runtime/cli/kata-check_ppc64le.go b/src/runtime/cli/kata-check_ppc64le.go new file mode 100644 index 000000000..b57c971ce --- /dev/null +++ b/src/runtime/cli/kata-check_ppc64le.go @@ -0,0 +1,190 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "os/exec" + "regexp" + "strconv" + "strings" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/sirupsen/logrus" +) + +const ( + cpuFlagsTag = genericCPUFlagsTag + archCPUVendorField = "" + archCPUModelField = "model" +) + +var ( + ppc64CpuCmd = "ppc64_cpu" + smtStatusOption = "--smt" + _ = genericCPUVendorField + _ = genericCPUModelField +) + +// archRequiredCPUFlags maps a CPU flag value to search for and a +// human-readable description of that value. +var archRequiredCPUFlags = map[string]string{} + +// archRequiredCPUAttribs maps a CPU (non-CPU flag) attribute value to search for +// and a human-readable description of that value. +var archRequiredCPUAttribs = map[string]string{} + +// archRequiredKernelModules maps a required module name to a human-readable +// description of the modules functionality and an optional list of +// required module parameters. +var archRequiredKernelModules = map[string]kernelModule{ + "kvm": { + desc: "Kernel-based Virtual Machine", + required: true, + }, + "kvm_hv": { + desc: "Kernel-based Virtual Machine hardware virtualization", + required: true, + }, + "vhost_vsock": { + desc: "Host Support for Linux VM Sockets", + required: false, + }, +} + +func setCPUtype(hypervisorType vc.HypervisorType) error { + return nil +} + +func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error { + return kvmIsUsable() +} + +// hostIsVMContainerCapable checks to see if the host is theoretically capable +// of creating a VM container. +func hostIsVMContainerCapable(details vmContainerCapableDetails) error { + + _, err := getCPUInfo(details.cpuInfoFile) + if err != nil { + return err + } + + text, err := katautils.GetFileContents(details.cpuInfoFile) + if err != nil { + return err + } + + ae := regexp.MustCompile("[0-9]+") + re := regexp.MustCompile("POWER[0-9]") + powerProcessor, err := strconv.Atoi(ae.FindString(re.FindString(text))) + if err != nil { + kataLog.WithError(err).Error("Failed to find Power Processor number from ", details.cpuInfoFile) + } + + if powerProcessor <= 8 { + if !isSMTOff() { + return fmt.Errorf("SMT is not Off. %s", failMessage) + } + } + + count, err := checkKernelModules(details.requiredKernelModules, archKernelParamHandler) + if err != nil { + return err + } + + if count == 0 { + return nil + } + + return fmt.Errorf("ERROR: %s", failMessage) +} + +// kvmIsUsable determines if it will be possible to create a full virtual machine +// by creating a minimal VM and then deleting it. +func kvmIsUsable() error { + return genericKvmIsUsable() +} + +func archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool { + return genericArchKernelParamHandler(onVMM, fields, msg) +} + +func getPPC64leCPUInfo(cpuInfoFile string) (string, error) { + text, err := katautils.GetFileContents(cpuInfoFile) + if err != nil { + return "", err + } + + if len(strings.TrimSpace(text)) == 0 { + return "", fmt.Errorf("Cannot determine CPU details") + } + + return text, nil +} + +func getCPUDetails() (vendor, model string, err error) { + + if vendor, model, err := genericGetCPUDetails(); err == nil { + return vendor, model, nil + } + + cpuinfo, err := getPPC64leCPUInfo(procCPUInfo) + if err != nil { + return "", "", err + } + + lines := strings.Split(cpuinfo, "\n") + + for _, line := range lines { + if archCPUVendorField != "" { + if strings.HasPrefix(line, archCPUVendorField) { + fields := strings.Split(line, ":") + if len(fields) > 1 { + vendor = strings.TrimSpace(fields[1]) + } + } + } + + if archCPUModelField != "" { + if strings.HasPrefix(line, archCPUModelField) { + fields := strings.Split(line, ":") + if len(fields) > 1 { + model = strings.TrimSpace(fields[1]) + } + } + } + } + + if archCPUVendorField != "" && vendor == "" { + return "", "", fmt.Errorf("cannot find vendor field in file %v", procCPUInfo) + } + + if archCPUModelField != "" && model == "" { + return "", "", fmt.Errorf("cannot find model field in file %v", procCPUInfo) + } + + return vendor, model, nil +} + +func isSMTOff() bool { + + // Check if the SMT is available and off + + cmd := exec.Command(ppc64CpuCmd, smtStatusOption) + additionalEnv := "LANG=C" + cmd.Env = append(cmd.Env, additionalEnv) + out, err := cmd.Output() + + if err == nil && strings.TrimRight(string(out), "\n") == "SMT is off" { + return true + } else if err != nil { + kataLog.Warn("ppc64_cpu isn't installed, can't detect SMT") + return true + } + + return false +} diff --git a/src/runtime/cli/kata-check_ppc64le_test.go b/src/runtime/cli/kata-check_ppc64le_test.go new file mode 100644 index 000000000..3553a3f84 --- /dev/null +++ b/src/runtime/cli/kata-check_ppc64le_test.go @@ -0,0 +1,175 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func setupCheckHostIsVMContainerCapable(assert *assert.Assertions, cpuInfoFile string, cpuData []testCPUData, moduleData []testModuleData) { + createModules(assert, cpuInfoFile, moduleData) + + // all the modules files have now been created, so deal with the + // cpuinfo data. + for _, d := range cpuData { + err := makeCPUInfoFile(cpuInfoFile, d.vendorID, d.flags) + assert.NoError(err) + + details := vmContainerCapableDetails{ + cpuInfoFile: cpuInfoFile, + requiredCPUFlags: archRequiredCPUFlags, + requiredCPUAttribs: archRequiredCPUAttribs, + requiredKernelModules: archRequiredKernelModules, + } + + err = hostIsVMContainerCapable(details) + if d.expectError { + assert.Error(err) + } else { + assert.NoError(err) + } + } +} + +func TestCCCheckCLIFunction(t *testing.T) { + cpuData := []testCPUData{ + fakeCPUData, + } + + moduleData := []testModuleData{ + {filepath.Join(sysModuleDir, "kvm"), false, "Y"}, + {filepath.Join(sysModuleDir, "kvm_hv"), false, "Y"}, + } + + genericCheckCLIFunction(t, cpuData, moduleData) +} + +func TestArchKernelParamHandler(t *testing.T) { + assert := assert.New(t) + + type testData struct { + onVMM bool + expectIgnore bool + fields logrus.Fields + msg string + } + + data := []testData{ + {true, false, logrus.Fields{}, ""}, + {false, false, logrus.Fields{}, ""}, + + { + false, + false, + logrus.Fields{ + // wrong type + "parameter": 123, + }, + "foo", + }, + + { + false, + false, + logrus.Fields{ + "parameter": "unrestricted_guest", + }, + "", + }, + + { + true, + true, + logrus.Fields{ + "parameter": "unrestricted_guest", + }, + "", + }, + + { + false, + true, + logrus.Fields{ + "parameter": "nested", + }, + "", + }, + } + + for i, d := range data { + result := archKernelParamHandler(d.onVMM, d.fields, d.msg) + if d.expectIgnore { + assert.True(result, "test %d (%+v)", i, d) + } else { + assert.False(result, "test %d (%+v)", i, d) + } + } +} + +func TestKvmIsUsable(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedKvmDevice := kvmDevice + fakeKVMDevice := filepath.Join(dir, "kvm") + kvmDevice = fakeKVMDevice + + defer func() { + kvmDevice = savedKvmDevice + }() + + err = kvmIsUsable() + assert.Error(err) + + err = createEmptyFile(fakeKVMDevice) + assert.NoError(err) + + err = kvmIsUsable() + assert.Error(err) +} + +func TestGetCPUDetails(t *testing.T) { + + const validVendorName = "" + validVendor := fmt.Sprintf(`%s : %s`, archCPUVendorField, validVendorName) + + const validModelName = "8247-22L" + validModel := fmt.Sprintf(`%s : %s`, archCPUModelField, validModelName) + + validContents := fmt.Sprintf(` +a : b +%s +foo : bar +%s +`, validVendor, validModel) + + data := []testCPUDetail{ + {"", "", "", true}, + {"invalid", "", "", true}, + {archCPUVendorField, "", "", true}, + {validVendor, "", "", true}, + {validModel, "", validModelName, false}, + {validContents, validVendorName, validModelName, false}, + } + + genericTestGetCPUDetails(t, validVendor, validModel, validContents, data) +} + +func TestSetCPUtype(t *testing.T) { + testSetCPUTypeGeneric(t) +} diff --git a/src/runtime/cli/kata-check_s390x.go b/src/runtime/cli/kata-check_s390x.go new file mode 100644 index 000000000..0c67f1520 --- /dev/null +++ b/src/runtime/cli/kata-check_s390x.go @@ -0,0 +1,131 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "strings" + + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/sirupsen/logrus" +) + +const ( + cpuFlagsTag = genericCPUFlagsTag + archCPUVendorField = genericCPUVendorField + // On s390x the cpu model is indicated by the field machine. + // Example: + // processor 0: version = FF, identification = 3FEC87, machine = 2964 + archCPUModelField = "machine" +) + +// archRequiredCPUFlags maps a CPU flag value to search for and a +// human-readable description of that value. +var archRequiredCPUFlags = map[string]string{} + +// archRequiredCPUAttribs maps a CPU (non-CPU flag) attribute value to search for +// and a human-readable description of that value. +var archRequiredCPUAttribs = map[string]string{} + +// archRequiredKernelModules maps a required module name to a human-readable +// description of the modules functionality and an optional list of +// required module parameters. +var archRequiredKernelModules = map[string]kernelModule{ + "kvm": { + desc: "Kernel-based Virtual Machine", + required: true, + }, + "vhost_vsock": { + desc: "Host Support for Linux VM Sockets", + required: false, + }, +} + +func setCPUtype(hypervisorType vc.HypervisorType) error { + return nil +} + +// kvmIsUsable determines if it will be possible to create a full virtual machine +// by creating a minimal VM and then deleting it. +func kvmIsUsable() error { + return genericKvmIsUsable() +} + +func archHostCanCreateVMContainer(hypervisorType vc.HypervisorType) error { + return kvmIsUsable() +} + +// hostIsVMContainerCapable checks to see if the host is theoretically capable +// of creating a VM container. +func hostIsVMContainerCapable(details vmContainerCapableDetails) error { + + _, err := getCPUInfo(details.cpuInfoFile) + if err != nil { + return err + } + + count, err := checkKernelModules(details.requiredKernelModules, archKernelParamHandler) + if err != nil { + return err + } + + if count == 0 { + return nil + } + + return fmt.Errorf("ERROR: %s", failMessage) + +} + +func archKernelParamHandler(onVMM bool, fields logrus.Fields, msg string) bool { + return genericArchKernelParamHandler(onVMM, fields, msg) +} + +// getS390xCPUDetails returns the cpu information +func getS390xCPUDetails() (vendor, model string, err error) { + prefixModel := "processor" + cpuinfo, err := getCPUInfo(procCPUInfo) + if err != nil { + return "", "", err + } + + lines := strings.Split(cpuinfo, "\n") + + for _, line := range lines { + if archCPUVendorField != "" { + if strings.HasPrefix(line, archCPUVendorField) { + fields := strings.Split(line, ":") + if len(fields) > 1 { + vendor = strings.TrimSpace(fields[1]) + } + } + } + if archCPUModelField != "" { + if strings.HasPrefix(line, prefixModel) { + fields := strings.Split(strings.TrimSpace(line), ",") + cpuModel := strings.Split(fields[2], "=") + model = strings.TrimSpace(cpuModel[1]) + } + } + } + + if vendor == "" { + return "", "", fmt.Errorf("cannot find vendor field in file %v", procCPUInfo) + } + + if model == "" { + return "", "", fmt.Errorf("Error in parsing cpu model from %v", procCPUInfo) + } + + return vendor, model, nil +} + +func getCPUDetails() (vendor, model string, err error) { + if vendor, model, err := genericGetCPUDetails(); err == nil { + return vendor, model, nil + } + return getS390xCPUDetails() +} diff --git a/src/runtime/cli/kata-check_s390x_test.go b/src/runtime/cli/kata-check_s390x_test.go new file mode 100644 index 000000000..1bcffd7e6 --- /dev/null +++ b/src/runtime/cli/kata-check_s390x_test.go @@ -0,0 +1,168 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func setupCheckHostIsVMContainerCapable(assert *assert.Assertions, cpuInfoFile string, cpuData []testCPUData, moduleData []testModuleData) { + createModules(assert, cpuInfoFile, moduleData) + + // all the modules files have now been created, so deal with the + // cpuinfo data. + for _, d := range cpuData { + err := makeCPUInfoFile(cpuInfoFile, d.vendorID, d.flags) + assert.NoError(err) + + details := vmContainerCapableDetails{ + cpuInfoFile: cpuInfoFile, + requiredCPUFlags: archRequiredCPUFlags, + requiredCPUAttribs: archRequiredCPUAttribs, + requiredKernelModules: archRequiredKernelModules, + } + + err = hostIsVMContainerCapable(details) + if d.expectError { + assert.Error(err) + } else { + assert.NoError(err) + } + } +} + +func TestCCCheckCLIFunction(t *testing.T) { + cpuData := []testCPUData{ + fakeCPUData, + } + + moduleData := []testModuleData{ + {filepath.Join(sysModuleDir, "kvm"), false, "Y"}, + } + + genericCheckCLIFunction(t, cpuData, moduleData) +} + +func TestArchKernelParamHandler(t *testing.T) { + assert := assert.New(t) + + type testData struct { + onVMM bool + expectIgnore bool + fields logrus.Fields + msg string + } + + data := []testData{ + {true, false, logrus.Fields{}, ""}, + {false, false, logrus.Fields{}, ""}, + + { + false, + false, + logrus.Fields{ + // wrong type + "parameter": 123, + }, + "foo", + }, + + { + false, + false, + logrus.Fields{ + "parameter": "unrestricted_guest", + }, + "", + }, + + { + true, + true, + logrus.Fields{ + "parameter": "unrestricted_guest", + }, + "", + }, + + { + false, + true, + logrus.Fields{ + "parameter": "nested", + }, + "", + }, + } + + for i, d := range data { + result := archKernelParamHandler(d.onVMM, d.fields, d.msg) + if d.expectIgnore { + assert.True(result, "test %d (%+v)", i, d) + } else { + assert.False(result, "test %d (%+v)", i, d) + } + } +} + +func TestKvmIsUsable(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedKvmDevice := kvmDevice + fakeKVMDevice := filepath.Join(dir, "kvm") + kvmDevice = fakeKVMDevice + + defer func() { + kvmDevice = savedKvmDevice + }() + + err = kvmIsUsable() + assert.Error(err) + + err = createEmptyFile(fakeKVMDevice) + assert.NoError(err) + + err = kvmIsUsable() + assert.Error(err) +} + +func TestGetCPUDetails(t *testing.T) { + const validVendorName = "a vendor" + validVendor := fmt.Sprintf(`%s : %s`, archCPUVendorField, validVendorName) + + const validModelName = "some CPU model" + validModel := fmt.Sprintf(`processor 0: version = 00, identification = XXXXX, %s = %s`, archCPUModelField, validModelName) + + validContents := fmt.Sprintf(` +a : b +%s +foo : bar +%s +`, validVendor, validModel) + + data := []testCPUDetail{ + {"", "", "", true}, + {"invalid", "", "", true}, + {archCPUVendorField, "", "", true}, + {validVendor, "", "", true}, + {validModel, "", "", true}, + {validContents, validVendorName, validModelName, false}, + } + genericTestGetCPUDetails(t, validVendor, validModel, validContents, data) +} diff --git a/src/runtime/cli/kata-check_test.go b/src/runtime/cli/kata-check_test.go new file mode 100644 index 000000000..b824a01d6 --- /dev/null +++ b/src/runtime/cli/kata-check_test.go @@ -0,0 +1,992 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "bytes" + "flag" + "fmt" + "html/template" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + "testing" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +type testModuleData struct { + path string + isDir bool + contents string +} + +// nolint: structcheck, unused, deadcode +type testCPUData struct { + vendorID string + flags string + expectError bool +} + +// nolint: structcheck, unused, deadcode +type testCPUDetail struct { + contents string + expectedVendor string + expectedModel string + expectError bool +} + +var fakeCPUData = testCPUData{"", "", false} + +func createFile(file, contents string) error { + return ioutil.WriteFile(file, []byte(contents), testFileMode) +} + +func createModules(assert *assert.Assertions, cpuInfoFile string, moduleData []testModuleData) { + for _, d := range moduleData { + var dir string + + if d.isDir { + dir = d.path + } else { + dir = path.Dir(d.path) + } + + err := os.MkdirAll(dir, testDirMode) + assert.NoError(err) + + if !d.isDir { + err = createFile(d.path, d.contents) + assert.NoError(err) + } + + details := vmContainerCapableDetails{ + cpuInfoFile: cpuInfoFile, + } + + err = hostIsVMContainerCapable(details) + if katautils.FileExists(cpuInfoFile) { + assert.NoError(err) + } else { + assert.Error(err) + } + } +} + +func checkKernelParamHandler(assert *assert.Assertions, kernelModulesToCreate, expectedKernelModules map[string]kernelModule, handler kernelParamHandler, expectHandlerError bool, expectedErrorCount uint32) { + err := os.RemoveAll(sysModuleDir) + assert.NoError(err) + + count, err := checkKernelModules(map[string]kernelModule{}, handler) + + // No required modules means no error + assert.NoError(err) + assert.Equal(count, uint32(0)) + + count, err = checkKernelModules(expectedKernelModules, handler) + assert.NoError(err) + + // No modules exist + expectedCount := len(expectedKernelModules) + assert.Equal(count, uint32(expectedCount)) + + err = os.MkdirAll(sysModuleDir, testDirMode) + assert.NoError(err) + + for module, details := range kernelModulesToCreate { + path := filepath.Join(sysModuleDir, module) + err = os.MkdirAll(path, testDirMode) + assert.NoError(err) + + paramDir := filepath.Join(path, "parameters") + err = os.MkdirAll(paramDir, testDirMode) + assert.NoError(err) + + for param, value := range details.parameters { + paramPath := filepath.Join(paramDir, param) + err = createFile(paramPath, value) + assert.NoError(err) + } + } + + count, err = checkKernelModules(expectedKernelModules, handler) + + if expectHandlerError { + assert.Error(err) + return + } + + assert.NoError(err) + assert.Equal(count, expectedErrorCount) +} + +func makeCPUInfoFile(path, vendorID, flags string) error { + t := template.New("cpuinfo") + + t, err := t.Parse(testCPUInfoTemplate) + if err != nil { + return err + } + + args := map[string]string{ + "Flags": flags, + "VendorID": vendorID, + } + + contents := &bytes.Buffer{} + + err = t.Execute(contents, args) + if err != nil { + return err + } + + return ioutil.WriteFile(path, contents.Bytes(), testFileMode) +} + +// nolint: unused, deadcode +func genericTestGetCPUDetails(t *testing.T, validVendor string, validModel string, validContents string, data []testCPUDetail) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + savedProcCPUInfo := procCPUInfo + + testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo") + + // override + procCPUInfo = testProcCPUInfo + + defer func() { + procCPUInfo = savedProcCPUInfo + }() + + _, _, err = getCPUDetails() + // ENOENT + assert.Error(t, err) + assert.True(t, os.IsNotExist(err)) + + for _, d := range data { + err := createFile(procCPUInfo, d.contents) + assert.NoError(t, err) + + vendor, model, err := getCPUDetails() + + if d.expectError { + assert.Error(t, err, fmt.Sprintf("%+v", d)) + continue + } else { + assert.NoError(t, err, fmt.Sprintf("%+v", d)) + assert.Equal(t, d.expectedVendor, vendor) + assert.Equal(t, d.expectedModel, model) + } + } +} + +func genericCheckCLIFunction(t *testing.T, cpuData []testCPUData, moduleData []testModuleData) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + _, config, err := makeRuntimeConfig(dir) + assert.NoError(err) + + savedSysModuleDir := sysModuleDir + savedProcCPUInfo := procCPUInfo + + cpuInfoFile := filepath.Join(dir, "cpuinfo") + + // XXX: override + sysModuleDir = filepath.Join(dir, "sys/module") + procCPUInfo = cpuInfoFile + + defer func() { + sysModuleDir = savedSysModuleDir + procCPUInfo = savedProcCPUInfo + }() + + // Replace sysModuleDir in moduleData with the test temp path + for i := range moduleData { + moduleData[i].path = strings.Replace(moduleData[i].path, savedSysModuleDir, sysModuleDir, 1) + } + + err = os.MkdirAll(sysModuleDir, testDirMode) + if err != nil { + t.Fatal(err) + } + + devNull, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0666) + assert.NoError(err) + defer devNull.Close() + + savedLogOutput := kataLog.Logger.Out + + // discard normal output + kataLog.Logger.Out = devNull + + defer func() { + kataLog.Logger.Out = savedLogOutput + }() + + setupCheckHostIsVMContainerCapable(assert, cpuInfoFile, cpuData, moduleData) + + flagSet := &flag.FlagSet{} + ctx := createCLIContext(flagSet) + ctx.App.Name = "foo" + ctx.App.Metadata["runtimeConfig"] = config + + // create buffer to save logger output + buf := &bytes.Buffer{} + + // capture output this time + kataLog.Logger.Out = buf + + fn, ok := kataCheckCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + assert.NoError(err) + + output := buf.String() + + for _, c := range cpuData { + if c == fakeCPUData { + continue + } + + assert.True(findAnchoredString(output, c.vendorID)) + for _, flag := range strings.Fields(c.flags) { + assert.True(findAnchoredString(output, flag)) + } + } + + for _, m := range moduleData { + name := path.Base(m.path) + assert.True(findAnchoredString(output, name)) + } +} +func TestCheckGetCPUInfo(t *testing.T) { + assert := assert.New(t) + + type testData struct { + contents string + expectedResult string + expectError bool + } + + data := []testData{ + {"", "", true}, + {" ", "", true}, + {"\n", "", true}, + {"\n\n", "", true}, + {"hello\n", "hello", false}, + {"foo\n\n", "foo", false}, + {"foo\n\nbar\n\n", "foo", false}, + {"foo\n\nbar\nbaz\n\n", "foo", false}, + } + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + file := filepath.Join(dir, "cpuinfo") + // file doesn't exist + _, err = getCPUInfo(file) + assert.Error(err) + + for _, d := range data { + err = ioutil.WriteFile(file, []byte(d.contents), testFileMode) + if err != nil { + t.Fatal(err) + } + defer os.Remove(file) + + contents, err := getCPUInfo(file) + if d.expectError { + assert.Error(err, fmt.Sprintf("got %q, test data: %+v", contents, d)) + } else { + assert.NoError(err, fmt.Sprintf("got %q, test data: %+v", contents, d)) + } + + assert.Equal(d.expectedResult, contents) + } +} + +func TestCheckFindAnchoredString(t *testing.T) { + assert := assert.New(t) + + type testData struct { + haystack string + needle string + expectSuccess bool + } + + data := []testData{ + {"", "", false}, + {"", "foo", false}, + {"foo", "", false}, + {"food", "foo", false}, + {"foo", "foo", true}, + {"foo bar", "foo", true}, + {"foo bar baz", "bar", true}, + } + + for _, d := range data { + result := findAnchoredString(d.haystack, d.needle) + + if d.expectSuccess { + assert.True(result) + } else { + assert.False(result) + } + } +} + +func TestCheckGetCPUFlags(t *testing.T) { + assert := assert.New(t) + + type testData struct { + cpuinfo string + expectedFlags string + } + + data := []testData{ + {"", ""}, + {"foo", ""}, + {"foo bar", ""}, + {":", ""}, + + { + cpuFlagsTag, + "", + }, + { + cpuFlagsTag + ":", + "", + }, + { + fmt.Sprintf("%s: a b c", cpuFlagsTag), + "a b c", + }, + { + fmt.Sprintf("%s: a b c foo bar d", cpuFlagsTag), + "a b c foo bar d", + }, + } + + for _, d := range data { + result := getCPUFlags(d.cpuinfo) + assert.Equal(d.expectedFlags, result) + } +} + +func TestCheckCheckCPUFlags(t *testing.T) { + assert := assert.New(t) + + type testData struct { + cpuflags string + required map[string]string + expectCount uint32 + } + + data := []testData{ + { + "", + map[string]string{}, + 0, + }, + { + "", + map[string]string{ + "a": "A flag", + }, + 0, + }, + { + "", + map[string]string{ + "a": "A flag", + "b": "B flag", + }, + 0, + }, + { + "a b c", + map[string]string{ + "b": "B flag", + }, + 0, + }, + { + "a b c", + map[string]string{ + "x": "X flag", + "y": "Y flag", + "z": "Z flag", + }, + 3, + }, + } + + for _, d := range data { + count := checkCPUFlags(d.cpuflags, d.required) + assert.Equal(d.expectCount, count, "%+v", d) + } +} + +func TestCheckCheckCPUAttribs(t *testing.T) { + assert := assert.New(t) + + type testData struct { + cpuinfo string + required map[string]string + expectCount uint32 + } + + data := []testData{ + { + "", + map[string]string{}, + 0, + }, + { + "", + map[string]string{ + "a": "", + }, + 0, + }, + { + "a: b", + map[string]string{ + "b": "B attribute", + }, + 0, + }, + { + "a: b\nc: d\ne: f", + map[string]string{ + "b": "B attribute", + }, + 0, + }, + { + "a: b\n", + map[string]string{ + "b": "B attribute", + "c": "C attribute", + "d": "D attribute", + }, + 2, + }, + { + "a: b\nc: d\ne: f", + map[string]string{ + "b": "B attribute", + "d": "D attribute", + "f": "F attribute", + }, + 0, + }, + } + + for _, d := range data { + count := checkCPUAttribs(d.cpuinfo, d.required) + assert.Equal(d.expectCount, count, "%+v", d) + } +} + +func TestCheckHaveKernelModule(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedModProbeCmd := modProbeCmd + savedSysModuleDir := sysModuleDir + + // XXX: override (fake the modprobe command failing) + modProbeCmd = "false" + sysModuleDir = filepath.Join(dir, "sys/module") + + defer func() { + modProbeCmd = savedModProbeCmd + sysModuleDir = savedSysModuleDir + }() + + err = os.MkdirAll(sysModuleDir, testDirMode) + if err != nil { + t.Fatal(err) + } + + module := "foo" + + result := haveKernelModule(module) + assert.False(result) + + // XXX: override - make our fake "modprobe" succeed + modProbeCmd = "true" + + result = haveKernelModule(module) + assert.True(result) + + // disable "modprobe" again + modProbeCmd = "false" + + fooDir := filepath.Join(sysModuleDir, module) + err = os.MkdirAll(fooDir, testDirMode) + if err != nil { + t.Fatal(err) + } + + result = haveKernelModule(module) + assert.True(result) +} + +func TestCheckCheckKernelModules(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedModProbeCmd := modProbeCmd + savedSysModuleDir := sysModuleDir + + // XXX: override (fake the modprobe command failing) + modProbeCmd = "false" + sysModuleDir = filepath.Join(dir, "sys/module") + + defer func() { + modProbeCmd = savedModProbeCmd + sysModuleDir = savedSysModuleDir + }() + + err = os.MkdirAll(sysModuleDir, testDirMode) + if err != nil { + t.Fatal(err) + } + + testData := map[string]kernelModule{ + "foo": { + desc: "desc", + parameters: map[string]string{}, + required: true, + }, + "bar": { + desc: "desc", + parameters: map[string]string{ + "param1": "hello", + "param2": "world", + "param3": "a", + "param4": ".", + }, + required: true, + }, + } + + count, err := checkKernelModules(map[string]kernelModule{}, nil) + // No required modules means no error + assert.NoError(err) + assert.Equal(count, uint32(0)) + + count, err = checkKernelModules(testData, nil) + assert.NoError(err) + // No modules exist + assert.Equal(count, uint32(2)) + + for module, details := range testData { + path := filepath.Join(sysModuleDir, module) + err = os.MkdirAll(path, testDirMode) + if err != nil { + t.Fatal(err) + } + + paramDir := filepath.Join(path, "parameters") + err = os.MkdirAll(paramDir, testDirMode) + if err != nil { + t.Fatal(err) + } + + for param, value := range details.parameters { + paramPath := filepath.Join(paramDir, param) + err = createFile(paramPath, value) + if err != nil { + t.Fatal(err) + } + } + } + + count, err = checkKernelModules(testData, nil) + assert.NoError(err) + assert.Equal(count, uint32(0)) +} + +func TestCheckCheckKernelModulesUnreadableFile(t *testing.T) { + assert := assert.New(t) + + if tc.NotValid(ktu.NeedNonRoot()) { + t.Skip(ktu.TestDisabledNeedNonRoot) + } + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + testData := map[string]kernelModule{ + "foo": { + desc: "desc", + parameters: map[string]string{ + "param1": "wibble", + }, + required: true, + }, + } + + savedModProbeCmd := modProbeCmd + savedSysModuleDir := sysModuleDir + + // XXX: override (fake the modprobe command failing) + modProbeCmd = "false" + sysModuleDir = filepath.Join(dir, "sys/module") + + defer func() { + modProbeCmd = savedModProbeCmd + sysModuleDir = savedSysModuleDir + }() + + modPath := filepath.Join(sysModuleDir, "foo/parameters") + err = os.MkdirAll(modPath, testDirMode) + assert.NoError(err) + + modParamFile := filepath.Join(modPath, "param1") + + err = createEmptyFile(modParamFile) + assert.NoError(err) + + // make file unreadable by non-root user + err = os.Chmod(modParamFile, 0000) + assert.NoError(err) + + _, err = checkKernelModules(testData, nil) + assert.Error(err) +} + +func TestCheckCheckKernelModulesInvalidFileContents(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + testData := map[string]kernelModule{ + "foo": { + desc: "desc", + parameters: map[string]string{ + "param1": "wibble", + }, + required: true, + }, + } + + savedModProbeCmd := modProbeCmd + savedSysModuleDir := sysModuleDir + + // XXX: override (fake the modprobe command failing) + modProbeCmd = "false" + sysModuleDir = filepath.Join(dir, "sys/module") + + defer func() { + modProbeCmd = savedModProbeCmd + sysModuleDir = savedSysModuleDir + }() + + modPath := filepath.Join(sysModuleDir, "foo/parameters") + err = os.MkdirAll(modPath, testDirMode) + assert.NoError(err) + + modParamFile := filepath.Join(modPath, "param1") + + err = createFile(modParamFile, "burp") + assert.NoError(err) + + count, err := checkKernelModules(testData, nil) + assert.NoError(err) + assert.Equal(count, uint32(1)) +} + +func TestCheckCLIFunctionFail(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + _, config, err := makeRuntimeConfig(dir) + assert.NoError(err) + + oldProcCPUInfo := procCPUInfo + + // doesn't exist + procCPUInfo = filepath.Join(dir, "cpuinfo") + + defer func() { + procCPUInfo = oldProcCPUInfo + }() + + flagSet := &flag.FlagSet{} + ctx := createCLIContext(flagSet) + ctx.App.Name = "foo" + ctx.App.Metadata["runtimeConfig"] = config + + fn, ok := kataCheckCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + assert.Error(err) +} + +func TestCheckKernelParamHandler(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedModProbeCmd := modProbeCmd + savedSysModuleDir := sysModuleDir + + // XXX: override (fake the modprobe command failing) + modProbeCmd = "false" + sysModuleDir = filepath.Join(dir, "sys/module") + + defer func() { + modProbeCmd = savedModProbeCmd + sysModuleDir = savedSysModuleDir + }() + + handler := func(onVMM bool, fields logrus.Fields, msg string) bool { + param, ok := fields["parameter"].(string) + if !ok { + return false + } + + if param == "param1" { + return true + } + + // don't ignore the error + return false + } + + testData1 := map[string]kernelModule{ + "foo": { + desc: "desc", + parameters: map[string]string{}, + required: true, + }, + "bar": { + desc: "desc", + parameters: map[string]string{ + "param1": "hello", + "param2": "world", + }, + required: true, + }, + } + + checkKernelParamHandler(assert, testData1, testData1, handler, false, uint32(0)) + + testDataToCreate := map[string]kernelModule{ + "foo": { + desc: "desc", + parameters: map[string]string{ + "param1": "moo", + }, + required: true, + }, + } + + testDataToExpect := map[string]kernelModule{ + "foo": { + desc: "desc", + parameters: map[string]string{ + "param1": "bar", + }, + required: true, + }, + } + + // Expected and actual are different, but the handler should deal with + // the problem. + checkKernelParamHandler(assert, testDataToCreate, testDataToExpect, handler, false, uint32(0)) + + // Expected and actual are different, so with no handler we expect a + // single error (due to "param1"'s value being different) + checkKernelParamHandler(assert, testDataToCreate, testDataToExpect, nil, false, uint32(1)) +} + +func TestArchRequiredKernelModules(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + err = setCPUtype(config.HypervisorType) + assert.NoError(err) + + if len(archRequiredKernelModules) == 0 { + // No modules to check + return + } + + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + savedModProbeCmd := modProbeCmd + savedSysModuleDir := sysModuleDir + + // XXX: override + sysModuleDir = filepath.Join(dir, "sys/module") + modProbeCmd = "false" + + defer func() { + sysModuleDir = savedSysModuleDir + modProbeCmd = savedModProbeCmd + }() + + // Running check with no modules + count, err := checkKernelModules(archRequiredKernelModules, nil) + assert.NoError(err) + + // Test that count returned matches the # of modules with required set. + expectedCount := 0 + for _, module := range archRequiredKernelModules { + if module.required { + expectedCount++ + } + } + + assert.EqualValues(count, expectedCount) +} + +func TestCheckVersionConsistencyInComponents(t *testing.T) { + type testData struct { + proxyExist bool + expectError bool + shimVersion string + proxyVersion string + runtimeVersion string + } + + data := []testData{ + { + true, + true, + "kata-shim version 0.2.0-rc0-xxxxxxxxxxxxx", + "kata-proxy version 0.1.0-rc0-xxxxxxxxxxxxx", + "0.2.0-rc0", + }, + { + true, + true, + "kata-shim version 0.1.0-rc0-xxxxxxxxxxxxx", + "kata-proxy version 0.2.0-rc0-xxxxxxxxxxxxx", + "0.2.0-rc0", + }, + { + true, + true, + "kata-shim version 0.1.0-rc0-xxxxxxxxxxxxx", + "kata-proxy version 0.1.0-rc0-xxxxxxxxxxxxx", + "0.2.0-rc0", + }, + { + true, + false, + "kata-shim version 0.2.0-rc0-xxxxxxxxxxxxx", + "kata-proxy version 0.2.0-rc0-xxxxxxxxxxxxx", + "0.2.0-rc0", + }, + { + false, + true, + "kata-shim version 0.1.0-rc0-xxxxxxxxxxxxx", + "", + "0.2.0-rc0", + }, + { + false, + false, + "kata-shim version 0.2.0-rc0-xxxxxxxxxxxxx", + "", + "0.2.0-rc0", + }, + } + + origVersion := version + for _, d := range data { + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testShimVersion = d.shimVersion + if d.proxyExist { + testProxyVersion = d.proxyVersion + } + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + if !d.proxyExist { + config.ProxyType = vc.NoProxyType + } + version = d.runtimeVersion + defer func() { + version = origVersion + }() + + err = checkVersionConsistencyInComponents(config) + if d.expectError { + assert.Error(t, err, fmt.Sprintf("%+v", d)) + continue + } else { + assert.NoError(t, err, fmt.Sprintf("%+v", d)) + } + } +} diff --git a/src/runtime/cli/kata-env.go b/src/runtime/cli/kata-env.go new file mode 100644 index 000000000..d8a606839 --- /dev/null +++ b/src/runtime/cli/kata-env.go @@ -0,0 +1,517 @@ +// Copyright (c) 2017-2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "encoding/json" + "errors" + "os" + runtim "runtime" + "strings" + + "github.com/BurntSushi/toml" + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + exp "github.com/kata-containers/runtime/virtcontainers/experimental" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + vcUtils "github.com/kata-containers/runtime/virtcontainers/utils" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/urfave/cli" +) + +// Semantic version for the output of the command. +// +// XXX: Increment for every change to the output format +// (meaning any change to the EnvInfo type). +const formatVersion = "1.0.24" + +// MetaInfo stores information on the format of the output itself +type MetaInfo struct { + // output format version + Version string +} + +// KernelInfo stores kernel details +type KernelInfo struct { + Path string + Parameters string +} + +// InitrdInfo stores initrd image details +type InitrdInfo struct { + Path string +} + +// ImageInfo stores root filesystem image details +type ImageInfo struct { + Path string +} + +// CPUInfo stores host CPU details +type CPUInfo struct { + Vendor string + Model string +} + +// RuntimeConfigInfo stores runtime config details. +type RuntimeConfigInfo struct { + Path string +} + +// RuntimeInfo stores runtime details. +type RuntimeInfo struct { + Version RuntimeVersionInfo + Config RuntimeConfigInfo + Debug bool + Trace bool + DisableGuestSeccomp bool + DisableNewNetNs bool + SandboxCgroupOnly bool + Experimental []exp.Feature + Path string +} + +type VersionInfo struct { + Semver string + Major uint64 + Minor uint64 + Patch uint64 + Commit string +} + +// RuntimeVersionInfo stores details of the runtime version +type RuntimeVersionInfo struct { + Version VersionInfo + OCI string +} + +// HypervisorInfo stores hypervisor details +type HypervisorInfo struct { + MachineType string + Version string + Path string + BlockDeviceDriver string + EntropySource string + SharedFS string + VirtioFSDaemon string + Msize9p uint32 + MemorySlots uint32 + PCIeRootPort uint32 + HotplugVFIOOnRootBus bool + Debug bool + UseVSock bool +} + +// ProxyInfo stores proxy details +type ProxyInfo struct { + Type string + Version VersionInfo + Path string + Debug bool +} + +// ShimInfo stores shim details +type ShimInfo struct { + Type string + Version VersionInfo + Path string + Debug bool +} + +// AgentInfo stores agent details +type AgentInfo struct { + Type string + Debug bool + Trace bool + TraceMode string + TraceType string +} + +// DistroInfo stores host operating system distribution details. +type DistroInfo struct { + Name string + Version string +} + +// HostInfo stores host details +type HostInfo struct { + Kernel string + Architecture string + Distro DistroInfo + CPU CPUInfo + VMContainerCapable bool + SupportVSocks bool +} + +// NetmonInfo stores netmon details +type NetmonInfo struct { + Version VersionInfo + Path string + Debug bool + Enable bool +} + +// EnvInfo collects all information that will be displayed by the +// env command. +// +// XXX: Any changes must be coupled with a change to formatVersion. +type EnvInfo struct { + Meta MetaInfo + Runtime RuntimeInfo + Hypervisor HypervisorInfo + Image ImageInfo + Kernel KernelInfo + Initrd InitrdInfo + Proxy ProxyInfo + Shim ShimInfo + Agent AgentInfo + Host HostInfo + Netmon NetmonInfo +} + +func getMetaInfo() MetaInfo { + return MetaInfo{ + Version: formatVersion, + } +} + +func getRuntimeInfo(configFile string, config oci.RuntimeConfig) RuntimeInfo { + runtimeVersionInfo := constructVersionInfo(version) + runtimeVersionInfo.Commit = commit + + runtimeVersion := RuntimeVersionInfo{ + Version: runtimeVersionInfo, + OCI: specs.Version, + } + + runtimeConfig := RuntimeConfigInfo{ + Path: configFile, + } + + runtimePath, _ := os.Executable() + + return RuntimeInfo{ + Debug: config.Debug, + Trace: config.Trace, + Version: runtimeVersion, + Config: runtimeConfig, + Path: runtimePath, + DisableNewNetNs: config.DisableNewNetNs, + SandboxCgroupOnly: config.SandboxCgroupOnly, + Experimental: config.Experimental, + DisableGuestSeccomp: config.DisableGuestSeccomp, + } +} + +func getHostInfo() (HostInfo, error) { + hostKernelVersion, err := getKernelVersion() + if err != nil { + return HostInfo{}, err + } + + hostDistroName, hostDistroVersion, err := getDistroDetails() + if err != nil { + return HostInfo{}, err + } + + cpuVendor, cpuModel, err := getCPUDetails() + if err != nil { + return HostInfo{}, err + } + + hostVMContainerCapable := true + if runtim.GOARCH == "ppc64le" { + hostVMContainerCapable = false + } + + details := vmContainerCapableDetails{ + cpuInfoFile: procCPUInfo, + requiredCPUFlags: archRequiredCPUFlags, + requiredCPUAttribs: archRequiredCPUAttribs, + requiredKernelModules: archRequiredKernelModules, + } + + if err = hostIsVMContainerCapable(details); err != nil { + hostVMContainerCapable = false + } + + hostDistro := DistroInfo{ + Name: hostDistroName, + Version: hostDistroVersion, + } + + hostCPU := CPUInfo{ + Vendor: cpuVendor, + Model: cpuModel, + } + + host := HostInfo{ + Kernel: hostKernelVersion, + Architecture: arch, + Distro: hostDistro, + CPU: hostCPU, + VMContainerCapable: hostVMContainerCapable, + SupportVSocks: vcUtils.SupportsVsocks(), + } + + return host, nil +} + +func getProxyInfo(config oci.RuntimeConfig) ProxyInfo { + if config.ProxyType == vc.NoProxyType { + return ProxyInfo{Type: string(config.ProxyType)} + } + + proxyConfig := config.ProxyConfig + + var proxyVersionInfo VersionInfo + if version, err := getCommandVersion(proxyConfig.Path); err != nil { + proxyVersionInfo = unknownVersionInfo + } else { + proxyVersionInfo = constructVersionInfo(version) + } + + proxy := ProxyInfo{ + Type: string(config.ProxyType), + Version: proxyVersionInfo, + Path: proxyConfig.Path, + Debug: proxyConfig.Debug, + } + + return proxy +} + +func getNetmonInfo(config oci.RuntimeConfig) NetmonInfo { + netmonConfig := config.NetmonConfig + + var netmonVersionInfo VersionInfo + if version, err := getCommandVersion(netmonConfig.Path); err != nil { + netmonVersionInfo = unknownVersionInfo + } else { + netmonVersionInfo = constructVersionInfo(version) + } + + netmon := NetmonInfo{ + Version: netmonVersionInfo, + Path: netmonConfig.Path, + Debug: netmonConfig.Debug, + Enable: netmonConfig.Enable, + } + + return netmon +} + +func getCommandVersion(cmd string) (string, error) { + return katautils.RunCommand([]string{cmd, "--version"}) +} + +func getShimInfo(config oci.RuntimeConfig) (ShimInfo, error) { + shimConfig, ok := config.ShimConfig.(vc.ShimConfig) + if !ok { + return ShimInfo{}, errors.New("cannot determine shim config") + } + + shimPath := shimConfig.Path + + var shimVersionInfo VersionInfo + if version, err := getCommandVersion(shimConfig.Path); err != nil { + shimVersionInfo = unknownVersionInfo + } else { + shimVersionInfo = constructVersionInfo(version) + } + + shim := ShimInfo{ + Type: string(config.ShimType), + Version: shimVersionInfo, + Path: shimPath, + Debug: shimConfig.Debug, + } + + return shim, nil +} + +func getAgentInfo(config oci.RuntimeConfig) (AgentInfo, error) { + agent := AgentInfo{ + Type: string(config.AgentType), + } + + switch config.AgentType { + case vc.KataContainersAgent: + agentConfig, ok := config.AgentConfig.(vc.KataAgentConfig) + if !ok { + return AgentInfo{}, errors.New("cannot determine Kata agent config") + } + agent.Debug = agentConfig.Debug + agent.Trace = agentConfig.Trace + agent.TraceMode = agentConfig.TraceMode + agent.TraceType = agentConfig.TraceType + default: + // Nothing useful to report for the other agent types + } + + return agent, nil +} + +func getHypervisorInfo(config oci.RuntimeConfig) HypervisorInfo { + hypervisorPath := config.HypervisorConfig.HypervisorPath + + version, err := getCommandVersion(hypervisorPath) + if err != nil { + version = unknown + } + + return HypervisorInfo{ + Debug: config.HypervisorConfig.Debug, + MachineType: config.HypervisorConfig.HypervisorMachineType, + Version: version, + Path: hypervisorPath, + BlockDeviceDriver: config.HypervisorConfig.BlockDeviceDriver, + Msize9p: config.HypervisorConfig.Msize9p, + UseVSock: config.HypervisorConfig.UseVSock, + MemorySlots: config.HypervisorConfig.MemSlots, + EntropySource: config.HypervisorConfig.EntropySource, + SharedFS: config.HypervisorConfig.SharedFS, + VirtioFSDaemon: config.HypervisorConfig.VirtioFSDaemon, + + HotplugVFIOOnRootBus: config.HypervisorConfig.HotplugVFIOOnRootBus, + PCIeRootPort: config.HypervisorConfig.PCIeRootPort, + } +} + +func getEnvInfo(configFile string, config oci.RuntimeConfig) (env EnvInfo, err error) { + err = setCPUtype(config.HypervisorType) + if err != nil { + return EnvInfo{}, err + } + + meta := getMetaInfo() + + runtime := getRuntimeInfo(configFile, config) + + host, err := getHostInfo() + if err != nil { + return EnvInfo{}, err + } + + proxy := getProxyInfo(config) + + netmon := getNetmonInfo(config) + + shim, err := getShimInfo(config) + if err != nil { + return EnvInfo{}, err + } + + agent, err := getAgentInfo(config) + if err != nil { + return EnvInfo{}, err + } + + hypervisor := getHypervisorInfo(config) + + image := ImageInfo{ + Path: config.HypervisorConfig.ImagePath, + } + + kernel := KernelInfo{ + Path: config.HypervisorConfig.KernelPath, + Parameters: strings.Join(vc.SerializeParams(config.HypervisorConfig.KernelParams, "="), " "), + } + + initrd := InitrdInfo{ + Path: config.HypervisorConfig.InitrdPath, + } + + env = EnvInfo{ + Meta: meta, + Runtime: runtime, + Hypervisor: hypervisor, + Image: image, + Kernel: kernel, + Initrd: initrd, + Proxy: proxy, + Shim: shim, + Agent: agent, + Host: host, + Netmon: netmon, + } + + return env, nil +} + +func handleSettings(file *os.File, c *cli.Context) error { + if file == nil { + return errors.New("Invalid output file specified") + } + + configFile, ok := c.App.Metadata["configFile"].(string) + if !ok { + return errors.New("cannot determine config file") + } + + runtimeConfig, ok := c.App.Metadata["runtimeConfig"].(oci.RuntimeConfig) + if !ok { + return errors.New("cannot determine runtime config") + } + + env, err := getEnvInfo(configFile, runtimeConfig) + if err != nil { + return err + } + + if c.Bool("json") { + return writeJSONSettings(env, file) + } + + return writeTOMLSettings(env, file) +} + +func writeTOMLSettings(env EnvInfo, file *os.File) error { + encoder := toml.NewEncoder(file) + + err := encoder.Encode(env) + if err != nil { + return err + } + + return nil +} + +func writeJSONSettings(env EnvInfo, file *os.File) error { + encoder := json.NewEncoder(file) + + // Make it more human readable + encoder.SetIndent("", " ") + + err := encoder.Encode(env) + if err != nil { + return err + } + + return nil +} + +var kataEnvCLICommand = cli.Command{ + Name: envCmd, + Usage: "display settings. Default to TOML", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "json", + Usage: "Format output as JSON", + }, + }, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + span, _ := katautils.Trace(ctx, "kata-env") + defer span.Finish() + + return handleSettings(defaultOutputFile, context) + }, +} diff --git a/src/runtime/cli/kata-env_amd64_test.go b/src/runtime/cli/kata-env_amd64_test.go new file mode 100644 index 000000000..e58763be6 --- /dev/null +++ b/src/runtime/cli/kata-env_amd64_test.go @@ -0,0 +1,63 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func getExpectedHostDetails(tmpdir string) (HostInfo, error) { + expectedVendor := "moi" + expectedModel := "awesome XI" + expectedVMContainerCapable := false + return genericGetExpectedHostDetails(tmpdir, expectedVendor, expectedModel, expectedVMContainerCapable) +} + +func TestEnvGetEnvInfoSetsCPUType(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + savedArchRequiredCPUFlags := archRequiredCPUFlags + savedArchRequiredCPUAttribs := archRequiredCPUAttribs + savedArchRequiredKernelModules := archRequiredKernelModules + + defer func() { + archRequiredCPUFlags = savedArchRequiredCPUFlags + archRequiredCPUAttribs = savedArchRequiredCPUAttribs + archRequiredKernelModules = savedArchRequiredKernelModules + }() + + archRequiredCPUFlags = map[string]string{} + archRequiredCPUAttribs = map[string]string{} + archRequiredKernelModules = map[string]kernelModule{} + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + expectedEnv, err := getExpectedSettings(config, tmpdir, configFile) + assert.NoError(err) + + env, err := getEnvInfo(configFile, config) + assert.NoError(err) + + assert.Equal(expectedEnv, env) + + assert.NotEmpty(archRequiredCPUFlags) + assert.NotEmpty(archRequiredCPUAttribs) + assert.NotEmpty(archRequiredKernelModules) + + assert.Equal(archRequiredCPUFlags["vmx"], "Virtualization support") + + _, ok := archRequiredKernelModules["kvm"] + assert.True(ok) +} diff --git a/src/runtime/cli/kata-env_arm64_test.go b/src/runtime/cli/kata-env_arm64_test.go new file mode 100644 index 000000000..c2dcbb7be --- /dev/null +++ b/src/runtime/cli/kata-env_arm64_test.go @@ -0,0 +1,21 @@ +// Copyright (c) 2018 ARM Limited +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "testing" +) + +func getExpectedHostDetails(tmpdir string) (HostInfo, error) { + expectedVendor := "0x41" + expectedModel := "8" + expectedVMContainerCapable := true + return genericGetExpectedHostDetails(tmpdir, expectedVendor, expectedModel, expectedVMContainerCapable) +} + +func TestEnvGetEnvInfoSetsCPUType(t *testing.T) { + testEnvGetEnvInfoSetsCPUTypeGeneric(t) +} diff --git a/src/runtime/cli/kata-env_generic_test.go b/src/runtime/cli/kata-env_generic_test.go new file mode 100644 index 000000000..3077be61b --- /dev/null +++ b/src/runtime/cli/kata-env_generic_test.go @@ -0,0 +1,53 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// +build arm64 ppc64le + +package main + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func testEnvGetEnvInfoSetsCPUTypeGeneric(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + savedArchRequiredCPUFlags := archRequiredCPUFlags + savedArchRequiredCPUAttribs := archRequiredCPUAttribs + savedArchRequiredKernelModules := archRequiredKernelModules + + defer func() { + archRequiredCPUFlags = savedArchRequiredCPUFlags + archRequiredCPUAttribs = savedArchRequiredCPUAttribs + archRequiredKernelModules = savedArchRequiredKernelModules + }() + + assert.Empty(archRequiredCPUFlags) + assert.Empty(archRequiredCPUAttribs) + assert.NotEmpty(archRequiredKernelModules) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + expectedEnv, err := getExpectedSettings(config, tmpdir, configFile) + assert.NoError(err) + + env, err := getEnvInfo(configFile, config) + assert.NoError(err) + + assert.Equal(expectedEnv, env) + + assert.Equal(archRequiredCPUFlags, savedArchRequiredCPUFlags) + assert.Equal(archRequiredCPUAttribs, savedArchRequiredCPUAttribs) + assert.Equal(archRequiredKernelModules, savedArchRequiredKernelModules) +} diff --git a/src/runtime/cli/kata-env_ppc64le_test.go b/src/runtime/cli/kata-env_ppc64le_test.go new file mode 100644 index 000000000..81666f716 --- /dev/null +++ b/src/runtime/cli/kata-env_ppc64le_test.go @@ -0,0 +1,19 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import "testing" + +func getExpectedHostDetails(tmpdir string) (HostInfo, error) { + expectedVendor := "" + expectedModel := "POWER8" + expectedVMContainerCapable := false + return genericGetExpectedHostDetails(tmpdir, expectedVendor, expectedModel, expectedVMContainerCapable) +} + +func TestEnvGetEnvInfoSetsCPUType(t *testing.T) { + testEnvGetEnvInfoSetsCPUTypeGeneric(t) +} diff --git a/src/runtime/cli/kata-env_s390x_test.go b/src/runtime/cli/kata-env_s390x_test.go new file mode 100644 index 000000000..071b7bced --- /dev/null +++ b/src/runtime/cli/kata-env_s390x_test.go @@ -0,0 +1,91 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + vcUtils "github.com/kata-containers/runtime/virtcontainers/utils" + "path/filepath" + goruntime "runtime" +) + +func getExpectedHostDetails(tmpdir string) (HostInfo, error) { + type filesToCreate struct { + file string + contents string + } + + const expectedKernelVersion = "99.1" + const expectedArch = goruntime.GOARCH + + expectedDistro := DistroInfo{ + Name: "Foo", + Version: "42", + } + + expectedCPU := CPUInfo{ + Vendor: "moi", + Model: "awesome XI", + } + + expectedHostDetails := HostInfo{ + Kernel: expectedKernelVersion, + Architecture: expectedArch, + Distro: expectedDistro, + CPU: expectedCPU, + VMContainerCapable: true, + SupportVSocks: vcUtils.SupportsVsocks(), + } + + testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo") + testOSRelease := filepath.Join(tmpdir, "os-release") + + // XXX: This file is *NOT* created by this function on purpose + // (to ensure the only file checked by the tests is + // testOSRelease). osReleaseClr handling is tested in + // utils_test.go. + testOSReleaseClr := filepath.Join(tmpdir, "os-release-clr") + + testProcVersion := filepath.Join(tmpdir, "proc-version") + + // override + procVersion = testProcVersion + osRelease = testOSRelease + osReleaseClr = testOSReleaseClr + procCPUInfo = testProcCPUInfo + + procVersionContents := fmt.Sprintf("Linux version %s a b c", + expectedKernelVersion) + + osReleaseContents := fmt.Sprintf(` +NAME="%s" +VERSION_ID="%s" +`, expectedDistro.Name, expectedDistro.Version) + + procCPUInfoContents := fmt.Sprintf(` +%s : %s +processor 0: version = 00, identification = 3929E7, %s = %s +`, + archCPUVendorField, + expectedCPU.Vendor, + archCPUModelField, + expectedCPU.Model) + + data := []filesToCreate{ + {procVersion, procVersionContents}, + {osRelease, osReleaseContents}, + {procCPUInfo, procCPUInfoContents}, + } + + for _, d := range data { + err := createFile(d.file, d.contents) + if err != nil { + return HostInfo{}, err + } + } + + return expectedHostDetails, nil +} diff --git a/src/runtime/cli/kata-env_test.go b/src/runtime/cli/kata-env_test.go new file mode 100644 index 000000000..b31b6cc21 --- /dev/null +++ b/src/runtime/cli/kata-env_test.go @@ -0,0 +1,1265 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + goruntime "runtime" + "strings" + "testing" + + "github.com/BurntSushi/toml" + vc "github.com/kata-containers/runtime/virtcontainers" + vcUtils "github.com/kata-containers/runtime/virtcontainers/utils" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/urfave/cli" + + "github.com/kata-containers/runtime/pkg/katatestutils" + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/stretchr/testify/assert" +) + +var ( + testProxyVersion = "proxy version 0.1" + testShimVersion = "shim version 0.1" + testNetmonVersion = "netmon version 0.1" + testHypervisorVersion = "QEMU emulator version 2.7.0+git.741f430a96-6.1, Copyright (c) 2003-2016 Fabrice Bellard and the QEMU Project developers" +) + +var ( + hypervisorDebug = false + enableVirtioFS = false + proxyDebug = false + runtimeDebug = false + runtimeTrace = false + shimDebug = false + netmonDebug = false + agentDebug = false + agentTrace = false +) + +// makeVersionBinary creates a shell script with the specified file +// name. When run as "file --version", it will display the specified +// version to stdout and exit successfully. +func makeVersionBinary(file, version string) error { + err := createFile(file, + fmt.Sprintf(`#!/bin/sh + [ "$1" = "--version" ] && echo "%s"`, version)) + if err != nil { + return err + } + + err = os.Chmod(file, testExeFileMode) + if err != nil { + return err + } + + return nil +} + +func createConfig(configPath string, fileData string) error { + + err := ioutil.WriteFile(configPath, []byte(fileData), testFileMode) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to create config file %s %v\n", configPath, err) + return err + } + + return nil +} + +func makeRuntimeConfig(prefixDir string) (configFile string, config oci.RuntimeConfig, err error) { + const logPath = "/log/path" + hypervisorPath := filepath.Join(prefixDir, "hypervisor") + kernelPath := filepath.Join(prefixDir, "kernel") + imagePath := filepath.Join(prefixDir, "image") + kernelParams := "foo=bar xyz" + machineType := "machineType" + shimPath := filepath.Join(prefixDir, "shim") + proxyPath := filepath.Join(prefixDir, "proxy") + netmonPath := filepath.Join(prefixDir, "netmon") + disableBlock := true + blockStorageDriver := "virtio-scsi" + enableIOThreads := true + hotplugVFIOOnRootBus := true + pcieRootPort := uint32(2) + disableNewNetNs := false + sharedFS := "virtio-9p" + + filesToCreate := []string{ + hypervisorPath, + kernelPath, + imagePath, + } + + for _, file := range filesToCreate { + // files must exist and be >0 bytes. + err := katautils.WriteFile(file, "foo", testFileMode) + if err != nil { + return "", oci.RuntimeConfig{}, err + } + } + + err = makeVersionBinary(shimPath, testShimVersion) + if err != nil { + return "", oci.RuntimeConfig{}, err + } + + err = makeVersionBinary(proxyPath, testProxyVersion) + if err != nil { + return "", oci.RuntimeConfig{}, err + } + + err = makeVersionBinary(netmonPath, testNetmonVersion) + if err != nil { + return "", oci.RuntimeConfig{}, err + } + + err = makeVersionBinary(hypervisorPath, testHypervisorVersion) + if err != nil { + return "", oci.RuntimeConfig{}, err + } + + if enableVirtioFS { + sharedFS = "virtio-fs" + } + + hypConfig := katautils.GetDefaultHypervisorConfig() + + configFileOptions := katatestutils.RuntimeConfigOptions{ + Hypervisor: "qemu", + HypervisorPath: hypervisorPath, + KernelPath: kernelPath, + ImagePath: imagePath, + KernelParams: kernelParams, + MachineType: machineType, + ShimPath: shimPath, + ProxyPath: proxyPath, + NetmonPath: netmonPath, + LogPath: logPath, + DefaultGuestHookPath: hypConfig.GuestHookPath, + DisableBlock: disableBlock, + BlockDeviceDriver: blockStorageDriver, + EnableIOThreads: enableIOThreads, + HotplugVFIOOnRootBus: hotplugVFIOOnRootBus, + PCIeRootPort: pcieRootPort, + DisableNewNetNs: disableNewNetNs, + DefaultVCPUCount: hypConfig.NumVCPUs, + DefaultMaxVCPUCount: hypConfig.DefaultMaxVCPUs, + DefaultMemSize: hypConfig.MemorySize, + DefaultMsize9p: hypConfig.Msize9p, + HypervisorDebug: hypervisorDebug, + RuntimeDebug: runtimeDebug, + RuntimeTrace: runtimeTrace, + ProxyDebug: proxyDebug, + ShimDebug: shimDebug, + NetmonDebug: netmonDebug, + AgentDebug: agentDebug, + AgentTrace: agentTrace, + SharedFS: sharedFS, + } + + runtimeConfig := katatestutils.MakeRuntimeConfigFileData(configFileOptions) + + configFile = path.Join(prefixDir, "runtime.toml") + err = createConfig(configFile, runtimeConfig) + if err != nil { + return "", oci.RuntimeConfig{}, err + } + + _, config, err = katautils.LoadConfiguration(configFile, true, false) + if err != nil { + return "", oci.RuntimeConfig{}, err + } + + return configFile, config, nil +} + +func getExpectedProxyDetails(config oci.RuntimeConfig) (ProxyInfo, error) { + return ProxyInfo{ + Type: string(config.ProxyType), + Version: constructVersionInfo(testProxyVersion), + Path: config.ProxyConfig.Path, + Debug: config.ProxyConfig.Debug, + }, nil +} + +func getExpectedNetmonDetails(config oci.RuntimeConfig) (NetmonInfo, error) { + return NetmonInfo{ + Version: constructVersionInfo(testNetmonVersion), + Path: config.NetmonConfig.Path, + Debug: config.NetmonConfig.Debug, + Enable: config.NetmonConfig.Enable, + }, nil +} + +func getExpectedShimDetails(config oci.RuntimeConfig) (ShimInfo, error) { + shimConfig, ok := config.ShimConfig.(vc.ShimConfig) + if !ok { + return ShimInfo{}, fmt.Errorf("failed to get shim config") + } + + shimPath := shimConfig.Path + + return ShimInfo{ + Type: string(config.ShimType), + Version: constructVersionInfo(testShimVersion), + Path: shimPath, + Debug: shimConfig.Debug, + }, nil +} + +func getExpectedAgentDetails(config oci.RuntimeConfig) (AgentInfo, error) { + + agentConfig, ok := config.AgentConfig.(vc.KataAgentConfig) + if !ok { + return AgentInfo{}, fmt.Errorf("expected KataAgentConfig, got %T", config.AgentConfig) + } + + return AgentInfo{ + Type: string(config.AgentType), + Debug: agentConfig.Debug, + Trace: agentConfig.Trace, + + // No trace mode/type set by default + TraceMode: "", + TraceType: "", + }, nil +} + +// nolint: unused, deadcode +func genericGetExpectedHostDetails(tmpdir string, expectedVendor string, expectedModel string, expectedVMContainerCapable bool) (HostInfo, error) { + type filesToCreate struct { + file string + contents string + } + + const expectedKernelVersion = "99.1" + const expectedArch = goruntime.GOARCH + + expectedDistro := DistroInfo{ + Name: "Foo", + Version: "42", + } + + expectedCPU := CPUInfo{ + Vendor: expectedVendor, + Model: expectedModel, + } + + expectedHostDetails := HostInfo{ + Kernel: expectedKernelVersion, + Architecture: expectedArch, + Distro: expectedDistro, + CPU: expectedCPU, + VMContainerCapable: expectedVMContainerCapable, + SupportVSocks: vcUtils.SupportsVsocks(), + } + + testProcCPUInfo := filepath.Join(tmpdir, "cpuinfo") + testOSRelease := filepath.Join(tmpdir, "os-release") + + // XXX: This file is *NOT* created by this function on purpose + // (to ensure the only file checked by the tests is + // testOSRelease). osReleaseClr handling is tested in + // utils_test.go. + testOSReleaseClr := filepath.Join(tmpdir, "os-release-clr") + + testProcVersion := filepath.Join(tmpdir, "proc-version") + + // override + procVersion = testProcVersion + osRelease = testOSRelease + osReleaseClr = testOSReleaseClr + procCPUInfo = testProcCPUInfo + + procVersionContents := fmt.Sprintf("Linux version %s a b c", + expectedKernelVersion) + + osReleaseContents := fmt.Sprintf(` +NAME="%s" +VERSION_ID="%s" +`, expectedDistro.Name, expectedDistro.Version) + + procCPUInfoContents := fmt.Sprintf(` +%s : %s +%s : %s +`, + archCPUVendorField, + expectedCPU.Vendor, + archCPUModelField, + expectedCPU.Model) + + data := []filesToCreate{ + {procVersion, procVersionContents}, + {osRelease, osReleaseContents}, + {procCPUInfo, procCPUInfoContents}, + } + + for _, d := range data { + err := createFile(d.file, d.contents) + if err != nil { + return HostInfo{}, err + } + } + + if goruntime.GOARCH == "arm64" { + expectedHostDetails.CPU.Vendor = "ARM Limited" + expectedHostDetails.CPU.Model = "v8" + } + + return expectedHostDetails, nil +} + +func getExpectedHypervisor(config oci.RuntimeConfig) HypervisorInfo { + return HypervisorInfo{ + Version: testHypervisorVersion, + Path: config.HypervisorConfig.HypervisorPath, + MachineType: config.HypervisorConfig.HypervisorMachineType, + BlockDeviceDriver: config.HypervisorConfig.BlockDeviceDriver, + Msize9p: config.HypervisorConfig.Msize9p, + MemorySlots: config.HypervisorConfig.MemSlots, + Debug: config.HypervisorConfig.Debug, + EntropySource: config.HypervisorConfig.EntropySource, + SharedFS: config.HypervisorConfig.SharedFS, + VirtioFSDaemon: config.HypervisorConfig.VirtioFSDaemon, + + HotplugVFIOOnRootBus: config.HypervisorConfig.HotplugVFIOOnRootBus, + PCIeRootPort: config.HypervisorConfig.PCIeRootPort, + } +} + +func getExpectedImage(config oci.RuntimeConfig) ImageInfo { + return ImageInfo{ + Path: config.HypervisorConfig.ImagePath, + } +} + +func getExpectedKernel(config oci.RuntimeConfig) KernelInfo { + return KernelInfo{ + Path: config.HypervisorConfig.KernelPath, + Parameters: strings.Join(vc.SerializeParams(config.HypervisorConfig.KernelParams, "="), " "), + } +} + +func getExpectedRuntimeDetails(config oci.RuntimeConfig, configFile string) RuntimeInfo { + runtimePath, _ := os.Executable() + + runtimeVersionInfo := constructVersionInfo(version) + runtimeVersionInfo.Commit = commit + return RuntimeInfo{ + Version: RuntimeVersionInfo{ + Version: runtimeVersionInfo, + OCI: specs.Version, + }, + Config: RuntimeConfigInfo{ + Path: configFile, + }, + Path: runtimePath, + Debug: config.Debug, + Trace: config.Trace, + DisableNewNetNs: config.DisableNewNetNs, + } +} + +func getExpectedSettings(config oci.RuntimeConfig, tmpdir, configFile string) (EnvInfo, error) { + meta := getExpectedMetaInfo() + + runtime := getExpectedRuntimeDetails(config, configFile) + + proxy, err := getExpectedProxyDetails(config) + if err != nil { + return EnvInfo{}, err + } + + shim, err := getExpectedShimDetails(config) + if err != nil { + return EnvInfo{}, err + } + + agent, err := getExpectedAgentDetails(config) + if err != nil { + return EnvInfo{}, err + } + + host, err := getExpectedHostDetails(tmpdir) + if err != nil { + return EnvInfo{}, err + } + + netmon, err := getExpectedNetmonDetails(config) + if err != nil { + return EnvInfo{}, err + } + + hypervisor := getExpectedHypervisor(config) + kernel := getExpectedKernel(config) + image := getExpectedImage(config) + + env := EnvInfo{ + Meta: meta, + Runtime: runtime, + Hypervisor: hypervisor, + Image: image, + Kernel: kernel, + Proxy: proxy, + Shim: shim, + Agent: agent, + Host: host, + Netmon: netmon, + } + + return env, nil +} + +func getExpectedMetaInfo() MetaInfo { + return MetaInfo{ + Version: formatVersion, + } +} + +func TestEnvGetMetaInfo(t *testing.T) { + expectedMeta := getExpectedMetaInfo() + + meta := getMetaInfo() + + assert.Equal(t, expectedMeta, meta) +} + +func TestEnvGetHostInfo(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + expectedHostDetails, err := getExpectedHostDetails(tmpdir) + assert.NoError(t, err) + + host, err := getHostInfo() + assert.NoError(t, err) + + assert.Equal(t, expectedHostDetails, host) +} + +func TestEnvGetHostInfoNoProcCPUInfo(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + _, err = getExpectedHostDetails(tmpdir) + assert.NoError(t, err) + + err = os.Remove(procCPUInfo) + assert.NoError(t, err) + + _, err = getHostInfo() + assert.Error(t, err) +} + +func TestEnvGetHostInfoNoOSRelease(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + _, err = getExpectedHostDetails(tmpdir) + assert.NoError(t, err) + + err = os.Remove(osRelease) + assert.NoError(t, err) + + _, err = getHostInfo() + assert.NoError(t, err) +} + +func TestEnvGetHostInfoNoProcVersion(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + _, err = getExpectedHostDetails(tmpdir) + assert.NoError(t, err) + + err = os.Remove(procVersion) + assert.NoError(t, err) + + _, err = getHostInfo() + assert.Error(t, err) +} + +func TestEnvGetEnvInfo(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + // Run test twice to ensure the individual component debug+trace + // options are tested. + for _, toggle := range []bool{false, true} { + hypervisorDebug = toggle + enableVirtioFS = toggle + proxyDebug = toggle + runtimeDebug = toggle + runtimeTrace = toggle + shimDebug = toggle + agentDebug = toggle + agentTrace = toggle + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + expectedEnv, err := getExpectedSettings(config, tmpdir, configFile) + assert.NoError(t, err) + + env, err := getEnvInfo(configFile, config) + assert.NoError(t, err) + + assert.Equal(t, expectedEnv, env) + } +} + +func TestEnvGetEnvInfoNoHypervisorVersion(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + expectedEnv, err := getExpectedSettings(config, tmpdir, configFile) + assert.NoError(err) + + err = os.Remove(config.HypervisorConfig.HypervisorPath) + assert.NoError(err) + + expectedEnv.Hypervisor.Version = unknown + + env, err := getEnvInfo(configFile, config) + assert.NoError(err) + + assert.Equal(expectedEnv, env) +} + +func TestEnvGetEnvInfoShimError(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + config.ShimConfig = "invalid shim config" + + _, err = getEnvInfo(configFile, config) + assert.Error(err) +} + +func TestEnvGetEnvInfoAgentError(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + config.AgentConfig = "invalid agent config" + + _, err = getEnvInfo(configFile, config) + assert.Error(err) +} + +func TestEnvGetEnvInfoNoOSRelease(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + _, err = getExpectedSettings(config, tmpdir, configFile) + assert.NoError(t, err) + + err = os.Remove(osRelease) + assert.NoError(t, err) + + _, err = getEnvInfo(configFile, config) + assert.NoError(t, err) +} + +func TestEnvGetEnvInfoNoProcCPUInfo(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + _, err = getExpectedSettings(config, tmpdir, configFile) + assert.NoError(t, err) + + err = os.Remove(procCPUInfo) + assert.NoError(t, err) + + _, err = getEnvInfo(configFile, config) + assert.Error(t, err) +} + +func TestEnvGetEnvInfoNoProcVersion(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + _, err = getExpectedSettings(config, tmpdir, configFile) + assert.NoError(t, err) + + err = os.Remove(procVersion) + assert.NoError(t, err) + + _, err = getEnvInfo(configFile, config) + assert.Error(t, err) +} + +func TestEnvGetRuntimeInfo(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + expectedRuntime := getExpectedRuntimeDetails(config, configFile) + + runtime := getRuntimeInfo(configFile, config) + + assert.Equal(t, expectedRuntime, runtime) +} + +func TestEnvGetProxyInfo(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + expectedProxy, err := getExpectedProxyDetails(config) + assert.NoError(t, err) + + proxy := getProxyInfo(config) + assert.NoError(t, err) + + assert.Equal(t, expectedProxy, proxy) +} + +func TestEnvGetProxyInfoNoVersion(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + expectedProxy, err := getExpectedProxyDetails(config) + assert.NoError(t, err) + + // remove the proxy ensuring its version cannot be queried + err = os.Remove(config.ProxyConfig.Path) + assert.NoError(t, err) + + expectedProxy.Version = unknownVersionInfo + + proxy := getProxyInfo(config) + assert.NoError(t, err) + + assert.Equal(t, expectedProxy, proxy) +} + +func TestEnvGetNetmonInfo(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + expectedNetmon, err := getExpectedNetmonDetails(config) + assert.NoError(t, err) + + netmon := getNetmonInfo(config) + assert.NoError(t, err) + + assert.Equal(t, expectedNetmon, netmon) +} + +func TestEnvGetNetmonInfoNoVersion(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + expectedNetmon, err := getExpectedNetmonDetails(config) + assert.NoError(t, err) + + // remove the netmon ensuring its version cannot be queried + err = os.Remove(config.NetmonConfig.Path) + assert.NoError(t, err) + + expectedNetmon.Version = unknownVersionInfo + + netmon := getNetmonInfo(config) + assert.NoError(t, err) + + assert.Equal(t, expectedNetmon, netmon) +} + +func TestEnvGetShimInfo(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + expectedShim, err := getExpectedShimDetails(config) + assert.NoError(t, err) + + shim, err := getShimInfo(config) + assert.NoError(t, err) + + assert.Equal(t, expectedShim, shim) +} + +func TestEnvGetShimInfoNoVersion(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + expectedShim, err := getExpectedShimDetails(config) + assert.NoError(t, err) + + shimPath := expectedShim.Path + + // ensure querying the shim version fails + err = createFile(shimPath, `#!/bin/sh + exit 1`) + assert.NoError(t, err) + + expectedShim.Version = unknownVersionInfo + + shim, err := getShimInfo(config) + assert.NoError(t, err) + + assert.Equal(t, expectedShim, shim) +} + +func TestEnvGetShimInfoInvalidType(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + _, err = getExpectedShimDetails(config) + assert.NoError(t, err) + + config.ShimConfig = "foo" + _, err = getShimInfo(config) + assert.Error(t, err) +} + +func TestEnvGetAgentInfo(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + expectedAgent, err := getExpectedAgentDetails(config) + assert.NoError(t, err) + + agent, err := getAgentInfo(config) + assert.NoError(t, err) + + assert.Equal(t, expectedAgent, agent) + + agentConfig, ok := config.AgentConfig.(vc.KataAgentConfig) + assert.True(t, ok) + + agentConfig.Debug = true + config.AgentConfig = agentConfig + agent, err = getAgentInfo(config) + assert.NoError(t, err) + assert.True(t, agent.Debug) + + agentConfig.Trace = true + agentConfig.TraceMode = "traceMode" + agentConfig.TraceType = "traceType" + config.AgentConfig = agentConfig + agent, err = getAgentInfo(config) + assert.NoError(t, err) + assert.True(t, agent.Trace) + assert.Equal(t, agent.TraceMode, "traceMode") + assert.Equal(t, agent.TraceType, "traceType") + + config.AgentConfig = "I am the wrong type" + _, err = getAgentInfo(config) + assert.Error(t, err) +} + +func testEnvShowTOMLSettings(t *testing.T, tmpdir string, tmpfile *os.File) error { + + runtime := RuntimeInfo{} + + hypervisor := HypervisorInfo{ + Path: "/resolved/hypervisor/path", + MachineType: "hypervisor-machine-type", + } + + image := ImageInfo{ + Path: "/resolved/image/path", + } + + kernel := KernelInfo{ + Path: "/kernel/path", + Parameters: "foo=bar xyz", + } + + proxy := ProxyInfo{ + Type: "proxy-type", + Version: constructVersionInfo(testProxyVersion), + Path: "file:///proxy-url", + Debug: false, + } + + shim := ShimInfo{ + Type: "shim-type", + Version: constructVersionInfo(testShimVersion), + Path: "/resolved/shim/path", + } + + agent := AgentInfo{ + Type: "agent-type", + } + + expectedHostDetails, err := getExpectedHostDetails(tmpdir) + assert.NoError(t, err) + + env := EnvInfo{ + Runtime: runtime, + Hypervisor: hypervisor, + Image: image, + Kernel: kernel, + Proxy: proxy, + Shim: shim, + Agent: agent, + Host: expectedHostDetails, + } + + err = writeTOMLSettings(env, tmpfile) + if err != nil { + return err + } + + contents, err := katautils.GetFileContents(tmpfile.Name()) + assert.NoError(t, err) + + buf := new(bytes.Buffer) + encoder := toml.NewEncoder(buf) + err = encoder.Encode(env) + assert.NoError(t, err) + + expectedContents := buf.String() + + assert.Equal(t, expectedContents, contents) + + return nil +} + +func testEnvShowJSONSettings(t *testing.T, tmpdir string, tmpfile *os.File) error { + + runtime := RuntimeInfo{} + + hypervisor := HypervisorInfo{ + Path: "/resolved/hypervisor/path", + MachineType: "hypervisor-machine-type", + } + + image := ImageInfo{ + Path: "/resolved/image/path", + } + + kernel := KernelInfo{ + Path: "/kernel/path", + Parameters: "foo=bar xyz", + } + + proxy := ProxyInfo{ + Type: "proxy-type", + Version: constructVersionInfo(testProxyVersion), + Path: "file:///proxy-url", + Debug: false, + } + + shim := ShimInfo{ + Type: "shim-type", + Version: constructVersionInfo(testShimVersion), + Path: "/resolved/shim/path", + } + + agent := AgentInfo{ + Type: "agent-type", + } + + expectedHostDetails, err := getExpectedHostDetails(tmpdir) + assert.NoError(t, err) + + env := EnvInfo{ + Runtime: runtime, + Hypervisor: hypervisor, + Image: image, + Kernel: kernel, + Proxy: proxy, + Shim: shim, + Agent: agent, + Host: expectedHostDetails, + } + + err = writeJSONSettings(env, tmpfile) + if err != nil { + return err + } + + contents, err := katautils.GetFileContents(tmpfile.Name()) + assert.NoError(t, err) + + buf := new(bytes.Buffer) + encoder := json.NewEncoder(buf) + // Ensure we have the same human readable layout + encoder.SetIndent("", " ") + err = encoder.Encode(env) + assert.NoError(t, err) + + expectedContents := buf.String() + + assert.Equal(t, expectedContents, contents) + + return nil +} + +func TestEnvShowSettings(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + tmpfile, err := ioutil.TempFile("", "envShowSettings-") + assert.NoError(t, err) + defer os.Remove(tmpfile.Name()) + + err = testEnvShowTOMLSettings(t, tmpdir, tmpfile) + assert.NoError(t, err) + + // Reset the file to empty for next test + tmpfile.Truncate(0) + tmpfile.Seek(0, 0) + err = testEnvShowJSONSettings(t, tmpdir, tmpfile) + assert.NoError(t, err) +} + +func TestEnvShowSettingsInvalidFile(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + tmpfile, err := ioutil.TempFile("", "envShowSettings-") + assert.NoError(t, err) + defer os.Remove(tmpfile.Name()) + + // close the file + tmpfile.Close() + + err = testEnvShowTOMLSettings(t, tmpdir, tmpfile) + assert.Error(t, err) + + // Reset the file to empty for next test + tmpfile.Truncate(0) + tmpfile.Seek(0, 0) + err = testEnvShowJSONSettings(t, tmpdir, tmpfile) + assert.Error(t, err) +} + +func TestEnvHandleSettings(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + _, err = getExpectedSettings(config, tmpdir, configFile) + assert.NoError(t, err) + + set := flag.NewFlagSet("test", flag.ContinueOnError) + ctx := createCLIContext(set) + ctx.App.Name = "foo" + ctx.App.Metadata["configFile"] = configFile + ctx.App.Metadata["runtimeConfig"] = config + + tmpfile, err := ioutil.TempFile("", "") + assert.NoError(t, err) + defer os.Remove(tmpfile.Name()) + + err = handleSettings(tmpfile, ctx) + assert.NoError(t, err) + + var env EnvInfo + + _, err = toml.DecodeFile(tmpfile.Name(), &env) + assert.NoError(t, err) +} + +func TestEnvHandleSettingsInvalidShimConfig(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + _, err = getExpectedSettings(config, tmpdir, configFile) + assert.NoError(err) + + config.ShimConfig = "invalid shim config" + + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + ctx.App.Metadata["configFile"] = configFile + ctx.App.Metadata["runtimeConfig"] = config + + tmpfile, err := ioutil.TempFile("", "") + assert.NoError(err) + defer os.Remove(tmpfile.Name()) + + err = handleSettings(tmpfile, ctx) + assert.Error(err) +} + +func TestEnvHandleSettingsInvalidParams(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + configFile, _, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + ctx.App.Metadata["configFile"] = configFile + + err = handleSettings(nil, ctx) + assert.Error(err) +} + +func TestEnvHandleSettingsEmptyMap(t *testing.T) { + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + ctx.App.Metadata = map[string]interface{}{} + err := handleSettings(os.Stdout, ctx) + assert.Error(t, err) +} + +func TestEnvHandleSettingsInvalidFile(t *testing.T) { + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + ctx.App.Metadata["configFile"] = "foo" + ctx.App.Metadata["runtimeConfig"] = oci.RuntimeConfig{} + + err := handleSettings(nil, ctx) + assert.Error(t, err) +} + +func TestEnvHandleSettingsInvalidConfigFileType(t *testing.T) { + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + ctx.App.Metadata["configFile"] = 123 + ctx.App.Metadata["runtimeConfig"] = oci.RuntimeConfig{} + + err := handleSettings(os.Stderr, ctx) + assert.Error(t, err) +} + +func TestEnvHandleSettingsInvalidRuntimeConfigType(t *testing.T) { + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + ctx.App.Metadata["configFile"] = "/some/where" + ctx.App.Metadata["runtimeConfig"] = true + + err := handleSettings(os.Stderr, ctx) + assert.Error(t, err) +} + +func TestEnvCLIFunction(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + _, err = getExpectedSettings(config, tmpdir, configFile) + assert.NoError(t, err) + + app := cli.NewApp() + set := flag.NewFlagSet("test", flag.ContinueOnError) + ctx := createCLIContextWithApp(set, app) + app.Name = "foo" + + ctx.App.Metadata["configFile"] = configFile + ctx.App.Metadata["runtimeConfig"] = config + + fn, ok := kataEnvCLICommand.Action.(func(context *cli.Context) error) + assert.True(t, ok) + + devNull, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0666) + assert.NoError(t, err) + + // throw away output + savedOutputFile := defaultOutputFile + defaultOutputFile = devNull + + defer func() { + defaultOutputFile = savedOutputFile + }() + + err = fn(ctx) + assert.NoError(t, err) + + set.Bool("json", true, "") + ctx = createCLIContextWithApp(set, app) + + err = fn(ctx) + assert.NoError(t, err) +} + +func TestEnvCLIFunctionFail(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + configFile, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(t, err) + + _, err = getExpectedSettings(config, tmpdir, configFile) + assert.NoError(t, err) + + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + + ctx.App.Metadata["configFile"] = configFile + ctx.App.Metadata["runtimeConfig"] = config + + fn, ok := kataEnvCLICommand.Action.(func(context *cli.Context) error) + assert.True(t, ok) + + savedOutputFile := defaultOutputFile + // invalidate + defaultOutputFile = nil + + defer func() { + defaultOutputFile = savedOutputFile + }() + + err = fn(ctx) + assert.Error(t, err) +} + +func TestGetHypervisorInfo(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + _, config, err := makeRuntimeConfig(tmpdir) + assert.NoError(err) + + info := getHypervisorInfo(config) + assert.Equal(info.Version, testHypervisorVersion) + + err = os.Remove(config.HypervisorConfig.HypervisorPath) + assert.NoError(err) + + info = getHypervisorInfo(config) + assert.Equal(info.Version, unknown) +} diff --git a/src/runtime/cli/kata-overhead.go b/src/runtime/cli/kata-overhead.go new file mode 100644 index 000000000..821415215 --- /dev/null +++ b/src/runtime/cli/kata-overhead.go @@ -0,0 +1,136 @@ +// +build cgo,linux +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "fmt" + "time" + + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/kata-containers/runtime/virtcontainers/types" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var kataOverheadCLICommand = cli.Command{ + Name: "kata-overhead", + Usage: "provides kata overhead at sandbox level", + ArgsUsage: ` [sandbox-id...] + + is your name for the instance of the sandbox.`, + + Description: `The kata-overhead command shows the overhead of a running Kata sandbox. Overhead + is calculated as the sum of pod resource utilization as measured on host cgroup minus the total + container usage measured inside the Kata guest for each container's cgroup.`, + + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + args := context.Args() + if !args.Present() { + return fmt.Errorf("Missing container ID, should at least provide one") + } + + for _, cID := range []string(args) { + if err := overhead(ctx, cID); err != nil { + return err + } + } + + return nil + }, +} + +func overhead(ctx context.Context, containerID string) error { + span, _ := katautils.Trace(ctx, "overhead") + defer span.Finish() + + kataLog = kataLog.WithField("container", containerID) + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + + status, sandboxID, err := getExistingContainerInfo(ctx, containerID) + if err != nil { + return err + } + + containerID = status.ID + + kataLog = kataLog.WithFields(logrus.Fields{ + "container": containerID, + "sandbox": sandboxID, + }) + + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + span.SetTag("sandbox", sandboxID) + + if status.State.State == types.StateStopped { + return fmt.Errorf("container with id %s is not running", status.ID) + } + + initTime := time.Now().UnixNano() + + initialSandboxStats, initialContainerStats, err := vci.StatsSandbox(ctx, sandboxID) + if err != nil { + return err + } + + hostInitCPU := initialSandboxStats.CgroupStats.CPUStats.CPUUsage.TotalUsage + guestInitCPU := uint64(0) + for _, cs := range initialContainerStats { + guestInitCPU += cs.CgroupStats.CPUStats.CPUUsage.TotalUsage + } + + // Wait for 1 second to calculate CPU usage + time.Sleep(time.Second * 1) + finishtTime := time.Now().UnixNano() + + finishSandboxStats, finishContainersStats, err := vci.StatsSandbox(ctx, sandboxID) + if err != nil { + return err + } + + hostFinalCPU := finishSandboxStats.CgroupStats.CPUStats.CPUUsage.TotalUsage + guestFinalCPU := uint64(0) + for _, cs := range finishContainersStats { + guestFinalCPU += cs.CgroupStats.CPUStats.CPUUsage.TotalUsage + } + + var guestMemoryUsage uint64 + for _, cs := range finishContainersStats { + guestMemoryUsage += cs.CgroupStats.MemoryStats.Usage.Usage + } + + hostMemoryUsage := finishSandboxStats.CgroupStats.MemoryStats.Usage.Usage + deltaTime := finishtTime - initTime + + cpuUsageGuest := float64(guestFinalCPU-guestInitCPU) / float64(deltaTime) * 100 + cpuUsageHost := float64(hostFinalCPU-hostInitCPU) / float64(deltaTime) * 100 + + fmt.Printf("Sandbox overhead for container: %s\n", containerID) + fmt.Printf("cpu_overhead=%f\n", cpuUsageHost-cpuUsageGuest) + fmt.Printf("memory_overhead_bytes=%d\n\n", hostMemoryUsage-guestMemoryUsage) + fmt.Printf(" --CPU details--\n") + fmt.Printf("cpu_host=%f\n", cpuUsageHost) + fmt.Printf("\tcpu_host_init=%d\n", hostInitCPU) + fmt.Printf("\tcpu_host_final=%d\n", hostFinalCPU) + fmt.Printf("cpu_guest=%f\n", cpuUsageGuest) + fmt.Printf("\tcpu_guest_init=%d\n", guestInitCPU) + fmt.Printf("\tcpu_guest_final=%d\n", guestFinalCPU) + fmt.Printf("Number of available vCPUs=%d\n", finishSandboxStats.Cpus) + fmt.Printf(" --Memory details--\n") + fmt.Printf("memory_host_bytes=%d\n", hostMemoryUsage) + fmt.Printf("memory_guest_bytes=%d\n\n", guestMemoryUsage) + + return nil +} diff --git a/src/runtime/cli/kill.go b/src/runtime/cli/kill.go new file mode 100644 index 000000000..60fa41e09 --- /dev/null +++ b/src/runtime/cli/kill.go @@ -0,0 +1,193 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "fmt" + "strconv" + "syscall" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/kata-containers/runtime/virtcontainers/types" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var killCLICommand = cli.Command{ + Name: "kill", + Usage: "Kill sends signals to the container's init process", + ArgsUsage: ` [signal] + + is the name for the instance of the container + [signal] is the signal to be sent to the init process (default: SIGTERM) + +EXAMPLE: + If the container id is "ubuntu01" the following will send a "KILL" signal + to the init process of the "ubuntu01" container: + + # ` + name + ` kill ubuntu01 KILL`, + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "all, a", + Usage: "send the specified signal to all processes inside the container", + }, + }, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + args := context.Args() + if !args.Present() { + return fmt.Errorf("Missing container ID") + } + + // If signal is provided, it has to be the second argument. + signal := args.Get(1) + if signal == "" { + signal = "SIGTERM" + } + + return kill(ctx, args.First(), signal, context.Bool("all")) + }, +} + +var signalList = map[string]syscall.Signal{ + "SIGABRT": syscall.SIGABRT, + "SIGALRM": syscall.SIGALRM, + "SIGBUS": syscall.SIGBUS, + "SIGCHLD": syscall.SIGCHLD, + "SIGCLD": syscall.SIGCLD, + "SIGCONT": syscall.SIGCONT, + "SIGFPE": syscall.SIGFPE, + "SIGHUP": syscall.SIGHUP, + "SIGILL": syscall.SIGILL, + "SIGINT": syscall.SIGINT, + "SIGIO": syscall.SIGIO, + "SIGIOT": syscall.SIGIOT, + "SIGKILL": syscall.SIGKILL, + "SIGPIPE": syscall.SIGPIPE, + "SIGPOLL": syscall.SIGPOLL, + "SIGPROF": syscall.SIGPROF, + "SIGPWR": syscall.SIGPWR, + "SIGQUIT": syscall.SIGQUIT, + "SIGSEGV": syscall.SIGSEGV, + "SIGSTKFLT": syscall.SIGSTKFLT, + "SIGSTOP": syscall.SIGSTOP, + "SIGSYS": syscall.SIGSYS, + "SIGTERM": syscall.SIGTERM, + "SIGTRAP": syscall.SIGTRAP, + "SIGTSTP": syscall.SIGTSTP, + "SIGTTIN": syscall.SIGTTIN, + "SIGTTOU": syscall.SIGTTOU, + "SIGUNUSED": syscall.SIGUNUSED, + "SIGURG": syscall.SIGURG, + "SIGUSR1": syscall.SIGUSR1, + "SIGUSR2": syscall.SIGUSR2, + "SIGVTALRM": syscall.SIGVTALRM, + "SIGWINCH": syscall.SIGWINCH, + "SIGXCPU": syscall.SIGXCPU, + "SIGXFSZ": syscall.SIGXFSZ, +} + +func kill(ctx context.Context, containerID, signal string, all bool) error { + span, _ := katautils.Trace(ctx, "kill") + defer span.Finish() + + kataLog = kataLog.WithField("container", containerID) + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + + // Checks the MUST and MUST NOT from OCI runtime specification + status, sandboxID, err := getExistingContainerInfo(ctx, containerID) + + if err != nil { + return err + } + + containerID = status.ID + + kataLog = kataLog.WithFields(logrus.Fields{ + "container": containerID, + "sandbox": sandboxID, + }) + + span.SetTag("container", containerID) + span.SetTag("sandbox", sandboxID) + + setExternalLoggers(ctx, kataLog) + + signum, err := processSignal(signal) + if err != nil { + return err + } + + kataLog.WithField("signal", signal).WithField("container state", status.State.State).Info("kill") + + // container MUST be created, running or paused + if status.State.State == types.StateReady || status.State.State == types.StateRunning || status.State.State == types.StatePaused { + if err := vci.KillContainer(ctx, sandboxID, containerID, signum, all); err != nil { + return err + } + } else if !all { + return fmt.Errorf("container not running") + } + + if signum != syscall.SIGKILL && signum != syscall.SIGTERM { + return nil + } + + containerType, err := oci.GetContainerType(status.Annotations) + if err != nil { + return err + } + + switch containerType { + case vc.PodSandbox: + _, err = vci.StopSandbox(ctx, sandboxID, signum == syscall.SIGKILL) + case vc.PodContainer: + _, err = vci.StopContainer(ctx, sandboxID, containerID) + default: + return fmt.Errorf("Invalid container type found") + } + + return err +} + +func processSignal(signal string) (syscall.Signal, error) { + signum, signalOk := signalList[signal] + if signalOk { + return signum, nil + } + + // Support for short name signals (INT) + signum, signalOk = signalList["SIG"+signal] + if signalOk { + return signum, nil + } + + // Support for numeric signals + s, err := strconv.Atoi(signal) + if err != nil { + return 0, fmt.Errorf("Failed to convert signal %s to int", signal) + } + + signum = syscall.Signal(s) + // Check whether signal is valid or not + for _, sig := range signalList { + if sig == signum { + // signal is a valid signal + return signum, nil + } + } + + return 0, fmt.Errorf("Signal %s is not supported", signal) +} diff --git a/src/runtime/cli/kill_test.go b/src/runtime/cli/kill_test.go new file mode 100644 index 000000000..7052b923b --- /dev/null +++ b/src/runtime/cli/kill_test.go @@ -0,0 +1,431 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "fmt" + "os" + "syscall" + "testing" + + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" + + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/kata-containers/runtime/virtcontainers/types" +) + +var ( + testKillContainerFuncReturnNil = func(ctx context.Context, sandboxID, containerID string, signal syscall.Signal, all bool) error { + return nil + } + + testStopContainerFuncReturnNil = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + return &vcmock.Container{}, nil + } + + testStopSandboxFuncReturnNil = func(ctx context.Context, sandboxID string, force bool) (vc.VCSandbox, error) { + return &vcmock.Sandbox{}, nil + } +) + +func TestProcessSignal(t *testing.T) { + tests := []struct { + signal string + valid bool + signum syscall.Signal + }{ + {"SIGDCKBY", false, 0}, //invalid signal + {"DCKBY", false, 0}, //invalid signal + {"99999", false, 0}, //invalid signal + {"SIGTERM", true, syscall.SIGTERM}, + {"TERM", true, syscall.SIGTERM}, + {"15", true, syscall.SIGTERM}, + } + + for _, test := range tests { + signum, err := processSignal(test.signal) + if signum != test.signum { + t.Fatalf("signal received: %d expected signal: %d\n", signum, test.signum) + } + if test.valid && err != nil { + t.Fatalf("signal %s is a valid but a error was received: %s\n", test.signal, err) + } + if !test.valid && err == nil { + t.Fatalf("signal %s is not a valid signal and no error was reported\n", test.signal) + } + } +} + +func testKillCLIFunctionTerminationSignalSuccessful(t *testing.T, sig string) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateRunning, + } + + annotations := map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + } + + testingImpl.KillContainerFunc = testKillContainerFuncReturnNil + testingImpl.StopContainerFunc = testStopContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil + } + + defer func() { + testingImpl.KillContainerFunc = nil + testingImpl.StopContainerFunc = nil + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID, sig}) + + execCLICommandFunc(assert, killCLICommand, set, false) + + annotations = map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + } + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil + } + + testingImpl.StopContainerFunc = nil + testingImpl.StopSandboxFunc = testStopSandboxFuncReturnNil + defer func() { + testingImpl.StopSandboxFunc = nil + }() + + execCLICommandFunc(assert, killCLICommand, set, false) +} + +func TestKillCLIFunctionSigkillSuccessful(t *testing.T) { + testKillCLIFunctionTerminationSignalSuccessful(t, "SIGKILL") +} + +func TestKillCLIFunctionSigtermSuccessful(t *testing.T) { + testKillCLIFunctionTerminationSignalSuccessful(t, "SIGTERM") +} + +func TestKillCLIFunctionNotTerminationSignalSuccessful(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateRunning, + } + + testingImpl.KillContainerFunc = testKillContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil + } + + defer func() { + testingImpl.KillContainerFunc = nil + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID, "SIGUSR1"}) + + execCLICommandFunc(assert, killCLICommand, set, false) +} + +func TestKillCLIFunctionNoSignalSuccessful(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateRunning, + } + + annotations := map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + } + + testingImpl.KillContainerFunc = testKillContainerFuncReturnNil + testingImpl.StopContainerFunc = testStopContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil + } + + defer func() { + testingImpl.KillContainerFunc = nil + testingImpl.StopContainerFunc = nil + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, killCLICommand, set, false) + + annotations = map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + } + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil + } + + testingImpl.StopContainerFunc = nil + testingImpl.StopSandboxFunc = testStopSandboxFuncReturnNil + defer func() { + testingImpl.StopSandboxFunc = nil + }() + + execCLICommandFunc(assert, killCLICommand, set, false) +} + +func TestKillCLIFunctionEnableAllSuccessful(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateRunning, + } + + annotations := map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + } + + testingImpl.KillContainerFunc = func(ctx context.Context, sandboxID, containerID string, signal syscall.Signal, all bool) error { + if !all { + return fmt.Errorf("Expecting -all flag = true, Got false") + } + + return nil + } + testingImpl.StopContainerFunc = testStopContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil + } + + defer func() { + testingImpl.KillContainerFunc = nil + testingImpl.StopContainerFunc = nil + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Bool("all", true, "") + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, killCLICommand, set, false) + + annotations = map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + } + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, annotations, &specs.Spec{}), nil + } + + testingImpl.StopContainerFunc = nil + testingImpl.StopSandboxFunc = testStopSandboxFuncReturnNil + defer func() { + testingImpl.StopSandboxFunc = nil + }() + + execCLICommandFunc(assert, killCLICommand, set, false) +} + +func TestKillCLIFunctionNoContainerIDFailure(t *testing.T) { + assert := assert.New(t) + + set := flag.NewFlagSet("", 0) + + execCLICommandFunc(assert, killCLICommand, set, true) +} + +func TestKillCLIFunctionContainerNotExistFailure(t *testing.T) { + assert := assert.New(t) + + testingImpl.KillContainerFunc = testKillContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{}, nil + } + + defer func() { + testingImpl.KillContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, killCLICommand, set, true) +} + +func TestKillCLIFunctionInvalidSignalFailure(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateRunning, + } + + testingImpl.KillContainerFunc = testKillContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil + } + + defer func() { + testingImpl.KillContainerFunc = nil + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID, "SIGINVALID"}) + + execCLICommandFunc(assert, killCLICommand, set, true) +} + +func TestKillCLIFunctionStatePausedSuccessful(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StatePaused, + } + + testingImpl.KillContainerFunc = testKillContainerFuncReturnNil + testingImpl.StopContainerFunc = testStopContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, + map[string]string{string(vcAnnotations.ContainerTypeKey): string(vc.PodContainer)}, &specs.Spec{}), nil + } + + defer func() { + testingImpl.KillContainerFunc = nil + testingImpl.StatusContainerFunc = nil + testingImpl.StopContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, killCLICommand, set, false) +} + +func TestKillCLIFunctionInvalidStateStoppedFailure(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateStopped, + } + + testingImpl.KillContainerFunc = testKillContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil + } + + defer func() { + testingImpl.KillContainerFunc = nil + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, killCLICommand, set, true) +} + +func TestKillCLIFunctionKillContainerFailure(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateRunning, + } + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, killCLICommand, set, true) +} + +func TestKillCLIFunctionInvalidStateStoppedAllSuccess(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateStopped, + } + + testingImpl.KillContainerFunc = testKillContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil + } + + defer func() { + testingImpl.KillContainerFunc = nil + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + var all bool + set.BoolVar(&all, "all", false, "") + set.Parse([]string{"-all", testContainerID, "10"}) + + execCLICommandFunc(assert, killCLICommand, set, false) +} diff --git a/src/runtime/cli/list.go b/src/runtime/cli/list.go new file mode 100644 index 000000000..c85024bb6 --- /dev/null +++ b/src/runtime/cli/list.go @@ -0,0 +1,394 @@ +// Copyright (c) 2014,2015,2016,2017 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "os" + "strings" + "syscall" + "text/tabwriter" + "time" + + "github.com/urfave/cli" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + oci "github.com/kata-containers/runtime/virtcontainers/pkg/oci" +) + +const formatOptions = `table or json` + +// containerState represents the platform agnostic pieces relating to a +// running container's status and state +type containerState struct { + // Version is the OCI version for the container + Version string `json:"ociVersion"` + // ID is the container ID + ID string `json:"id"` + // InitProcessPid is the init process id in the parent namespace + InitProcessPid int `json:"pid"` + // Status is the current status of the container, running, paused, ... + Status string `json:"status"` + // Bundle is the path on the filesystem to the bundle + Bundle string `json:"bundle"` + // Rootfs is a path to a directory containing the container's root filesystem. + Rootfs string `json:"rootfs"` + // Created is the unix timestamp for the creation time of the container in UTC + Created time.Time `json:"created"` + // Annotations is the user defined annotations added to the config. + Annotations map[string]string `json:"annotations,omitempty"` + // The owner of the state directory (the owner of the container). + Owner string `json:"owner"` +} + +type asset struct { + Path string `json:"path"` + Custom bool `json:"bool"` +} + +// hypervisorDetails stores details of the hypervisor used to host +// the container +type hypervisorDetails struct { + HypervisorAsset asset `json:"hypervisorAsset"` + ImageAsset asset `json:"imageAsset"` + KernelAsset asset `json:"kernelAsset"` +} + +// fullContainerState specifies the core state plus the hypervisor +// details +type fullContainerState struct { + containerState + CurrentHypervisorDetails hypervisorDetails `json:"currentHypervisor"` + LatestHypervisorDetails hypervisorDetails `json:"latestHypervisor"` + StaleAssets []string +} + +type formatState interface { + Write(state []fullContainerState, showAll bool, file *os.File) error +} + +type formatJSON struct{} +type formatIDList struct{} +type formatTabular struct{} + +var listCLICommand = cli.Command{ + Name: "list", + Usage: "lists containers started by " + name + " with the given root", + ArgsUsage: ` + +Where the given root is specified via the global option "--root" +(default: "` + defaultRootDirectory + `"). + +EXAMPLE 1: +To list containers created via the default "--root": + # ` + name + ` list + +EXAMPLE 2: +To list containers created using a non-default value for "--root": + # ` + name + ` --root value list`, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "format, f", + Value: "table", + Usage: `select one of: ` + formatOptions, + }, + cli.BoolFlag{ + Name: "quiet, q", + Usage: "display only container IDs", + }, + cli.BoolFlag{ + Name: "kata-all", + Usage: "display all available " + project + " information", + }, + }, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + span, ctx := katautils.Trace(ctx, "list") + defer span.Finish() + + s, err := getContainers(ctx, context) + if err != nil { + return err + } + + file := defaultOutputFile + showAll := context.Bool("kata-all") + + var fs formatState = formatIDList{} + + if context.Bool("quiet") { + fs = formatIDList{} + } else { + + switch context.String("format") { + case "table": + fs = formatTabular{} + + case "json": + fs = formatJSON{} + + default: + return fmt.Errorf("invalid format option") + } + } + + return fs.Write(s, showAll, file) + }, +} + +// getStaleAssetsreturns compares the two specified hypervisorDetails objects +// and returns a list of strings representing which assets in "old" are not +// current compared to "new". If old and new are identical, the empty string +// will be returned. +// +// Notes: +// +// - This function is trivial because it relies upon the fact that new +// containers are always created with the latest versions of all assets. +// +// - WARNING: Since this function only compares local values, it is unable to +// determine if newer (remote) assets are available. +func getStaleAssets(old, new hypervisorDetails) []string { + var stale []string + + if old.KernelAsset.Path != new.KernelAsset.Path { + if old.KernelAsset.Custom { + // The workload kernel asset is a custom one, i.e. it's not coming + // from the runtime configuration file. Thus it does not make sense + // to compare it against the configured kernel asset. + // We assume a custom kernel asset has been updated if the + // corresponding path no longer exists, i.e. it's been replaced by + // a new kernel, e.g. with a new version name. + // Replacing a custom kernel asset binary with exactly the same + // binary name won't allow us to detect if it's staled or not. + if _, err := os.Stat(old.KernelAsset.Path); os.IsNotExist(err) { + stale = append(stale, "kernel") + } + } else { + stale = append(stale, "kernel") + } + } + + if old.ImageAsset.Path != new.ImageAsset.Path { + if old.ImageAsset.Custom { + // The workload image asset is a custom one, i.e. it's not coming + // from the runtime configuration file. Thus it does not make sense + // to compare it against the configured image asset. + // We assume a custom image asset has been updated if the + // corresponding path no longer exists, i.e. it's been replaced by + // a new image, e.g. with a new version name. + // Replacing a custom image asset binary with exactly the same + // binary name won't allow us to detect if it's staled or not. + if _, err := os.Stat(old.ImageAsset.Path); os.IsNotExist(err) { + stale = append(stale, "image") + } + } else { + stale = append(stale, "image") + } + } + + return stale +} + +func (f formatIDList) Write(state []fullContainerState, showAll bool, file *os.File) error { + for _, item := range state { + _, err := fmt.Fprintln(file, item.ID) + if err != nil { + return err + } + } + + return nil +} + +func (f formatTabular) Write(state []fullContainerState, showAll bool, file *os.File) error { + // values used by runc + flags := uint(0) + minWidth := 12 + tabWidth := 1 + padding := 3 + + w := tabwriter.NewWriter(file, minWidth, tabWidth, padding, ' ', flags) + + fmt.Fprint(w, "ID\tPID\tSTATUS\tBUNDLE\tCREATED\tOWNER") + + if showAll { + fmt.Fprint(w, "\tHYPERVISOR\tKERNEL\tIMAGE\tLATEST-KERNEL\tLATEST-IMAGE\tSTALE\n") + } else { + fmt.Fprintf(w, "\n") + } + + for _, item := range state { + fmt.Fprintf(w, "%s\t%d\t%s\t%s\t%s\t%s", + item.ID, + item.InitProcessPid, + item.Status, + item.Bundle, + item.Created.Format(time.RFC3339Nano), + item.Owner) + + if showAll { + stale := strings.Join(item.StaleAssets, ",") + if stale == "" { + stale = "-" + } + + current := item.CurrentHypervisorDetails + latest := item.LatestHypervisorDetails + + all := fmt.Sprintf("\t%s\t%s\t%s", + current.HypervisorAsset.Path, + current.KernelAsset.Path, + current.ImageAsset.Path) + + if !current.KernelAsset.Custom { + all += fmt.Sprintf("\t%s", latest.KernelAsset.Path) + } else { + all += fmt.Sprintf("\t%s", current.KernelAsset.Path) + } + + if !current.ImageAsset.Custom { + all += fmt.Sprintf("\t%s", latest.ImageAsset.Path) + } else { + all += fmt.Sprintf("\t%s", current.ImageAsset.Path) + } + + all += fmt.Sprintf("\t%s\n", stale) + + fmt.Fprint(w, all) + } else { + fmt.Fprint(w, "\n") + } + } + + return w.Flush() +} + +func (f formatJSON) Write(state []fullContainerState, showAll bool, file *os.File) error { + return json.NewEncoder(file).Encode(state) +} + +// getDirOwner returns the UID of the specified directory +func getDirOwner(dir string) (uint32, error) { + if dir == "" { + return 0, errors.New("BUG: need directory") + } + st, err := os.Stat(dir) + if err != nil { + return 0, err + } + + if !st.IsDir() { + return 0, fmt.Errorf("%q is not a directory", dir) + } + + statType, ok := st.Sys().(*syscall.Stat_t) + if !ok { + return 0, fmt.Errorf("cannot convert %+v to stat type for directory %q", st, dir) + } + + return statType.Uid, nil +} + +func getContainers(ctx context.Context, context *cli.Context) ([]fullContainerState, error) { + runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig) + if !ok { + return nil, errors.New("invalid runtime config") + } + + latestHypervisorDetails := getHypervisorDetails(&runtimeConfig.HypervisorConfig) + + sandboxList, err := vci.ListSandbox(ctx) + if err != nil { + return nil, err + } + + var s []fullContainerState + + for _, sandbox := range sandboxList { + if len(sandbox.ContainersStatus) == 0 { + // ignore empty sandboxes + continue + } + + currentHypervisorDetails := getHypervisorDetails(&sandbox.HypervisorConfig) + + for _, container := range sandbox.ContainersStatus { + ociState := oci.StatusToOCIState(container) + staleAssets := getStaleAssets(currentHypervisorDetails, latestHypervisorDetails) + + uid, err := getDirOwner(container.RootFs) + if err != nil { + fmt.Fprintf(os.Stderr, "WARNING: failed to get container %s rootfs: %s\n", ociState.ID, err) + continue + } + + owner := fmt.Sprintf("#%v", uid) + + s = append(s, fullContainerState{ + containerState: containerState{ + Version: ociState.Version, + ID: ociState.ID, + InitProcessPid: ociState.Pid, + Status: ociState.Status, + Bundle: ociState.Bundle, + Rootfs: container.RootFs, + Created: container.StartTime, + Annotations: ociState.Annotations, + Owner: owner, + }, + CurrentHypervisorDetails: currentHypervisorDetails, + LatestHypervisorDetails: latestHypervisorDetails, + StaleAssets: staleAssets, + }) + } + } + + return s, nil +} + +// getHypervisorDetails returns details of the latest version of the +// hypervisor and the associated assets. +func getHypervisorDetails(hypervisorConfig *vc.HypervisorConfig) hypervisorDetails { + hypervisorPath, err := hypervisorConfig.HypervisorAssetPath() + if err != nil { + hypervisorPath = hypervisorConfig.HypervisorPath + } + + kernelPath, err := hypervisorConfig.KernelAssetPath() + if err != nil { + kernelPath = hypervisorConfig.KernelPath + } + + imagePath, err := hypervisorConfig.ImageAssetPath() + if err != nil { + imagePath = hypervisorConfig.ImagePath + } + + return hypervisorDetails{ + HypervisorAsset: asset{ + Path: hypervisorPath, + Custom: hypervisorConfig.CustomHypervisorAsset(), + }, + KernelAsset: asset{ + Path: kernelPath, + Custom: hypervisorConfig.CustomKernelAsset(), + }, + ImageAsset: asset{ + Path: imagePath, + Custom: hypervisorConfig.CustomImageAsset(), + }, + } +} diff --git a/src/runtime/cli/list_test.go b/src/runtime/cli/list_test.go new file mode 100644 index 000000000..62a5a56b5 --- /dev/null +++ b/src/runtime/cli/list_test.go @@ -0,0 +1,785 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + "testing" + "time" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +type TestFileWriter struct { + Name string + File *os.File +} + +var hypervisorDetails1 = hypervisorDetails{ + HypervisorAsset: asset{ + Path: "/hypervisor/path", + }, + ImageAsset: asset{ + Path: "/image/path", + }, + KernelAsset: asset{ + Path: "/kernel/path", + }, +} + +var hypervisorDetails2 = hypervisorDetails{ + HypervisorAsset: asset{ + Path: "/hypervisor/path2", + }, + ImageAsset: asset{ + Path: "/image/path2", + }, + KernelAsset: asset{ + Path: "/kernel/path2", + }, +} + +var hypervisorDetails3 = hypervisorDetails{ + HypervisorAsset: asset{ + Path: "/hypervisor/path3", + }, + ImageAsset: asset{ + Path: "/image/path3", + }, + KernelAsset: asset{ + Path: "/kernel/path3", + }, +} + +var testStatuses = []fullContainerState{ + { + containerState: containerState{ + Version: "", + ID: "1", + InitProcessPid: 1234, + Status: "running", + Bundle: "/somewhere/over/the/rainbow", + Created: time.Now().UTC(), + Annotations: map[string]string(nil), + Owner: "#0", + }, + + CurrentHypervisorDetails: hypervisorDetails1, + LatestHypervisorDetails: hypervisorDetails1, + StaleAssets: []string{}, + }, + { + containerState: containerState{ + Version: "", + ID: "2", + InitProcessPid: 2345, + Status: "stopped", + Bundle: "/this/path/is/invalid", + Created: time.Now().UTC(), + Annotations: map[string]string(nil), + Owner: "#0", + }, + + CurrentHypervisorDetails: hypervisorDetails2, + LatestHypervisorDetails: hypervisorDetails2, + StaleAssets: []string{}, + }, + { + containerState: containerState{ + Version: "", + ID: "3", + InitProcessPid: 9999, + Status: "ready", + Bundle: "/foo/bar/baz", + Created: time.Now().UTC(), + Annotations: map[string]string(nil), + Owner: "#0", + }, + + CurrentHypervisorDetails: hypervisorDetails3, + LatestHypervisorDetails: hypervisorDetails3, + StaleAssets: []string{}, + }, +} + +// Implement the io.Writer interface +func (w *TestFileWriter) Write(bytes []byte) (n int, err error) { + return w.File.Write(bytes) +} + +func formatListDataAsBytes(formatter formatState, state []fullContainerState, showAll bool) (bytes []byte, err error) { + tmpfile, err := ioutil.TempFile("", "formatListData-") + if err != nil { + return nil, err + } + + defer os.Remove(tmpfile.Name()) + + err = formatter.Write(state, showAll, tmpfile) + if err != nil { + return nil, err + } + + tmpfile.Close() + + return ioutil.ReadFile(tmpfile.Name()) +} + +func formatListDataAsString(formatter formatState, state []fullContainerState, showAll bool) (lines []string, err error) { + bytes, err := formatListDataAsBytes(formatter, state, showAll) + if err != nil { + return nil, err + } + + lines = strings.Split(string(bytes), "\n") + + // Remove last line if empty + length := len(lines) + last := lines[length-1] + if last == "" { + lines = lines[:length-1] + } + + return lines, nil +} + +func TestStateToIDList(t *testing.T) { + + // no header + expectedLength := len(testStatuses) + + // showAll should not affect the output + for _, showAll := range []bool{true, false} { + lines, err := formatListDataAsString(&formatIDList{}, testStatuses, showAll) + if err != nil { + t.Fatal(err) + } + + var expected []string + for _, s := range testStatuses { + expected = append(expected, s.ID) + } + + length := len(lines) + + if length != expectedLength { + t.Fatalf("Expected %d lines, got %d: %v", expectedLength, length, lines) + } + + assert.Equal(t, lines, expected, "lines + expected") + } +} + +func TestStateToTabular(t *testing.T) { + // +1 for header line + expectedLength := len(testStatuses) + 1 + + expectedDefaultHeaderPattern := `\AID\s+PID\s+STATUS\s+BUNDLE\s+CREATED\s+OWNER` + expectedExtendedHeaderPattern := `HYPERVISOR\s+KERNEL\s+IMAGE\s+LATEST-KERNEL\s+LATEST-IMAGE\s+STALE` + endingPattern := `\s*\z` + + lines, err := formatListDataAsString(&formatTabular{}, testStatuses, false) + if err != nil { + t.Fatal(err) + } + + length := len(lines) + + expectedHeaderPattern := expectedDefaultHeaderPattern + endingPattern + expectedHeaderRE := regexp.MustCompile(expectedHeaderPattern) + + if length != expectedLength { + t.Fatalf("Expected %d lines, got %d", expectedLength, length) + } + + header := lines[0] + + matches := expectedHeaderRE.FindAllStringSubmatch(header, -1) + if matches == nil { + t.Fatalf("Header line failed to match:\n"+ + "pattern : %v\n"+ + "line : %v\n", + expectedDefaultHeaderPattern, + header) + } + + for i, status := range testStatuses { + lineIndex := i + 1 + line := lines[lineIndex] + + expectedLinePattern := fmt.Sprintf(`\A%s\s+%d\s+%s\s+%s\s+%s\s+%s\s*\z`, + regexp.QuoteMeta(status.ID), + status.InitProcessPid, + regexp.QuoteMeta(status.Status), + regexp.QuoteMeta(status.Bundle), + regexp.QuoteMeta(status.Created.Format(time.RFC3339Nano)), + regexp.QuoteMeta(status.Owner)) + + expectedLineRE := regexp.MustCompile(expectedLinePattern) + + matches := expectedLineRE.FindAllStringSubmatch(line, -1) + if matches == nil { + t.Fatalf("Data line failed to match:\n"+ + "pattern : %v\n"+ + "line : %v\n", + expectedLinePattern, + line) + } + } + + // Try again with full details this time + lines, err = formatListDataAsString(&formatTabular{}, testStatuses, true) + if err != nil { + t.Fatal(err) + } + + length = len(lines) + + expectedHeaderPattern = expectedDefaultHeaderPattern + `\s+` + expectedExtendedHeaderPattern + endingPattern + expectedHeaderRE = regexp.MustCompile(expectedHeaderPattern) + + if length != expectedLength { + t.Fatalf("Expected %d lines, got %d", expectedLength, length) + } + + header = lines[0] + + matches = expectedHeaderRE.FindAllStringSubmatch(header, -1) + if matches == nil { + t.Fatalf("Header line failed to match:\n"+ + "pattern : %v\n"+ + "line : %v\n", + expectedDefaultHeaderPattern, + header) + } + + for i, status := range testStatuses { + lineIndex := i + 1 + line := lines[lineIndex] + + expectedLinePattern := fmt.Sprintf(`\A%s\s+%d\s+%s\s+%s\s+%s\s+%s\s+%s\s+%s\s+%s\s+%s\s+%s\s+%s\s*\z`, + regexp.QuoteMeta(status.ID), + status.InitProcessPid, + regexp.QuoteMeta(status.Status), + regexp.QuoteMeta(status.Bundle), + regexp.QuoteMeta(status.Created.Format(time.RFC3339Nano)), + regexp.QuoteMeta(status.Owner), + regexp.QuoteMeta(status.CurrentHypervisorDetails.HypervisorAsset.Path), + regexp.QuoteMeta(status.CurrentHypervisorDetails.KernelAsset.Path), + regexp.QuoteMeta(status.CurrentHypervisorDetails.ImageAsset.Path), + regexp.QuoteMeta(status.LatestHypervisorDetails.KernelAsset.Path), + regexp.QuoteMeta(status.LatestHypervisorDetails.ImageAsset.Path), + regexp.QuoteMeta("-")) + + expectedLineRE := regexp.MustCompile(expectedLinePattern) + + matches := expectedLineRE.FindAllStringSubmatch(line, -1) + if matches == nil { + t.Fatalf("Data line failed to match:\n"+ + "pattern : %v\n"+ + "line : %v\n", + expectedLinePattern, + line) + } + } +} + +func TestStateToJSON(t *testing.T) { + expectedLength := len(testStatuses) + + // showAll should not affect the output + for _, showAll := range []bool{true, false} { + bytes, err := formatListDataAsBytes(&formatJSON{}, testStatuses, showAll) + if err != nil { + t.Fatal(err) + } + + // Force capacity to match the original otherwise assert.Equal() complains. + states := make([]fullContainerState, 0, len(testStatuses)) + + err = json.Unmarshal(bytes, &states) + if err != nil { + t.Fatal(err) + } + + length := len(states) + + if length != expectedLength { + t.Fatalf("Expected %d lines, got %d", expectedLength, length) + } + + // golang tip (what will presumably become v1.9) now + // stores a monotonic clock value as part of time.Time's + // internal representation (this is shown by a suffix in + // the form "m=±ddd.nnnnnnnnn" when calling String() on + // the time.Time object). However, this monotonic value + // is stripped out when marshaling. + // + // This behaviour change makes comparing the original + // object and the marshaled-and-then-unmarshaled copy of + // the object doomed to failure. + // + // The solution? Manually strip the monotonic time out + // of the original before comparison (yuck!) + // + // See: + // + // - https://go-review.googlesource.com/c/36255/7/src/time/time.go#54 + // + for i := 0; i < expectedLength; i++ { + // remove monotonic time part + testStatuses[i].Created = testStatuses[i].Created.Truncate(0) + } + + assert.Equal(t, states, testStatuses, "states + testStatuses") + } +} + +func TestListCLIFunctionNoContainers(t *testing.T) { + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + ctx.App.Metadata["foo"] = "bar" + + fn, ok := listCLICommand.Action.(func(context *cli.Context) error) + assert.True(t, ok) + + err := fn(ctx) + + // no config in the Metadata + assert.Error(t, err) +} + +func TestListGetContainersListSandboxFail(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + ctx.App.Metadata = map[string]interface{}{ + "runtimeConfig": runtimeConfig, + } + + _, err = getContainers(context.Background(), ctx) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) +} + +func TestListGetContainers(t *testing.T) { + assert := assert.New(t) + + testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) { + // No pre-existing sandboxes + return []vc.SandboxStatus{}, nil + } + + defer func() { + testingImpl.ListSandboxFunc = nil + }() + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + ctx.App.Metadata = map[string]interface{}{ + "runtimeConfig": runtimeConfig, + } + + state, err := getContainers(context.Background(), ctx) + assert.NoError(err) + assert.Equal(state, []fullContainerState(nil)) +} + +func TestListGetContainersSandboxWithoutContainers(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) { + return []vc.SandboxStatus{ + { + ID: sandbox.ID(), + ContainersStatus: []vc.ContainerStatus(nil), + }, + }, nil + } + + defer func() { + testingImpl.ListSandboxFunc = nil + }() + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + ctx.App.Metadata = map[string]interface{}{ + "runtimeConfig": runtimeConfig, + } + + state, err := getContainers(context.Background(), ctx) + assert.NoError(err) + assert.Equal(state, []fullContainerState(nil)) +} + +func TestListGetContainersSandboxWithContainer(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + rootfs := filepath.Join(tmpdir, "rootfs") + err = os.MkdirAll(rootfs, testDirMode) + assert.NoError(err) + + testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) { + return []vc.SandboxStatus{ + { + ID: sandbox.ID(), + ContainersStatus: []vc.ContainerStatus{ + { + ID: sandbox.ID(), + Annotations: map[string]string{}, + RootFs: rootfs, + }, + }, + }, + }, nil + } + + defer func() { + testingImpl.ListSandboxFunc = nil + }() + + ctx := createCLIContext(nil) + ctx.App.Name = "foo" + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + + _, err = getContainers(context.Background(), ctx) + assert.NoError(err) +} + +func TestListCLIFunctionFormatFail(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + quietFlags := flag.NewFlagSet("test", 0) + quietFlags.Bool("quiet", true, "") + + tableFlags := flag.NewFlagSet("test", 0) + tableFlags.String("format", "table", "") + + jsonFlags := flag.NewFlagSet("test", 0) + jsonFlags.String("format", "json", "") + + invalidFlags := flag.NewFlagSet("test", 0) + invalidFlags.String("format", "not-a-valid-format", "") + + type testData struct { + format string + flags *flag.FlagSet + } + + data := []testData{ + {"quiet", quietFlags}, + {"table", tableFlags}, + {"json", jsonFlags}, + {"invalid", invalidFlags}, + } + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + rootfs := filepath.Join(tmpdir, "rootfs") + + testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) { + return []vc.SandboxStatus{ + { + ID: sandbox.ID(), + ContainersStatus: []vc.ContainerStatus{ + { + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + }, + RootFs: rootfs, + }, + }, + }, + }, nil + } + + defer func() { + testingImpl.ListSandboxFunc = nil + }() + + savedOutputFile := defaultOutputFile + defer func() { + defaultOutputFile = savedOutputFile + }() + + // purposely invalid + var invalidFile *os.File + + for _, d := range data { + // start off with an invalid output file + defaultOutputFile = invalidFile + + ctx := createCLIContext(d.flags) + ctx.App.Name = "foo" + ctx.App.Metadata["foo"] = "bar" + + fn, ok := listCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok, d) + + err = fn(ctx) + + // no config in the Metadata + assert.Error(err, d) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err, d) + + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + + err = os.MkdirAll(rootfs, testDirMode) + assert.NoError(err) + + err = fn(ctx) + + // invalid output file + assert.Error(err, d) + assert.False(vcmock.IsMockError(err), d) + + output := filepath.Join(tmpdir, "output") + f, err := os.OpenFile(output, os.O_WRONLY|os.O_CREATE, testFileMode) + assert.NoError(err) + defer f.Close() + + // output file is now valid + defaultOutputFile = f + + err = fn(ctx) + if d.format == "invalid" { + assert.Error(err) + assert.False(vcmock.IsMockError(err), d) + } else { + assert.NoError(err) + } + } +} + +func TestListCLIFunctionQuiet(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + rootfs := filepath.Join(tmpdir, "rootfs") + err = os.MkdirAll(rootfs, testDirMode) + assert.NoError(err) + + testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) { + return []vc.SandboxStatus{ + { + ID: sandbox.ID(), + ContainersStatus: []vc.ContainerStatus{ + { + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + }, + RootFs: rootfs, + }, + }, + }, + }, nil + } + + defer func() { + testingImpl.ListSandboxFunc = nil + }() + + set := flag.NewFlagSet("test", 0) + set.Bool("quiet", true, "") + + ctx := createCLIContext(set) + ctx.App.Name = "foo" + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + + savedOutputFile := defaultOutputFile + defer func() { + defaultOutputFile = savedOutputFile + }() + + output := filepath.Join(tmpdir, "output") + f, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY|os.O_SYNC, testFileMode) + assert.NoError(err) + defer f.Close() + + defaultOutputFile = f + + fn, ok := listCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + assert.NoError(err) + f.Close() + + text, err := katautils.GetFileContents(output) + assert.NoError(err) + + trimmed := strings.TrimSpace(text) + assert.Equal(testSandboxID, trimmed) +} + +func TestListGetDirOwner(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + _, err = getDirOwner("") + // invalid parameter + assert.Error(err) + + dir := filepath.Join(tmpdir, "dir") + + _, err = getDirOwner(dir) + // ENOENT + assert.Error(err) + + err = createEmptyFile(dir) + assert.NoError(err) + + _, err = getDirOwner(dir) + // wrong file type + assert.Error(err) + + err = os.Remove(dir) + assert.NoError(err) + + err = os.MkdirAll(dir, testDirMode) + assert.NoError(err) + + uid := uint32(os.Getuid()) + + dirUID, err := getDirOwner(dir) + assert.NoError(err) + assert.Equal(dirUID, uid) +} + +func TestListWithRootfsMissShouldSuccess(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + rootfs := filepath.Join(tmpdir, "rootfs") + err = os.MkdirAll(rootfs, testDirMode) + assert.NoError(err) + + testingImpl.ListSandboxFunc = func(ctx context.Context) ([]vc.SandboxStatus, error) { + return []vc.SandboxStatus{ + { + ID: sandbox.ID(), + ContainersStatus: []vc.ContainerStatus{ + { + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + }, + RootFs: rootfs, + }, + }, + }, + }, nil + } + + defer func() { + testingImpl.ListSandboxFunc = nil + }() + + set := flag.NewFlagSet("test", 0) + set.String("format", "table", "") + ctx := createCLIContext(set) + ctx.App.Name = "foo" + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + ctx.App.Metadata["runtimeConfig"] = runtimeConfig + + fn, ok := listCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + err = fn(ctx) + assert.NoError(err) + + // remove container rootfs, check list command should also work + assert.NoError(os.RemoveAll(rootfs)) + err = fn(ctx) + assert.NoError(err) +} diff --git a/src/runtime/cli/main.go b/src/runtime/cli/main.go new file mode 100644 index 000000000..df16c5f34 --- /dev/null +++ b/src/runtime/cli/main.go @@ -0,0 +1,606 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017-2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "errors" + "fmt" + "io" + "os" + "os/signal" + goruntime "runtime" + "strings" + "syscall" + + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/kata-containers/runtime/pkg/signals" + vc "github.com/kata-containers/runtime/virtcontainers" + exp "github.com/kata-containers/runtime/virtcontainers/experimental" + vf "github.com/kata-containers/runtime/virtcontainers/factory" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/kata-containers/runtime/virtcontainers/pkg/rootless" + specs "github.com/opencontainers/runtime-spec/specs-go" + opentracing "github.com/opentracing/opentracing-go" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +// specConfig is the name of the file holding the containers configuration +const specConfig = "config.json" + +// arch is the architecture for the running program +const arch = goruntime.GOARCH + +var usage = fmt.Sprintf(`%s runtime + +%s is a command line program for running applications packaged +according to the Open Container Initiative (OCI).`, name, name) + +var notes = fmt.Sprintf(` +NOTES: + +- Commands starting "%s-" and options starting "--%s-" are `+project+` extensions. + +URL: + + The canonical URL for this project is: %s + +`, projectPrefix, projectPrefix, projectURL) + +// kataLog is the logger used to record all messages +var kataLog *logrus.Entry + +// originalLoggerLevel is the default log level. It is used to revert the +// current log level back to its original value if debug output is not +// required. We set the default to 'Warn' for the runtime. +var originalLoggerLevel = logrus.WarnLevel + +var debug = false + +// if true, coredump when an internal error occurs or a fatal signal is received +var crashOnError = false + +// concrete virtcontainer implementation +var virtcontainersImpl = &vc.VCImpl{} + +// vci is used to access a particular virtcontainers implementation. +// Normally, it refers to the official package, but is re-assigned in +// the tests to allow virtcontainers to be mocked. +var vci vc.VC = virtcontainersImpl + +// defaultOutputFile is the default output file to write the gathered +// information to. +var defaultOutputFile = os.Stdout + +// defaultErrorFile is the default output file to write error +// messages to. +var defaultErrorFile = os.Stderr + +// runtimeFlags is the list of supported global command-line flags +var runtimeFlags = []cli.Flag{ + cli.StringFlag{ + Name: configFilePathOption, + Usage: project + " config file path", + }, + cli.StringFlag{ + Name: "log", + Value: "/dev/null", + Usage: "set the log file path where internal debug information is written", + }, + cli.StringFlag{ + Name: "log-format", + Value: "text", + Usage: "set the format used by logs ('text' (default), or 'json')", + }, + cli.StringFlag{ + Name: "root", + Value: defaultRootDirectory, + Usage: "root directory for storage of container state (this should be located in tmpfs)", + }, + cli.StringFlag{ + Name: "rootless", + Value: "auto", + Usage: "ignore cgroup permission errors ('true', 'false', or 'auto')", + }, + cli.BoolFlag{ + Name: showConfigPathsOption, + Usage: "show config file paths that will be checked for (in order)", + }, + cli.BoolFlag{ + Name: "systemd-cgroup", + Usage: "enable systemd cgroup support, expects cgroupsPath to be of form \"slice:prefix:name\" for e.g. \"system.slice:runc:434234\"", + }, +} + +// runtimeCommands is the list of supported command-line (sub-) +// commands. +var runtimeCommands = []cli.Command{ + createCLICommand, + deleteCLICommand, + execCLICommand, + killCLICommand, + listCLICommand, + pauseCLICommand, + psCLICommand, + resumeCLICommand, + runCLICommand, + specCLICommand, + startCLICommand, + stateCLICommand, + updateCLICommand, + eventsCLICommand, + versionCLICommand, + + // Kata Containers specific extensions + kataCheckCLICommand, + kataEnvCLICommand, + kataNetworkCLICommand, + kataOverheadCLICommand, + factoryCLICommand, +} + +// runtimeBeforeSubcommands is the function to run before command-line +// parsing occurs. +var runtimeBeforeSubcommands = beforeSubcommands + +// runtimeAfterSubcommands is the function to run after the command-line +// has been parsed. +var runtimeAfterSubcommands = afterSubcommands + +// runtimeCommandNotFound is the function to handle an invalid sub-command. +var runtimeCommandNotFound = commandNotFound + +// runtimeVersion is the function that returns the full version +// string describing the runtime. +var runtimeVersion = makeVersionString + +// saved default cli package values (for testing). +var savedCLIAppHelpTemplate = cli.AppHelpTemplate +var savedCLIVersionPrinter = cli.VersionPrinter +var savedCLIErrWriter = cli.ErrWriter + +func init() { + kataLog = logrus.WithFields(logrus.Fields{ + "name": name, + "source": "runtime", + "arch": arch, + "pid": os.Getpid(), + }) + + // Save the original log level and then set to debug level to ensure + // that any problems detected before the config file is parsed are + // logged. This is required since the config file determines the true + // log level for the runtime: once parsed the log level is set + // appropriately but for issues between now and completion of the + // config file parsing, it is prudent to operate in verbose mode. + originalLoggerLevel = kataLog.Logger.Level + kataLog.Logger.Level = logrus.DebugLevel +} + +// setupSignalHandler sets up signal handling, starting a go routine to deal +// with signals as they arrive. +// +// Note that the specified context is NOT used to create a trace span (since the +// first (root) span must be created in beforeSubcommands()): it is simply +// used to pass to the crash handling functions to finalise tracing. +func setupSignalHandler(ctx context.Context) { + signals.SetLogger(kataLog) + + sigCh := make(chan os.Signal, 8) + + for _, sig := range signals.HandledSignals() { + signal.Notify(sigCh, sig) + } + + dieCb := func() { + katautils.StopTracing(ctx) + } + + go func() { + for { + sig := <-sigCh + + nativeSignal, ok := sig.(syscall.Signal) + if !ok { + err := errors.New("unknown signal") + kataLog.WithError(err).WithField("signal", sig.String()).Error() + continue + } + + if signals.FatalSignal(nativeSignal) { + kataLog.WithField("signal", sig).Error("received fatal signal") + signals.Die(dieCb) + } else if debug && signals.NonFatalSignal(nativeSignal) { + kataLog.WithField("signal", sig).Debug("handling signal") + signals.Backtrace() + } + } + }() +} + +// setExternalLoggers registers the specified logger with the external +// packages which accept a logger to handle their own logging. +func setExternalLoggers(ctx context.Context, logger *logrus.Entry) { + var span opentracing.Span + + // Only create a new span if a root span already exists. This is + // required to ensure that this function will not disrupt the root + // span logic by creating a span before the proper root span has been + // created. + + if opentracing.SpanFromContext(ctx) != nil { + span, ctx = katautils.Trace(ctx, "setExternalLoggers") + defer span.Finish() + } + + // Set virtcontainers logger. + vci.SetLogger(ctx, logger) + + // Set vm factory logger. + vf.SetLogger(ctx, logger) + + // Set the OCI package logger. + oci.SetLogger(ctx, logger) + + // Set the katautils package logger + katautils.SetLogger(ctx, logger, originalLoggerLevel) + + // Set the rootless package logger + rootless.SetLogger(ctx, logger) +} + +// beforeSubcommands is the function to perform preliminary checks +// before command-line parsing occurs. +func beforeSubcommands(c *cli.Context) error { + var configFile string + var runtimeConfig oci.RuntimeConfig + var err error + + katautils.SetConfigOptions(name, defaultRuntimeConfiguration, defaultSysConfRuntimeConfiguration) + + handleShowConfig(c) + + if userWantsUsage(c) { + // No setup required if the user just + // wants to see the usage statement. + return nil + } + + r, err := parseBoolOrAuto(c.GlobalString("rootless")) + if err != nil { + return err + } + // If flag is true/false, assign the rootless flag. + // vc will not perform any auto-detection in that case. + // In case flag is nil or auto, vc detects if the runtime is running as rootless. + if r != nil { + rootless.SetRootless(*r) + } + // Support --systed-cgroup + // Issue: https://github.com/kata-containers/runtime/issues/2428 + + ignoreConfigLogs := false + var traceRootSpan string + + subCmdIsCheckCmd := (c.NArg() >= 1 && (c.Args()[0] == checkCmd)) + if subCmdIsCheckCmd { + // checkCmd will use the default logrus logger to stderr + // raise the logger default level to warn + kataLog.Logger.SetLevel(logrus.WarnLevel) + // do not print configuration logs for checkCmd + ignoreConfigLogs = true + } else { + if path := c.GlobalString("log"); path != "" { + f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0640) + if err != nil { + return err + } + kataLog.Logger.Out = f + } + + switch c.GlobalString("log-format") { + case "text": + // retain logrus's default. + case "json": + kataLog.Logger.Formatter = new(logrus.JSONFormatter) + default: + return fmt.Errorf("unknown log-format %q", c.GlobalString("log-format")) + } + + // Add the name of the sub-command to each log entry for easier + // debugging. + cmdName := c.Args().First() + if c.App.Command(cmdName) != nil { + kataLog = kataLog.WithField("command", cmdName) + + // Name for the root span (used for tracing) now the + // sub-command name is known. + traceRootSpan = name + " " + cmdName + } + + // Since a context is required, pass a new (throw-away) one - we + // cannot use the main context as tracing hasn't been enabled yet + // (meaning any spans created at this point will be silently ignored). + setExternalLoggers(context.Background(), kataLog) + + if c.NArg() == 1 && c.Args()[0] == envCmd { + // simply report the logging setup + ignoreConfigLogs = true + } + } + + configFile, runtimeConfig, err = katautils.LoadConfiguration(c.GlobalString(configFilePathOption), ignoreConfigLogs, false) + if err != nil { + fatal(err) + } + if !subCmdIsCheckCmd { + debug = runtimeConfig.Debug + crashOnError = runtimeConfig.Debug + + if traceRootSpan != "" { + // Create the tracer. + // + // Note: no spans are created until the command-line has been parsed. + // This delays collection of trace data slightly but benefits the user by + // ensuring the first span is the name of the sub-command being + // invoked from the command-line. + err = setupTracing(c, traceRootSpan) + if err != nil { + return err + } + } + } + + args := strings.Join(c.Args(), " ") + + fields := logrus.Fields{ + "version": version, + "commit": commit, + "arguments": `"` + args + `"`, + } + + err = addExpFeatures(c, runtimeConfig) + if err != nil { + return err + } + + kataLog.WithFields(fields).Info() + + // make the data accessible to the sub-commands. + c.App.Metadata["runtimeConfig"] = runtimeConfig + c.App.Metadata["configFile"] = configFile + + return nil +} + +// handleShowConfig determines if the user wishes to see the configuration +// paths. If so, it will display them and then exit. +func handleShowConfig(context *cli.Context) { + if context.GlobalBool(showConfigPathsOption) { + files := katautils.GetDefaultConfigFilePaths() + + for _, file := range files { + fmt.Fprintf(defaultOutputFile, "%s\n", file) + } + + exit(0) + } +} + +func setupTracing(context *cli.Context, rootSpanName string) error { + tracer, err := katautils.CreateTracer(name) + if err != nil { + fatal(err) + } + + // Create the root span now that the sub-command name is + // known. + // + // Note that this "Before" function is called (and returns) + // before the subcommand handler is called. As such, we cannot + // "Finish()" the span here - that is handled in the .After + // function. + span := tracer.StartSpan(rootSpanName) + + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + span.SetTag("subsystem", "runtime") + + // Associate the root span with the context + ctx = opentracing.ContextWithSpan(ctx, span) + + // Add tracer to metadata and update the context + context.App.Metadata["tracer"] = tracer + context.App.Metadata["context"] = ctx + + return nil +} + +// add supported experimental features in context +func addExpFeatures(clictx *cli.Context, runtimeConfig oci.RuntimeConfig) error { + ctx, err := cliContextToContext(clictx) + if err != nil { + return err + } + + var exps []string + for _, e := range runtimeConfig.Experimental { + exps = append(exps, e.Name) + } + + ctx = exp.ContextWithExp(ctx, exps) + // Add tracer to metadata and update the context + clictx.App.Metadata["context"] = ctx + return nil +} + +func afterSubcommands(c *cli.Context) error { + ctx, err := cliContextToContext(c) + if err != nil { + return err + } + + katautils.StopTracing(ctx) + + return nil +} + +// function called when an invalid command is specified which causes the +// runtime to error. +func commandNotFound(c *cli.Context, command string) { + err := fmt.Errorf("Invalid command %q", command) + fatal(err) +} + +// makeVersionString returns a multi-line string describing the runtime +// version along with the version of the OCI specification it supports. +func makeVersionString() string { + v := make([]string, 0, 3) + + versionStr := version + if versionStr == "" { + versionStr = unknown + } + + v = append(v, name+" : "+versionStr) + + commitStr := commit + if commitStr == "" { + commitStr = unknown + } + + v = append(v, " commit : "+commitStr) + + specVersionStr := specs.Version + if specVersionStr == "" { + specVersionStr = unknown + } + + v = append(v, " OCI specs: "+specVersionStr) + + return strings.Join(v, "\n") +} + +// setCLIGlobals modifies various cli package global variables +func setCLIGlobals() { + cli.AppHelpTemplate = fmt.Sprintf(`%s%s`, cli.AppHelpTemplate, notes) + + // Override the default function to display version details to + // ensure the "--version" option and "version" command are identical. + cli.VersionPrinter = func(c *cli.Context) { + fmt.Fprintln(defaultOutputFile, c.App.Version) + } + + // If the command returns an error, cli takes upon itself to print + // the error on cli.ErrWriter and exit. + // Use our own writer here to ensure the log gets sent to the right + // location. + cli.ErrWriter = &fatalWriter{cli.ErrWriter} +} + +// createRuntimeApp creates an application to process the command-line +// arguments and invoke the requested runtime command. +func createRuntimeApp(ctx context.Context, args []string) error { + app := cli.NewApp() + + app.Name = name + app.Writer = defaultOutputFile + app.Usage = usage + app.CommandNotFound = runtimeCommandNotFound + app.Version = runtimeVersion() + app.Flags = runtimeFlags + app.Commands = runtimeCommands + app.Before = runtimeBeforeSubcommands + app.After = runtimeAfterSubcommands + app.EnableBashCompletion = true + + // allow sub-commands to access context + app.Metadata = map[string]interface{}{ + "context": ctx, + } + + return app.Run(args) +} + +// userWantsUsage determines if the user only wishes to see the usage +// statement. +func userWantsUsage(context *cli.Context) bool { + if context.NArg() == 0 { + return true + } + + if context.NArg() == 1 && (context.Args()[0] == "help" || context.Args()[0] == "version") { + return true + } + + if context.NArg() >= 2 && (context.Args()[1] == "-h" || context.Args()[1] == "--help") { + return true + } + + return false +} + +// fatal prints the error's details exits the program. +func fatal(err error) { + kataLog.Error(err) + fmt.Fprintln(defaultErrorFile, err) + exit(1) +} + +type fatalWriter struct { + cliErrWriter io.Writer +} + +func (f *fatalWriter) Write(p []byte) (n int, err error) { + // Ensure error is logged before displaying to the user + kataLog.Error(string(p)) + return f.cliErrWriter.Write(p) +} + +func createRuntime(ctx context.Context) { + setupSignalHandler(ctx) + + setCLIGlobals() + + err := createRuntimeApp(ctx, os.Args) + if err != nil { + fatal(err) + } +} + +// cliContextToContext extracts the generic context from the specified +// cli context. +func cliContextToContext(c *cli.Context) (context.Context, error) { + if c == nil { + return nil, errors.New("need cli.Context") + } + + // extract the main context + ctx, ok := c.App.Metadata["context"].(context.Context) + if !ok { + return nil, errors.New("invalid or missing context in metadata") + } + + return ctx, nil +} + +func main() { + // create a new empty context + ctx := context.Background() + + dieCb := func() { + katautils.StopTracing(ctx) + } + + defer signals.HandlePanic(dieCb) + + createRuntime(ctx) +} diff --git a/src/runtime/cli/main_test.go b/src/runtime/cli/main_test.go new file mode 100644 index 000000000..d7c999246 --- /dev/null +++ b/src/runtime/cli/main_test.go @@ -0,0 +1,1087 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "flag" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path" + "path/filepath" + "regexp" + "strings" + "testing" + + "github.com/dlespiau/covertool/pkg/cover" + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/kata-containers/runtime/virtcontainers/pkg/rootless" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/kata-containers/runtime/virtcontainers/types" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" + jaeger "github.com/uber/jaeger-client-go" + "github.com/urfave/cli" +) + +const ( + testDirMode = os.FileMode(0750) + testFileMode = os.FileMode(0640) + testExeFileMode = os.FileMode(0750) + + // small docker image used to create root filesystems from + testDockerImage = "busybox" + + testSandboxID = "99999999-9999-9999-99999999999999999" + testContainerID = "1" + testBundle = "bundle" +) + +var ( + // package variables set by calling TestMain() + testDir = "" + testBundleDir = "" + tc ktu.TestConstraint + ctrEngine = katautils.CtrEngine{} +) + +// testingImpl is a concrete mock RVC implementation used for testing +var testingImpl = &vcmock.VCMock{} + +func init() { + if version == "" { + panic("ERROR: invalid build: version not set") + } + + if commit == "" { + panic("ERROR: invalid build: commit not set") + } + + if defaultSysConfRuntimeConfiguration == "" { + panic("ERROR: invalid build: defaultSysConfRuntimeConfiguration not set") + } + + if defaultRuntimeConfiguration == "" { + panic("ERROR: invalid build: defaultRuntimeConfiguration not set") + } + + fmt.Printf("INFO: running as actual user %v (effective %v), actual group %v (effective %v)\n", + os.Getuid(), os.Geteuid(), os.Getgid(), os.Getegid()) + + fmt.Printf("INFO: switching to fake virtcontainers implementation for testing\n") + vci = testingImpl + + var err error + + fmt.Printf("INFO: creating test directory\n") + testDir, err = ioutil.TempDir("", fmt.Sprintf("%s-", name)) + if err != nil { + panic(fmt.Sprintf("ERROR: failed to create test directory: %v", err)) + } + fmt.Printf("INFO: test directory is %v\n", testDir) + + var output string + for _, name := range katautils.DockerLikeCtrEngines { + fmt.Printf("INFO: checking for container engine: %s\n", name) + + output, err = ctrEngine.Init(name) + if err == nil { + break + } + } + + if ctrEngine.Name == "" { + panic(fmt.Sprintf("ERROR: Docker-like container engine not accessible to current user: %v (error %v)", + output, err)) + } + + // Do this now to avoid hitting the test timeout value due to + // slow network response. + fmt.Printf("INFO: ensuring required container image (%v) is available\n", testDockerImage) + // Only hit the network if the image doesn't exist locally + _, err = ctrEngine.Inspect(testDockerImage) + if err == nil { + fmt.Printf("INFO: container image %v already exists locally\n", testDockerImage) + } else { + fmt.Printf("INFO: pulling container image %v\n", testDockerImage) + _, err = ctrEngine.Pull(testDockerImage) + if err != nil { + panic(err) + } + } + + testBundleDir = filepath.Join(testDir, testBundle) + err = os.MkdirAll(testBundleDir, testDirMode) + if err != nil { + panic(fmt.Sprintf("ERROR: failed to create bundle directory %v: %v", testBundleDir, err)) + } + + fmt.Printf("INFO: creating OCI bundle in %v for tests to use\n", testBundleDir) + err = realMakeOCIBundle(testBundleDir) + if err != nil { + panic(fmt.Sprintf("ERROR: failed to create OCI bundle: %v", err)) + } + + tc = ktu.NewTestConstraint(false) +} + +// resetCLIGlobals undoes the effects of setCLIGlobals(), restoring the original values +func resetCLIGlobals() { + cli.AppHelpTemplate = savedCLIAppHelpTemplate + cli.VersionPrinter = savedCLIVersionPrinter + cli.ErrWriter = savedCLIErrWriter +} + +func runUnitTests(m *testing.M) { + ret := m.Run() + + os.RemoveAll(testDir) + + os.Exit(ret) +} + +// TestMain is the common main function used by ALL the test functions +// for this package. +func TestMain(m *testing.M) { + // Parse the command line using the stdlib flag package so the flags defined + // in the testing package get populated. + cover.ParseAndStripTestFlags() + + // Make sure we have the opportunity to flush the coverage report to disk when + // terminating the process. + defer func() { + cover.FlushProfiles() + }() + + // If the test binary name is kata-runtime.coverage, we've are being asked to + // run the coverage-instrumented kata-runtime. + if path.Base(os.Args[0]) == name+".coverage" || + path.Base(os.Args[0]) == name { + main() + exit(0) + } + + runUnitTests(m) +} + +func createEmptyFile(path string) (err error) { + return ioutil.WriteFile(path, []byte(""), testFileMode) +} + +func grep(pattern, file string) error { + if file == "" { + return errors.New("need file") + } + + bytes, err := ioutil.ReadFile(file) + if err != nil { + return err + } + + re := regexp.MustCompile(pattern) + matches := re.FindAllStringSubmatch(string(bytes), -1) + + if matches == nil { + return fmt.Errorf("pattern %q not found in file %q", pattern, file) + } + + return nil +} + +// newTestHypervisorConfig creaets a new virtcontainers +// HypervisorConfig, ensuring that the required resources are also +// created. +// +// Note: no parameter validation in case caller wishes to create an invalid +// object. +func newTestHypervisorConfig(dir string, create bool) (vc.HypervisorConfig, error) { + kernelPath := path.Join(dir, "kernel") + imagePath := path.Join(dir, "image") + hypervisorPath := path.Join(dir, "hypervisor") + + if create { + for _, file := range []string{kernelPath, imagePath, hypervisorPath} { + err := createEmptyFile(file) + if err != nil { + return vc.HypervisorConfig{}, err + } + } + } + + return vc.HypervisorConfig{ + KernelPath: kernelPath, + ImagePath: imagePath, + HypervisorPath: hypervisorPath, + HypervisorMachineType: "pc-lite", + }, nil +} + +// newTestRuntimeConfig creates a new RuntimeConfig +func newTestRuntimeConfig(dir, consolePath string, create bool) (oci.RuntimeConfig, error) { + if dir == "" { + return oci.RuntimeConfig{}, errors.New("BUG: need directory") + } + + hypervisorConfig, err := newTestHypervisorConfig(dir, create) + if err != nil { + return oci.RuntimeConfig{}, err + } + + return oci.RuntimeConfig{ + HypervisorType: vc.QemuHypervisor, + HypervisorConfig: hypervisorConfig, + AgentType: vc.KataContainersAgent, + ProxyType: vc.KataProxyType, + ShimType: vc.KataShimType, + Console: consolePath, + }, nil +} + +// createOCIConfig creates an OCI configuration (spec) file in +// the bundle directory specified (which must exist). +func createOCIConfig(bundleDir string) error { + if bundleDir == "" { + return errors.New("BUG: Need bundle directory") + } + + if !katautils.FileExists(bundleDir) { + return fmt.Errorf("BUG: Bundle directory %s does not exist", bundleDir) + } + + var configCmd string + + // Search for a suitable version of runc to use to generate + // the OCI config file. + for _, cmd := range []string{"docker-runc", "runc"} { + fullPath, err := exec.LookPath(cmd) + if err == nil { + configCmd = fullPath + break + } + } + + if configCmd == "" { + return errors.New("Cannot find command to generate OCI config file") + } + + _, err := katautils.RunCommand([]string{configCmd, "spec", "--bundle", bundleDir}) + if err != nil { + return err + } + + specFile := filepath.Join(bundleDir, specConfig) + if !katautils.FileExists(specFile) { + return fmt.Errorf("generated OCI config file does not exist: %v", specFile) + } + + return nil +} + +// createRootfs creates a minimal root filesystem below the specified +// directory. +func createRootfs(dir string) error { + err := os.MkdirAll(dir, testDirMode) + if err != nil { + return err + } + + container, err := ctrEngine.Create(testDockerImage) + if err != nil { + return err + } + + err = ctrEngine.GetRootfs(container, dir) + if err != nil { + return err + } + + // Clean up + _, err = ctrEngine.Rm(container) + if err != nil { + return err + } + + return nil +} + +// realMakeOCIBundle will create an OCI bundle (including the "config.json" +// config file) in the directory specified (which must already exist). +// +// XXX: Note that tests should *NOT* call this function - they should +// XXX: instead call makeOCIBundle(). +func realMakeOCIBundle(bundleDir string) error { + if bundleDir == "" { + return errors.New("BUG: Need bundle directory") + } + + if !katautils.FileExists(bundleDir) { + return fmt.Errorf("BUG: Bundle directory %v does not exist", bundleDir) + } + + err := createOCIConfig(bundleDir) + if err != nil { + return err + } + + // Note the unusual parameter (a directory, not the config + // file to parse!) + spec, err := compatoci.ParseConfigJSON(bundleDir) + if err != nil { + return err + } + + // Determine the rootfs directory name the OCI config refers to + ociRootPath := spec.Root.Path + + rootfsDir := filepath.Join(bundleDir, ociRootPath) + + if strings.HasPrefix(ociRootPath, "/") { + return fmt.Errorf("Cannot handle absolute rootfs as bundle must be unique to each test") + } + + err = createRootfs(rootfsDir) + if err != nil { + return err + } + + return nil +} + +// Create an OCI bundle in the specified directory. +// +// Note that the directory will be created, but it's parent is expected to exist. +// +// This function works by copying the already-created test bundle. Ideally, +// the bundle would be recreated for each test, but createRootfs() uses +// docker which on some systems is too slow, resulting in the tests timing +// out. +func makeOCIBundle(bundleDir string) error { + from := testBundleDir + to := bundleDir + + // only the basename of bundleDir needs to exist as bundleDir + // will get created by cp(1). + base := filepath.Dir(bundleDir) + + for _, dir := range []string{from, base} { + if !katautils.FileExists(dir) { + return fmt.Errorf("BUG: directory %v should exist", dir) + } + } + + output, err := katautils.RunCommandFull([]string{"cp", "-a", from, to}, true) + if err != nil { + return fmt.Errorf("failed to copy test OCI bundle from %v to %v: %v (output: %v)", from, to, err, output) + } + + return nil +} + +func writeOCIConfigFile(spec specs.Spec, configPath string) error { + if configPath == "" { + return errors.New("BUG: need config file path") + } + + bytes, err := json.MarshalIndent(spec, "", "\t") + if err != nil { + return err + } + + return ioutil.WriteFile(configPath, bytes, testFileMode) +} + +func newSingleContainerStatus(containerID string, containerState types.ContainerState, annotations map[string]string, spec *specs.Spec) vc.ContainerStatus { + return vc.ContainerStatus{ + ID: containerID, + State: containerState, + Annotations: annotations, + Spec: spec, + } +} + +func execCLICommandFunc(assertHandler *assert.Assertions, cliCommand cli.Command, set *flag.FlagSet, expectedErr bool) { + ctx := createCLIContext(set) + ctx.App.Name = "foo" + + fn, ok := cliCommand.Action.(func(context *cli.Context) error) + assertHandler.True(ok) + + err := fn(ctx) + + if expectedErr { + assertHandler.Error(err) + } else { + assertHandler.Nil(err) + } +} + +func createCLIContextWithApp(flagSet *flag.FlagSet, app *cli.App) *cli.Context { + ctx := cli.NewContext(app, flagSet, nil) + + // create the map if required + if ctx.App.Metadata == nil { + ctx.App.Metadata = map[string]interface{}{} + } + + // add standard entries + ctx.App.Metadata["context"] = context.Background() + ctx.App.Metadata["tracer"] = &jaeger.Tracer{} + + return ctx +} + +func createCLIContext(flagset *flag.FlagSet) *cli.Context { + return createCLIContextWithApp(flagset, cli.NewApp()) +} + +func TestMakeOCIBundle(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + bundleDir := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundleDir) + assert.NoError(err) + + specFile := filepath.Join(bundleDir, specConfig) + assert.True(katautils.FileExists(specFile)) +} + +func TestCreateOCIConfig(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + bundleDir := filepath.Join(tmpdir, "bundle") + + err = createOCIConfig(bundleDir) + // ENOENT + assert.Error(err) + + err = os.MkdirAll(bundleDir, testDirMode) + assert.NoError(err) + + err = createOCIConfig(bundleDir) + assert.NoError(err) + + specFile := filepath.Join(bundleDir, specConfig) + assert.True(katautils.FileExists(specFile)) +} + +func TestCreateRootfs(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + rootfsDir := filepath.Join(tmpdir, "rootfs") + assert.False(katautils.FileExists(rootfsDir)) + + err = createRootfs(rootfsDir) + assert.NoError(err) + + // non-comprehensive list of expected directories + expectedDirs := []string{"bin", "dev", "etc", "usr", "var"} + + assert.True(katautils.FileExists(rootfsDir)) + + for _, dir := range expectedDirs { + dirPath := filepath.Join(rootfsDir, dir) + assert.True(katautils.FileExists(dirPath)) + } +} + +func TestMainUserWantsUsage(t *testing.T) { + assert := assert.New(t) + + type testData struct { + arguments []string + expectTrue bool + } + + data := []testData{ + {[]string{}, true}, + {[]string{"help"}, true}, + {[]string{"version"}, true}, + {[]string{"sub-command", "-h"}, true}, + {[]string{"sub-command", "--help"}, true}, + + {[]string{""}, false}, + {[]string{"sub-command", "--foo"}, false}, + {[]string{"kata-check"}, false}, + {[]string{"haaaalp"}, false}, + {[]string{"wibble"}, false}, + {[]string{"versioned"}, false}, + } + + for i, d := range data { + set := flag.NewFlagSet("", 0) + set.Parse(d.arguments) + + ctx := createCLIContext(set) + result := userWantsUsage(ctx) + + if d.expectTrue { + assert.True(result, "test %d (%+v)", i, d) + } else { + assert.False(result, "test %d (%+v)", i, d) + } + } +} + +func TestMainBeforeSubCommands(t *testing.T) { + assert := assert.New(t) + + type testData struct { + arguments []string + expectError bool + } + + data := []testData{ + {[]string{}, false}, + {[]string{"help"}, false}, + {[]string{"version"}, false}, + {[]string{"sub-command", "-h"}, false}, + {[]string{"sub-command", "--help"}, false}, + } + + for i, d := range data { + set := flag.NewFlagSet("", 0) + set.Parse(d.arguments) + + ctx := createCLIContext(set) + err := beforeSubcommands(ctx) + + if d.expectError { + assert.Errorf(err, "test %d (%+v)", i, d) + } else { + assert.NoError(err, "test %d (%+v)", i, d) + } + } +} + +func TestMainBeforeSubCommandsInvalidLogFile(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + logFile := filepath.Join(tmpdir, "log") + + // create the file as the wrong type to force a failure + err = os.MkdirAll(logFile, testDirMode) + assert.NoError(err) + + set := flag.NewFlagSet("", 0) + set.String("log", logFile, "") + set.Parse([]string{"create"}) + + ctx := createCLIContext(set) + + err = beforeSubcommands(ctx) + assert.Error(err) +} + +func TestMainBeforeSubCommandsInvalidLogFormat(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + logFile := filepath.Join(tmpdir, "log") + + set := flag.NewFlagSet("", 0) + set.Bool("debug", true, "") + set.String("log", logFile, "") + set.String("log-format", "captain-barnacles", "") + set.Parse([]string{"create"}) + + logOut := kataLog.Logger.Out + kataLog.Logger.Out = nil + + defer func() { + kataLog.Logger.Out = logOut + }() + + ctx := createCLIContext(set) + + err = beforeSubcommands(ctx) + assert.Error(err) + assert.NotNil(kataLog.Logger.Out) +} + +func TestMainBeforeSubCommandsLoadConfigurationFail(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + logFile := filepath.Join(tmpdir, "log") + configFile := filepath.Join(tmpdir, "config") + + for _, logFormat := range []string{"json", "text"} { + set := flag.NewFlagSet("", 0) + set.Bool("debug", true, "") + set.String("log", logFile, "") + set.String("log-format", logFormat, "") + set.String("kata-config", configFile, "") + set.Parse([]string{"kata-env"}) + + ctx := createCLIContext(set) + + savedExitFunc := exitFunc + + exitStatus := 0 + exitFunc = func(status int) { exitStatus = status } + + defer func() { + exitFunc = savedExitFunc + }() + + // calls fatal() so no return + _ = beforeSubcommands(ctx) + assert.NotEqual(exitStatus, 0) + } +} + +func TestMainBeforeSubCommandsShowCCConfigPaths(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + set := flag.NewFlagSet("", 0) + set.Bool("kata-show-default-config-paths", true, "") + + ctx := createCLIContext(set) + + savedExitFunc := exitFunc + + exitStatus := 99 + exitFunc = func(status int) { exitStatus = status } + + defer func() { + exitFunc = savedExitFunc + }() + + savedOutputFile := defaultOutputFile + + defer func() { + resetCLIGlobals() + defaultOutputFile = savedOutputFile + }() + + output := filepath.Join(tmpdir, "output") + f, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY|os.O_SYNC, testFileMode) + assert.NoError(err) + defer f.Close() + + defaultOutputFile = f + + setCLIGlobals() + + _ = beforeSubcommands(ctx) + assert.Equal(exitStatus, 0) + + text, err := katautils.GetFileContents(output) + assert.NoError(err) + + lines := strings.Split(text, "\n") + + // Remove last line if empty + length := len(lines) + last := lines[length-1] + if last == "" { + lines = lines[:length-1] + } + + assert.Equal(len(lines), 2) + + for i, line := range lines { + switch i { + case 0: + assert.Equal(line, defaultSysConfRuntimeConfiguration) + case 1: + assert.Equal(line, defaultRuntimeConfiguration) + } + } +} + +func TestMainFatal(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + var exitStatus int + savedExitFunc := exitFunc + + exitFunc = func(status int) { exitStatus = status } + + savedErrorFile := defaultErrorFile + + output := filepath.Join(tmpdir, "output") + f, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY|os.O_SYNC, testFileMode) + assert.NoError(err) + defaultErrorFile = f + + defer func() { + f.Close() + defaultErrorFile = savedErrorFile + exitFunc = savedExitFunc + }() + + exitError := errors.New("hello world") + + fatal(exitError) + assert.Equal(exitStatus, 1) + + text, err := katautils.GetFileContents(output) + assert.NoError(err) + + trimmed := strings.TrimSpace(text) + assert.Equal(exitError.Error(), trimmed) +} + +func testVersionString(assert *assert.Assertions, versionString, expectedVersion, expectedCommit, expectedOCIVersion string) { + foundVersion := false + foundCommit := false + foundOCIVersion := false + + versionRE := regexp.MustCompile(fmt.Sprintf(`%s\s*:\s*%v`, name, expectedVersion)) + commitRE := regexp.MustCompile(fmt.Sprintf(`%s\s*:\s*%v`, "commit", expectedCommit)) + + ociRE := regexp.MustCompile(fmt.Sprintf(`%s\s*:\s*%v`, "OCI specs", expectedOCIVersion)) + + lines := strings.Split(versionString, "\n") + assert.True(len(lines) > 0) + + for _, line := range lines { + vMatches := versionRE.FindAllStringSubmatch(line, -1) + if vMatches != nil { + foundVersion = true + } + + cMatches := commitRE.FindAllStringSubmatch(line, -1) + if cMatches != nil { + foundCommit = true + } + + oMatches := ociRE.FindAllStringSubmatch(line, -1) + if oMatches != nil { + foundOCIVersion = true + } + } + + args := fmt.Sprintf("versionString: %q, expectedVersion: %q, expectedCommit: %v, expectedOCIVersion: %v\n", + versionString, expectedVersion, expectedCommit, expectedOCIVersion) + + assert.True(foundVersion, args) + assert.True(foundCommit, args) + assert.True(foundOCIVersion, args) +} + +func TestMainMakeVersionString(t *testing.T) { + assert := assert.New(t) + + v := makeVersionString() + + testVersionString(assert, v, version, commit, specs.Version) +} + +func TestMainMakeVersionStringNoVersion(t *testing.T) { + assert := assert.New(t) + + savedVersion := version + version = "" + + defer func() { + version = savedVersion + }() + + v := makeVersionString() + + testVersionString(assert, v, unknown, commit, specs.Version) +} + +func TestMainMakeVersionStringNoCommit(t *testing.T) { + assert := assert.New(t) + + savedCommit := commit + commit = "" + + defer func() { + commit = savedCommit + }() + + v := makeVersionString() + + testVersionString(assert, v, version, unknown, specs.Version) +} + +func TestMainMakeVersionStringNoOCIVersion(t *testing.T) { + assert := assert.New(t) + + savedVersion := specs.Version + specs.Version = "" + + defer func() { + specs.Version = savedVersion + }() + + v := makeVersionString() + + testVersionString(assert, v, version, commit, unknown) +} + +func TestMainCreateRuntimeApp(t *testing.T) { + assert := assert.New(t) + + savedBefore := runtimeBeforeSubcommands + savedOutputFile := defaultOutputFile + + // disable + runtimeBeforeSubcommands = nil + + devNull, err := os.OpenFile("/dev/null", os.O_RDWR, 0640) + assert.NoError(err) + defer devNull.Close() + + defaultOutputFile = devNull + + setCLIGlobals() + + defer func() { + resetCLIGlobals() + runtimeBeforeSubcommands = savedBefore + defaultOutputFile = savedOutputFile + }() + + args := []string{name} + + err = createRuntimeApp(context.Background(), args) + assert.NoError(err, "%v", args) +} + +func TestMainCreateRuntimeAppInvalidSubCommand(t *testing.T) { + assert := assert.New(t) + + exitStatus := 0 + + savedBefore := runtimeBeforeSubcommands + savedExitFunc := exitFunc + + exitFunc = func(status int) { exitStatus = status } + + // disable + runtimeBeforeSubcommands = nil + + defer func() { + runtimeBeforeSubcommands = savedBefore + exitFunc = savedExitFunc + }() + + // calls fatal() so no return + _ = createRuntimeApp(context.Background(), []string{name, "i-am-an-invalid-sub-command"}) + + assert.NotEqual(exitStatus, 0) +} + +func TestMainCreateRuntime(t *testing.T) { + assert := assert.New(t) + + const cmd = "foo" + const msg = "moo FAILURE" + + resetCLIGlobals() + + exitStatus := 0 + + savedOSArgs := os.Args + savedExitFunc := exitFunc + savedBefore := runtimeBeforeSubcommands + savedCommands := runtimeCommands + + os.Args = []string{name, cmd} + exitFunc = func(status int) { exitStatus = status } + + // disable + runtimeBeforeSubcommands = nil + + // override sub-commands + runtimeCommands = []cli.Command{ + { + Name: cmd, + Action: func(context *cli.Context) error { + return errors.New(msg) + }, + }, + } + + defer func() { + os.Args = savedOSArgs + exitFunc = savedExitFunc + runtimeBeforeSubcommands = savedBefore + runtimeCommands = savedCommands + }() + + assert.Equal(exitStatus, 0) + createRuntime(context.Background()) + assert.NotEqual(exitStatus, 0) +} + +func TestMainVersionPrinter(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + savedOutputFile := defaultOutputFile + + defer func() { + resetCLIGlobals() + defaultOutputFile = savedOutputFile + }() + + output := filepath.Join(tmpdir, "output") + f, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY|os.O_SYNC, testFileMode) + assert.NoError(err) + defer f.Close() + + defaultOutputFile = f + + setCLIGlobals() + + err = createRuntimeApp(context.Background(), []string{name, "--version"}) + assert.NoError(err) + + err = grep(fmt.Sprintf(`%s\s*:\s*%s`, name, version), output) + assert.NoError(err) +} + +func TestMainFatalWriter(t *testing.T) { + assert := assert.New(t) + + const cmd = "foo" + const msg = "moo FAILURE" + + // create buffer to save logger output + buf := &bytes.Buffer{} + + savedBefore := runtimeBeforeSubcommands + savedLogOutput := kataLog.Logger.Out + savedCLIExiter := cli.OsExiter + savedCommands := runtimeCommands + + // disable + runtimeBeforeSubcommands = nil + + // save all output + kataLog.Logger.Out = buf + + cli.OsExiter = func(status int) {} + + // override sub-commands + runtimeCommands = []cli.Command{ + { + Name: cmd, + Action: func(context *cli.Context) error { + return cli.NewExitError(msg, 42) + }, + }, + } + + defer func() { + runtimeBeforeSubcommands = savedBefore + kataLog.Logger.Out = savedLogOutput + cli.OsExiter = savedCLIExiter + runtimeCommands = savedCommands + }() + + setCLIGlobals() + + err := createRuntimeApp(context.Background(), []string{name, cmd}) + assert.Error(err) + + re := regexp.MustCompile( + fmt.Sprintf(`\blevel\b.*\berror\b.*\b%s\b`, msg)) + matches := re.FindAllStringSubmatch(buf.String(), -1) + assert.NotEmpty(matches) +} + +func TestMainSetCLIGlobals(t *testing.T) { + assert := assert.New(t) + + defer resetCLIGlobals() + + cli.AppHelpTemplate = "" + cli.VersionPrinter = nil + cli.ErrWriter = nil + + setCLIGlobals() + + assert.NotEqual(cli.AppHelpTemplate, "") + assert.NotNil(cli.VersionPrinter) + assert.NotNil(cli.ErrWriter) +} + +func TestMainResetCLIGlobals(t *testing.T) { + assert := assert.New(t) + + assert.NotEqual(cli.AppHelpTemplate, "") + assert.NotNil(savedCLIVersionPrinter) + assert.NotNil(savedCLIErrWriter) + + cli.AppHelpTemplate = "" + cli.VersionPrinter = nil + cli.ErrWriter = nil + + resetCLIGlobals() + + assert.Equal(cli.AppHelpTemplate, savedCLIAppHelpTemplate) + assert.NotNil(cli.VersionPrinter) + assert.NotNil(savedCLIVersionPrinter) +} + +func createTempContainerIDMapping(containerID, sandboxID string) (string, error) { + // Mocking rootless + rootless.IsRootless = func() bool { return false } + + tmpDir, err := ioutil.TempDir("", "containers-mapping") + if err != nil { + return "", err + } + ctrsMapTreePath = tmpDir + + path := filepath.Join(ctrsMapTreePath, containerID, sandboxID) + if err := os.MkdirAll(path, 0750); err != nil { + return "", err + } + + katautils.SetCtrsMapTreePath(ctrsMapTreePath) + return tmpDir, nil +} diff --git a/src/runtime/cli/network.go b/src/runtime/cli/network.go new file mode 100644 index 000000000..881a23580 --- /dev/null +++ b/src/runtime/cli/network.go @@ -0,0 +1,229 @@ +// Copyright (c) 2018 Huawei Corporation. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + + vcTypes "github.com/kata-containers/runtime/virtcontainers/pkg/types" + "github.com/kata-containers/runtime/virtcontainers/types" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +type networkType int + +const ( + // interfaceType for interface operation + interfaceType networkType = iota + + routeType +) + +var kataNetworkCLICommand = cli.Command{ + Name: "kata-network", + Usage: "manage interfaces and routes for container", + Subcommands: []cli.Command{ + addIfaceCommand, + delIfaceCommand, + listIfacesCommand, + updateRoutesCommand, + listRoutesCommand, + }, + Action: func(context *cli.Context) error { + return cli.ShowSubcommandHelp(context) + }, +} + +var addIfaceCommand = cli.Command{ + Name: "add-iface", + Usage: "add an interface to a container", + ArgsUsage: `add-iface file or - for stdin`, + Flags: []cli.Flag{}, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + return networkModifyCommand(ctx, context.Args().First(), context.Args().Get(1), interfaceType, true) + }, +} + +var delIfaceCommand = cli.Command{ + Name: "del-iface", + Usage: "delete an interface from a container", + ArgsUsage: `del-iface file or - for stdin`, + Flags: []cli.Flag{}, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + return networkModifyCommand(ctx, context.Args().First(), context.Args().Get(1), interfaceType, false) + }, +} + +var listIfacesCommand = cli.Command{ + Name: "list-ifaces", + Usage: "list network interfaces in a container", + ArgsUsage: `list-ifaces `, + Flags: []cli.Flag{}, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + return networkListCommand(ctx, context.Args().First(), interfaceType) + }, +} + +var updateRoutesCommand = cli.Command{ + Name: "update-routes", + Usage: "update routes of a container", + ArgsUsage: `update-routes file or - for stdin`, + Flags: []cli.Flag{}, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + return networkModifyCommand(ctx, context.Args().First(), context.Args().Get(1), routeType, true) + }, +} + +var listRoutesCommand = cli.Command{ + Name: "list-routes", + Usage: "list network routes in a container", + ArgsUsage: `list-routes `, + Flags: []cli.Flag{}, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + return networkListCommand(ctx, context.Args().First(), routeType) + }, +} + +func networkModifyCommand(ctx context.Context, containerID, input string, opType networkType, add bool) (err error) { + status, sandboxID, err := getExistingContainerInfo(ctx, containerID) + if err != nil { + return err + } + + containerID = status.ID + + kataLog = kataLog.WithFields(logrus.Fields{ + "container": containerID, + "sandbox": sandboxID, + }) + + setExternalLoggers(ctx, kataLog) + + // container MUST be running + if status.State.State != types.StateRunning { + return fmt.Errorf("container %s is not running", containerID) + } + + var ( + f *os.File + output = defaultOutputFile + ) + + if input == "-" { + f = os.Stdin + } else { + f, err = os.Open(input) + if err != nil { + return err + } + defer f.Close() + } + switch opType { + case interfaceType: + var inf, resultingInf *vcTypes.Interface + if err = json.NewDecoder(f).Decode(&inf); err != nil { + return err + } + if add { + resultingInf, err = vci.AddInterface(ctx, sandboxID, inf) + if err != nil { + kataLog.WithField("resulting-interface", fmt.Sprintf("%+v", resultingInf)). + WithError(err).Error("add interface failed") + } + } else { + resultingInf, err = vci.RemoveInterface(ctx, sandboxID, inf) + if err != nil { + kataLog.WithField("resulting-interface", fmt.Sprintf("%+v", resultingInf)). + WithError(err).Error("delete interface failed") + } + } + json.NewEncoder(output).Encode(resultingInf) + case routeType: + var routes, resultingRoutes []*vcTypes.Route + if err = json.NewDecoder(f).Decode(&routes); err != nil { + return err + } + resultingRoutes, err = vci.UpdateRoutes(ctx, sandboxID, routes) + json.NewEncoder(output).Encode(resultingRoutes) + if err != nil { + kataLog.WithField("resulting-routes", fmt.Sprintf("%+v", resultingRoutes)). + WithError(err).Error("update routes failed") + } + } + return err +} + +func networkListCommand(ctx context.Context, containerID string, opType networkType) (err error) { + status, sandboxID, err := getExistingContainerInfo(ctx, containerID) + if err != nil { + return err + } + + containerID = status.ID + + kataLog = kataLog.WithFields(logrus.Fields{ + "container": containerID, + "sandbox": sandboxID, + }) + + setExternalLoggers(ctx, kataLog) + + // container MUST be running + if status.State.State != types.StateRunning { + return fmt.Errorf("container %s is not running", containerID) + } + + var file = defaultOutputFile + + switch opType { + case interfaceType: + var interfaces []*vcTypes.Interface + interfaces, err = vci.ListInterfaces(ctx, sandboxID) + if err != nil { + kataLog.WithField("existing-interfaces", fmt.Sprintf("%+v", interfaces)). + WithError(err).Error("list interfaces failed") + } + json.NewEncoder(file).Encode(interfaces) + case routeType: + var routes []*vcTypes.Route + routes, err = vci.ListRoutes(ctx, sandboxID) + if err != nil { + kataLog.WithField("resulting-routes", fmt.Sprintf("%+v", routes)). + WithError(err).Error("update routes failed") + } + json.NewEncoder(file).Encode(routes) + } + return err +} diff --git a/src/runtime/cli/network_test.go b/src/runtime/cli/network_test.go new file mode 100644 index 000000000..4e3d943dc --- /dev/null +++ b/src/runtime/cli/network_test.go @@ -0,0 +1,92 @@ +// Copyright (c) 2018 Huawei Corporation. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "io/ioutil" + "os" + "testing" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" + + vc "github.com/kata-containers/runtime/virtcontainers" + vcTypes "github.com/kata-containers/runtime/virtcontainers/pkg/types" + "github.com/kata-containers/runtime/virtcontainers/types" +) + +var ( + testAddInterfaceFuncReturnNil = func(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error) { + return nil, nil + } + testRemoveInterfaceFuncReturnNil = func(ctx context.Context, sandboxID string, inf *vcTypes.Interface) (*vcTypes.Interface, error) { + return nil, nil + } + testListInterfacesFuncReturnNil = func(ctx context.Context, sandboxID string) ([]*vcTypes.Interface, error) { + return nil, nil + } + testUpdateRoutsFuncReturnNil = func(ctx context.Context, sandboxID string, routes []*vcTypes.Route) ([]*vcTypes.Route, error) { + return nil, nil + } + testListRoutesFuncReturnNil = func(ctx context.Context, sandboxID string) ([]*vcTypes.Route, error) { + return nil, nil + } +) + +func TestNetworkCliFunction(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateRunning, + } + + testingImpl.AddInterfaceFunc = testAddInterfaceFuncReturnNil + testingImpl.RemoveInterfaceFunc = testRemoveInterfaceFuncReturnNil + testingImpl.ListInterfacesFunc = testListInterfacesFuncReturnNil + testingImpl.UpdateRoutesFunc = testUpdateRoutsFuncReturnNil + testingImpl.ListRoutesFunc = testListRoutesFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil + } + + defer func() { + testingImpl.AddInterfaceFunc = nil + testingImpl.RemoveInterfaceFunc = nil + testingImpl.ListInterfacesFunc = nil + testingImpl.UpdateRoutesFunc = nil + testingImpl.ListRoutesFunc = nil + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + execCLICommandFunc(assert, addIfaceCommand, set, true) + + set.Parse([]string{testContainerID}) + execCLICommandFunc(assert, listIfacesCommand, set, false) + execCLICommandFunc(assert, listRoutesCommand, set, false) + + f, err := ioutil.TempFile("", "interface") + defer os.Remove(f.Name()) + assert.NoError(err) + assert.NotNil(f) + f.WriteString("{}") + + set.Parse([]string{testContainerID, f.Name()}) + execCLICommandFunc(assert, addIfaceCommand, set, false) + execCLICommandFunc(assert, delIfaceCommand, set, false) + + f.Seek(0, 0) + f.WriteString("[{}]") + f.Close() + execCLICommandFunc(assert, updateRoutesCommand, set, false) +} diff --git a/src/runtime/cli/oci.go b/src/runtime/cli/oci.go new file mode 100644 index 000000000..8ffac2df4 --- /dev/null +++ b/src/runtime/cli/oci.go @@ -0,0 +1,213 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "bufio" + "context" + "fmt" + "net" + "os" + "path/filepath" + "strings" + "syscall" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/opencontainers/runc/libcontainer/utils" +) + +// Contants related to cgroup memory directory +const ( + // Filesystem type corresponding to CGROUP_SUPER_MAGIC as listed + // here: http://man7.org/linux/man-pages/man2/statfs.2.html + cgroupFsType = 0x27e0eb +) + +var cgroupsDirPath string + +var procMountInfo = "/proc/self/mountinfo" + +// getContainerInfo returns the container status and its sandbox ID. +func getContainerInfo(ctx context.Context, containerID string) (vc.ContainerStatus, string, error) { + // container ID MUST be provided. + if containerID == "" { + return vc.ContainerStatus{}, "", fmt.Errorf("Missing container ID") + } + + sandboxID, err := katautils.FetchContainerIDMapping(containerID) + if err != nil { + return vc.ContainerStatus{}, "", err + } + if sandboxID == "" { + // Not finding a container should not trigger an error as + // getContainerInfo is used for checking the existence and + // the absence of a container ID. + return vc.ContainerStatus{}, "", nil + } + + ctrStatus, err := vci.StatusContainer(ctx, sandboxID, containerID) + if err != nil { + return vc.ContainerStatus{}, "", err + } + + return ctrStatus, sandboxID, nil +} + +func getExistingContainerInfo(ctx context.Context, containerID string) (vc.ContainerStatus, string, error) { + cStatus, sandboxID, err := getContainerInfo(ctx, containerID) + if err != nil { + return vc.ContainerStatus{}, "", err + } + + // container ID MUST exist. + if cStatus.ID == "" { + return vc.ContainerStatus{}, "", fmt.Errorf("Container ID (%v) does not exist", containerID) + } + + return cStatus, sandboxID, nil +} + +func validCreateParams(ctx context.Context, containerID, bundlePath string) (string, error) { + // container ID MUST be provided. + if containerID == "" { + return "", fmt.Errorf("Missing container ID") + } + + // container ID MUST be unique. + cStatus, _, err := getContainerInfo(ctx, containerID) + if err != nil { + return "", err + } + + if cStatus.ID != "" { + return "", fmt.Errorf("ID already in use, unique ID should be provided") + } + + // bundle path MUST be provided. + if bundlePath == "" { + return "", fmt.Errorf("Missing bundle path") + } + + // bundle path MUST be valid. + fileInfo, err := os.Stat(bundlePath) + if err != nil { + return "", fmt.Errorf("Invalid bundle path '%s': %s", bundlePath, err) + } + if !fileInfo.IsDir() { + return "", fmt.Errorf("Invalid bundle path '%s', it should be a directory", bundlePath) + } + + resolved, err := katautils.ResolvePath(bundlePath) + if err != nil { + return "", err + } + + return resolved, nil +} + +func isCgroupMounted(cgroupPath string) bool { + var statFs syscall.Statfs_t + + if err := syscall.Statfs(cgroupPath, &statFs); err != nil { + return false + } + + if statFs.Type != archConvertStatFs(cgroupFsType) { + return false + } + + return true +} + +func setupConsole(consolePath, consoleSockPath string) (string, error) { + if consolePath != "" { + return consolePath, nil + } + + if consoleSockPath == "" { + return "", nil + } + + console, err := newConsole() + if err != nil { + return "", err + } + defer console.master.Close() + + // Open the socket path provided by the caller + conn, err := net.Dial("unix", consoleSockPath) + if err != nil { + return "", err + } + + uConn, ok := conn.(*net.UnixConn) + if !ok { + return "", fmt.Errorf("casting to *net.UnixConn failed") + } + + socket, err := uConn.File() + if err != nil { + return "", err + } + + // Send the parent fd through the provided socket + if err := utils.SendFd(socket, console.master.Name(), console.master.Fd()); err != nil { + return "", err + } + + return console.slavePath, nil +} + +func noNeedForOutput(detach bool, tty bool) bool { + if !detach { + return false + } + + if !tty { + return false + } + + return true +} + +func getCgroupsDirPath(mountInfoFile string) (string, error) { + if cgroupsDirPath != "" { + return cgroupsDirPath, nil + } + + f, err := os.Open(mountInfoFile) + if err != nil { + return "", err + } + defer f.Close() + + var cgroupRootPath string + scanner := bufio.NewScanner(f) + for scanner.Scan() { + text := scanner.Text() + index := strings.Index(text, " - ") + if index < 0 { + continue + } + fields := strings.Split(text, " ") + postSeparatorFields := strings.Fields(text[index+3:]) + numPostFields := len(postSeparatorFields) + + if len(fields) < 5 || postSeparatorFields[0] != "cgroup" || numPostFields < 3 { + continue + } + + cgroupRootPath = filepath.Dir(fields[4]) + break + } + + if _, err = os.Stat(cgroupRootPath); err != nil { + return "", err + } + + return cgroupRootPath, nil +} diff --git a/src/runtime/cli/oci_test.go b/src/runtime/cli/oci_test.go new file mode 100644 index 000000000..d2c8777c4 --- /dev/null +++ b/src/runtime/cli/oci_test.go @@ -0,0 +1,319 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "fmt" + "io/ioutil" + "math/rand" + "net" + "os" + "path/filepath" + "testing" + "time" + + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/opencontainers/runc/libcontainer/utils" + "github.com/stretchr/testify/assert" +) + +var ( + consolePathTest = "console-test" + consoleSocketPathTest = "console-socket-test" +) + +func TestGetContainerInfoContainerIDEmptyFailure(t *testing.T) { + assert := assert.New(t) + status, _, err := getContainerInfo(context.Background(), "") + + assert.Error(err, "This test should fail because containerID is empty") + assert.Empty(status.ID, "Expected blank fullID, but got %v", status.ID) +} + +func TestGetContainerInfo(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + containerID := testContainerID + + containerStatus := vc.ContainerStatus{ + ID: containerID, + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + }, + } + + path, err := createTempContainerIDMapping(containerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return containerStatus, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + status, sandboxID, err := getContainerInfo(context.Background(), testContainerID) + assert.NoError(err) + assert.Equal(sandboxID, sandbox.ID()) + assert.Equal(status, containerStatus) +} + +func TestValidCreateParamsContainerIDEmptyFailure(t *testing.T) { + assert := assert.New(t) + _, err := validCreateParams(context.Background(), "", "") + + assert.Error(err, "This test should fail because containerID is empty") + assert.False(vcmock.IsMockError(err)) +} + +func TestGetExistingContainerInfoContainerIDEmptyFailure(t *testing.T) { + assert := assert.New(t) + status, _, err := getExistingContainerInfo(context.Background(), "") + + assert.Error(err, "This test should fail because containerID is empty") + assert.Empty(status.ID, "Expected blank fullID, but got %v", status.ID) +} + +func TestValidCreateParamsContainerIDNotUnique(t *testing.T) { + assert := assert.New(t) + + testSandboxID2 := testSandboxID + "2" + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + err = os.MkdirAll(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID2), 0750) + assert.NoError(err) + + _, err = validCreateParams(context.Background(), testContainerID, "") + + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestValidCreateParamsInvalidBundle(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + bundlePath := filepath.Join(tmpdir, "bundle") + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + _, err = validCreateParams(context.Background(), testContainerID, bundlePath) + // bundle is ENOENT + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestValidCreateParamsBundleIsAFile(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + bundlePath := filepath.Join(tmpdir, "bundle") + err = createEmptyFile(bundlePath) + assert.NoError(err) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + _, err = validCreateParams(context.Background(), testContainerID, bundlePath) + // bundle exists as a file, not a directory + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestSetupConsoleExistingConsolePathSuccessful(t *testing.T) { + assert := assert.New(t) + console, err := setupConsole(consolePathTest, "") + + assert.NoError(err) + assert.Equal(console, consolePathTest, "Got %q, Expecting %q", console, consolePathTest) +} + +func TestSetupConsoleExistingConsolePathAndConsoleSocketPathSuccessful(t *testing.T) { + assert := assert.New(t) + console, err := setupConsole(consolePathTest, consoleSocketPathTest) + + assert.NoError(err) + assert.Equal(console, consolePathTest, "Got %q, Expecting %q", console, consolePathTest) +} + +func TestSetupConsoleEmptyPathsSuccessful(t *testing.T) { + assert := assert.New(t) + + console, err := setupConsole("", "") + assert.NoError(err) + assert.Empty(console, "Console path should be empty, got %q instead", console) +} + +func TestSetupConsoleExistingConsoleSocketPath(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir("", "test-socket") + assert.NoError(err) + defer os.RemoveAll(dir) + + sockName := filepath.Join(dir, "console.sock") + + l, err := net.Listen("unix", sockName) + assert.NoError(err) + + console, err := setupConsole("", sockName) + assert.NoError(err) + + waitCh := make(chan error) + go func() { + conn, err1 := l.Accept() + if err != nil { + waitCh <- err1 + } + + uConn, ok := conn.(*net.UnixConn) + if !ok { + waitCh <- fmt.Errorf("casting to *net.UnixConn failed") + } + + f, err1 := uConn.File() + if err != nil { + waitCh <- err1 + } + + _, err1 = utils.RecvFd(f) + waitCh <- err1 + }() + + assert.NotEmpty(console, "Console socket path should not be empty") + + err = <-waitCh + assert.NoError(err) +} + +func TestSetupConsoleNotExistingSocketPathFailure(t *testing.T) { + assert := assert.New(t) + + console, err := setupConsole("", "unknown-sock-path") + assert.Error(err, "This test should fail because the console socket path does not exist") + assert.Empty(console, "This test should fail because the console socket path does not exist") +} + +func testNoNeedForOutput(t *testing.T, detach bool, tty bool, expected bool) { + assert := assert.New(t) + result := noNeedForOutput(detach, tty) + + assert.Equal(result, expected) +} + +func TestNoNeedForOutputDetachTrueTtyTrue(t *testing.T) { + testNoNeedForOutput(t, true, true, true) +} + +func TestNoNeedForOutputDetachFalseTtyTrue(t *testing.T) { + testNoNeedForOutput(t, false, true, false) +} + +func TestNoNeedForOutputDetachFalseTtyFalse(t *testing.T) { + testNoNeedForOutput(t, false, false, false) +} + +func TestNoNeedForOutputDetachTrueTtyFalse(t *testing.T) { + testNoNeedForOutput(t, true, false, false) +} + +func TestIsCgroupMounted(t *testing.T) { + assert := assert.New(t) + + r := rand.New(rand.NewSource(time.Now().Unix())) + randPath := fmt.Sprintf("/path/to/random/%d", r.Int63()) + + assert.False(isCgroupMounted(randPath), "%s does not exist", randPath) + + assert.False(isCgroupMounted(os.TempDir()), "%s is not a cgroup", os.TempDir()) + + cgroupsDirPath = "" + cgroupRootPath, err := getCgroupsDirPath(procMountInfo) + if err != nil { + assert.NoError(err) + } + memoryCgroupPath := filepath.Join(cgroupRootPath, "memory") + if _, err := os.Stat(memoryCgroupPath); os.IsNotExist(err) { + t.Skipf("memory cgroup does not exist: %s", memoryCgroupPath) + } + + assert.True(isCgroupMounted(memoryCgroupPath), "%s is a cgroup", memoryCgroupPath) +} + +func TestGetCgroupsDirPath(t *testing.T) { + assert := assert.New(t) + + type testData struct { + contents string + expectedResult string + expectError bool + } + + dir, err := ioutil.TempDir("", "") + if err != nil { + assert.NoError(err) + } + defer os.RemoveAll(dir) + + // make sure tested cgroupsDirPath is existed + testedCgroupDir := filepath.Join(dir, "weirdCgroup") + err = os.Mkdir(testedCgroupDir, testDirMode) + assert.NoError(err) + + weirdCgroupPath := filepath.Join(testedCgroupDir, "memory") + + data := []testData{ + {fmt.Sprintf("num1 num2 num3 / %s num6 num7 - cgroup cgroup rw,memory", weirdCgroupPath), testedCgroupDir, false}, + // cgroup mount is not properly formated, if fields post - less than 3 + {fmt.Sprintf("num1 num2 num3 / %s num6 num7 - cgroup cgroup ", weirdCgroupPath), "", true}, + {"a a a a a a a - b c d", "", true}, + {"a \na b \na b c\na b c d", "", true}, + {"", "", true}, + } + + file := filepath.Join(dir, "mountinfo") + + //file does not exist, should error here + _, err = getCgroupsDirPath(file) + assert.Error(err) + + for _, d := range data { + err := ioutil.WriteFile(file, []byte(d.contents), testFileMode) + assert.NoError(err) + + cgroupsDirPath = "" + path, err := getCgroupsDirPath(file) + if d.expectError { + assert.Error(err, fmt.Sprintf("got %q, test data: %+v", path, d)) + } else { + assert.NoError(err, fmt.Sprintf("got %q, test data: %+v", path, d)) + } + + assert.Equal(d.expectedResult, path) + } +} diff --git a/src/runtime/cli/pause.go b/src/runtime/cli/pause.go new file mode 100644 index 000000000..748fddfb9 --- /dev/null +++ b/src/runtime/cli/pause.go @@ -0,0 +1,93 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var noteText = `Use "` + name + ` list" to identify container statuses.` + +var pauseCLICommand = cli.Command{ + Name: "pause", + Usage: "suspend all processes in a container", + ArgsUsage: ` + +Where "" is the container name to be paused.`, + Description: `The pause command suspends all processes in a container. + + ` + noteText, + Action: pause, +} + +var resumeCLICommand = cli.Command{ + Name: "resume", + Usage: "unpause all previously paused processes in a container", + ArgsUsage: ` + +Where "" is the container name to be resumed.`, + Description: `The resume command unpauses all processes in a container. + + ` + noteText, + Action: resume, +} + +func pause(c *cli.Context) error { + return toggle(c, true) +} + +func resume(c *cli.Context) error { + return toggle(c, false) +} + +func toggle(c *cli.Context, pause bool) error { + ctx, err := cliContextToContext(c) + if err != nil { + return err + } + + return toggleContainerPause(ctx, c.Args().First(), pause) +} + +func toggleContainerPause(ctx context.Context, containerID string, pause bool) (err error) { + span, _ := katautils.Trace(ctx, "pause") + defer span.Finish() + span.SetTag("pause", pause) + + kataLog = kataLog.WithField("container", containerID) + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + + // Checks the MUST and MUST NOT from OCI runtime specification + status, sandboxID, err := getExistingContainerInfo(ctx, containerID) + if err != nil { + return err + } + + containerID = status.ID + + kataLog = kataLog.WithFields(logrus.Fields{ + "container": containerID, + "sandbox": sandboxID, + }) + + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + span.SetTag("sandbox", sandboxID) + + if pause { + err = vci.PauseContainer(ctx, sandboxID, containerID) + } else { + err = vci.ResumeContainer(ctx, sandboxID, containerID) + } + + return err +} diff --git a/src/runtime/cli/pause_test.go b/src/runtime/cli/pause_test.go new file mode 100644 index 000000000..a3660fa6a --- /dev/null +++ b/src/runtime/cli/pause_test.go @@ -0,0 +1,175 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "io/ioutil" + "os" + "testing" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" + + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/types" +) + +var ( + testPauseContainerFuncReturnNil = func(ctx context.Context, sandboxID, containerID string) error { + return nil + } + + testResumeContainerFuncReturnNil = func(ctx context.Context, sandboxID, containerID string) error { + return nil + } +) + +func TestPauseCLIFunctionSuccessful(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateRunning, + } + + testingImpl.PauseContainerFunc = testPauseContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil + } + + defer func() { + testingImpl.PauseContainerFunc = nil + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, pauseCLICommand, set, false) +} + +func TestPauseCLIFunctionContainerNotExistFailure(t *testing.T) { + assert := assert.New(t) + + testingImpl.PauseContainerFunc = testPauseContainerFuncReturnNil + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + defer func() { + testingImpl.PauseContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, pauseCLICommand, set, true) +} + +func TestPauseCLIFunctionPauseContainerFailure(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateRunning, + } + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, pauseCLICommand, set, true) +} + +func TestResumeCLIFunctionSuccessful(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateRunning, + } + + testingImpl.ResumeContainerFunc = testResumeContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil + } + + defer func() { + testingImpl.ResumeContainerFunc = nil + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, resumeCLICommand, set, false) +} + +func TestResumeCLIFunctionContainerNotExistFailure(t *testing.T) { + assert := assert.New(t) + + testingImpl.ResumeContainerFunc = testResumeContainerFuncReturnNil + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + defer func() { + testingImpl.ResumeContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, resumeCLICommand, set, true) +} + +func TestResumeCLIFunctionPauseContainerFailure(t *testing.T) { + assert := assert.New(t) + + state := types.ContainerState{ + State: types.StateRunning, + } + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return newSingleContainerStatus(testContainerID, state, map[string]string{}, &specs.Spec{}), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + set := flag.NewFlagSet("", 0) + set.Parse([]string{testContainerID}) + + execCLICommandFunc(assert, resumeCLICommand, set, true) +} diff --git a/src/runtime/cli/ps.go b/src/runtime/cli/ps.go new file mode 100644 index 000000000..93aa4f332 --- /dev/null +++ b/src/runtime/cli/ps.go @@ -0,0 +1,106 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "fmt" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/types" + + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var psCLICommand = cli.Command{ + Name: "ps", + Usage: "ps displays the processes running inside a container", + ArgsUsage: ` [ps options]`, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "format, f", + Value: "table", + Usage: `select one of: ` + formatOptions, + }, + }, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + if !context.Args().Present() { + return fmt.Errorf("Missing container ID, should at least provide one") + } + + var args []string + if len(context.Args()) > 1 { + // [1:] is to remove container_id: + // context.Args(): [container_id ps_arg1 ps_arg2 ...] + // args: [ps_arg1 ps_arg2 ...] + args = context.Args()[1:] + } + + return ps(ctx, context.Args().First(), context.String("format"), args) + }, + SkipArgReorder: true, +} + +func ps(ctx context.Context, containerID, format string, args []string) error { + span, _ := katautils.Trace(ctx, "ps") + defer span.Finish() + + if containerID == "" { + return fmt.Errorf("Missing container ID") + } + + kataLog = kataLog.WithField("container", containerID) + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + + // Checks the MUST and MUST NOT from OCI runtime specification + status, sandboxID, err := getExistingContainerInfo(ctx, containerID) + if err != nil { + return err + } + + containerID = status.ID + + kataLog = kataLog.WithFields(logrus.Fields{ + "container": containerID, + "sandbox": sandboxID, + }) + + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + span.SetTag("sandbox", sandboxID) + + // container MUST be running + if status.State.State != types.StateRunning { + return fmt.Errorf("Container %s is not running", containerID) + } + + var options vc.ProcessListOptions + + options.Args = args + if len(options.Args) == 0 { + options.Args = []string{"-ef"} + } + + options.Format = format + + msg, err := vci.ProcessListContainer(ctx, sandboxID, containerID, options) + if err != nil { + return err + } + + fmt.Print(string(msg)) + + return nil +} diff --git a/src/runtime/cli/ps_test.go b/src/runtime/cli/ps_test.go new file mode 100644 index 000000000..c214793f2 --- /dev/null +++ b/src/runtime/cli/ps_test.go @@ -0,0 +1,120 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "os" + "testing" + + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/kata-containers/runtime/virtcontainers/types" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +func TestPSCLIAction(t *testing.T) { + assert := assert.New(t) + + flagSet := flag.NewFlagSet("flag", flag.ContinueOnError) + flagSet.Parse([]string{"runtime"}) + + // create a new fake context + ctx := createCLIContext(flagSet) + + // get Action function + actionFunc, ok := psCLICommand.Action.(func(ctx *cli.Context) error) + assert.True(ok) + + err := actionFunc(ctx) + assert.Error(err, "Missing container ID") +} + +func TestPSFailure(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testContainerID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: sandbox.ID(), + MockSandbox: sandbox, + }, + } + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + // inexistent container + err = ps(context.Background(), "xyz123abc", "json", []string{"-ef"}) + assert.Error(err) + + // container is not running + err = ps(context.Background(), sandbox.ID(), "json", []string{"-ef"}) + assert.Error(err) +} + +func TestPSSuccessful(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testContainerID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: sandbox.ID(), + MockSandbox: sandbox, + }, + } + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + State: types.ContainerState{ + State: types.StateRunning, + }, + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + }, nil + } + + testingImpl.ProcessListContainerFunc = func(ctx context.Context, sandboxID, containerID string, options vc.ProcessListOptions) (vc.ProcessList, error) { + return []byte("echo,sleep,grep"), nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + testingImpl.ProcessListContainerFunc = nil + }() + + err = ps(context.Background(), sandbox.ID(), "json", []string{}) + assert.NoError(err) +} diff --git a/src/runtime/cli/run.go b/src/runtime/cli/run.go new file mode 100644 index 000000000..6125224e7 --- /dev/null +++ b/src/runtime/cli/run.go @@ -0,0 +1,129 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "errors" + "fmt" + "os" + "syscall" + + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/urfave/cli" +) + +var runCLICommand = cli.Command{ + Name: "run", + Usage: "create and run a container", + ArgsUsage: ` + + 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.`, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "bundle, b", + Value: "", + Usage: `path to the root of the bundle directory, defaults to the current directory`, + }, + cli.StringFlag{ + Name: "console", + Value: "", + Usage: "path to a pseudo terminal", + }, + cli.StringFlag{ + Name: "console-socket", + Value: "", + Usage: "path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal", + }, + cli.StringFlag{ + Name: "pid-file", + Value: "", + Usage: "specify the file to write the process id to", + }, + cli.BoolFlag{ + Name: "detach, d", + Usage: "detach from the container's process", + }, + cli.BoolFlag{ + Name: "no-pivot", + Usage: "warning: this flag is meaningless to kata-runtime, just defined in order to be compatible with docker in ramdisk", + }, + }, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig) + if !ok { + return errors.New("invalid runtime config") + } + + return run(ctx, context.Args().First(), + context.String("bundle"), + context.String("console"), + context.String("console-socket"), + context.String("pid-file"), + context.Bool("detach"), + context.Bool("systemd-cgroup"), + runtimeConfig) + }, +} + +func run(ctx context.Context, containerID, bundle, console, consoleSocket, pidFile string, detach, systemdCgroup bool, + runtimeConfig oci.RuntimeConfig) error { + span, ctx := katautils.Trace(ctx, "run") + defer span.Finish() + + consolePath, err := setupConsole(console, consoleSocket) + if err != nil { + return err + } + + if err := create(ctx, containerID, bundle, consolePath, pidFile, detach, systemdCgroup, runtimeConfig); err != nil { + return err + } + + sandbox, err := start(ctx, containerID) + if err != nil { + return err + } + + if detach { + return nil + } + + containers := sandbox.GetAllContainers() + if len(containers) == 0 { + return fmt.Errorf("There are no containers running in the sandbox: %s", sandbox.ID()) + } + + p, err := os.FindProcess(containers[0].GetPid()) + if err != nil { + return err + } + + ps, err := p.Wait() + if err != nil { + return fmt.Errorf("Process state %s: %s", ps.String(), err) + } + + // delete container's resources + if err := delete(ctx, sandbox.ID(), true); err != nil { + return err + } + + //runtime should forward container exit code to the system + return cli.NewExitError("", ps.Sys().(syscall.WaitStatus).ExitStatus()) +} diff --git a/src/runtime/cli/run_test.go b/src/runtime/cli/run_test.go new file mode 100644 index 000000000..a2b908c1e --- /dev/null +++ b/src/runtime/cli/run_test.go @@ -0,0 +1,670 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "testing" + + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" +) + +func TestRunCliAction(t *testing.T) { + assert := assert.New(t) + + flagSet := flag.NewFlagSet("flag", flag.ContinueOnError) + flagSet.Parse([]string{"runtime"}) + + // create a new fake context + ctx := createCLIContext(flagSet) + + // get Action function + actionFunc, ok := runCLICommand.Action.(func(ctx *cli.Context) error) + assert.True(ok) + + err := actionFunc(ctx) + assert.Error(err, "missing runtime configuration") + + // temporal dir to place container files + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + // create a new runtime config + runtimeConfig, err := newTestRuntimeConfig(tmpdir, "/dev/ptmx", true) + assert.NoError(err) + + ctx.App.Metadata = map[string]interface{}{ + "runtimeConfig": runtimeConfig, + } + + err = actionFunc(ctx) + assert.Error(err, "run without args") +} + +func TestRunInvalidArgs(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + MockContainers: []*vcmock.Container{ + {MockID: testContainerID}, + }, + } + + // fake functions used to run containers + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + return sandbox, nil + } + + testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return sandbox, nil + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + defer func() { + testingImpl.CreateSandboxFunc = nil + testingImpl.StartSandboxFunc = nil + }() + + // temporal dir to place container files + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + // create a new bundle + bundlePath := filepath.Join(tmpdir, "bundle") + + err = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + // pid file + pidFilePath := filepath.Join(tmpdir, "pid") + + // console file + consolePath := "/dev/ptmx" + + // inexistent path + inexistentPath := "/this/path/does/not/exist" + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, consolePath, true) + assert.NoError(err) + + type testArgs struct { + containerID string + bundle string + console string + consoleSocket string + pidFile string + detach bool + systemdCgroup bool + runtimeConfig oci.RuntimeConfig + } + + args := []testArgs{ + {"", "", "", "", "", true, true, oci.RuntimeConfig{}}, + {"", "", "", "", "", false, false, oci.RuntimeConfig{}}, + {"", "", "", "", "", true, false, runtimeConfig}, + {"", "", "", "", "", false, true, runtimeConfig}, + {"", "", "", "", pidFilePath, false, false, runtimeConfig}, + {"", "", "", "", inexistentPath, false, false, runtimeConfig}, + {"", "", "", "", pidFilePath, false, true, runtimeConfig}, + {"", "", "", inexistentPath, pidFilePath, false, true, runtimeConfig}, + {"", "", inexistentPath, inexistentPath, pidFilePath, false, false, runtimeConfig}, + {"", "", inexistentPath, "", pidFilePath, false, false, runtimeConfig}, + {"", "", consolePath, "", pidFilePath, false, true, runtimeConfig}, + {"", bundlePath, consolePath, "", pidFilePath, false, true, runtimeConfig}, + {testContainerID, inexistentPath, consolePath, "", pidFilePath, false, true, oci.RuntimeConfig{}}, + {testContainerID, inexistentPath, consolePath, "", inexistentPath, false, true, oci.RuntimeConfig{}}, + {testContainerID, bundlePath, consolePath, "", pidFilePath, false, false, oci.RuntimeConfig{}}, + {testContainerID, inexistentPath, consolePath, "", pidFilePath, false, false, runtimeConfig}, + {testContainerID, inexistentPath, consolePath, "", inexistentPath, false, true, runtimeConfig}, + {testContainerID, bundlePath, consolePath, "", pidFilePath, false, true, runtimeConfig}, + } + + for i, a := range args { + err := run(context.Background(), a.containerID, a.bundle, a.console, a.consoleSocket, a.pidFile, a.detach, a.systemdCgroup, a.runtimeConfig) + assert.Errorf(err, "test %d (%+v)", i, a) + } +} + +type runContainerData struct { + pidFilePath string + consolePath string + bundlePath string + spec *specs.Spec + sandbox *vcmock.Sandbox + runtimeConfig oci.RuntimeConfig + process *os.Process + tmpDir string +} + +func testRunContainerSetup(t *testing.T) runContainerData { + assert := assert.New(t) + + // create a fake container workload + workload := []string{"/bin/sleep", "10"} + cmd := exec.Command(workload[0], workload[1:]...) + err := cmd.Start() + assert.NoError(err, "unable to start fake container workload %+v: %s", workload, err) + + // temporal dir to place container files + // Note - it is returned to the caller, who does the defer remove to clean up. + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + + // pid file + pidFilePath := filepath.Join(tmpdir, "pid") + + // console file + consolePath := "/dev/ptmx" + + // create a new bundle + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + // sandbox id and container id must be the same otherwise delete will not works + sandbox := &vcmock.Sandbox{ + MockID: testContainerID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: testContainerID, + MockPid: cmd.Process.Pid, + MockSandbox: sandbox, + }, + } + + // create a new runtime config + runtimeConfig, err := newTestRuntimeConfig(tmpdir, consolePath, true) + assert.NoError(err) + + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + return runContainerData{ + pidFilePath: pidFilePath, + consolePath: consolePath, + bundlePath: bundlePath, + sandbox: sandbox, + spec: &ociSpec, + runtimeConfig: runtimeConfig, + process: cmd.Process, + tmpDir: tmpdir, + } +} + +func TestRunContainerSuccessful(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + d := testRunContainerSetup(t) + defer os.RemoveAll(d.tmpDir) + + // this flags is used to detect if createSandboxFunc was called + flagCreate := false + + // fake functions used to run containers + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + flagCreate = true + return d.sandbox, nil + } + + testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return d.sandbox, nil + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + // return an empty list on create + if !flagCreate { + return vc.ContainerStatus{}, nil + } + + // return a sandboxStatus with the container status + return vc.ContainerStatus{ + ID: d.sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + Spec: d.spec, + }, nil + } + + testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + // now we can kill the fake container workload + err := d.process.Kill() + assert.NoError(err) + + return d.sandbox.MockContainers[0], nil + } + + testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return d.sandbox, nil + } + + testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + return d.sandbox.MockContainers[0], nil + } + + defer func() { + testingImpl.CreateSandboxFunc = nil + testingImpl.StartSandboxFunc = nil + testingImpl.StatusContainerFunc = nil + testingImpl.StartContainerFunc = nil + testingImpl.DeleteSandboxFunc = nil + testingImpl.DeleteContainerFunc = nil + }() + + type errorTestArgs struct { + bundlePath string + + // regex string for text of error message + errorRE string + + // If true, expect a cli.ExitError, else expect any other type + // of error. + expectExitError bool + } + + args := []errorTestArgs{ + {"", "config.json: no such file or directory", false}, + {d.bundlePath, "", true}, + } + + for i, a := range args { + err = run(context.Background(), d.sandbox.ID(), a.bundlePath, d.consolePath, "", d.pidFilePath, false, true, d.runtimeConfig) + assert.Errorf(err, "test args %d (%+v)", i, a) + + if a.errorRE == "" { + assert.Empty(err.Error()) + } else { + re := regexp.MustCompile(a.errorRE) + matches := re.FindAllStringSubmatch(err.Error(), -1) + assert.NotEmpty(matches) + } + + e, ok := err.(*cli.ExitError) + + if a.expectExitError { + // should return ExitError with the message and exit code + assert.Truef(ok, "test args %d (%+v): error should be a cli.ExitError: %s", i, a, err) + assert.NotZero(e.ExitCode()) + } else { + assert.Falsef(ok, "test args %d (%+v): error should not be a cli.ExitError: %s", i, a, err) + } + } +} + +func TestRunContainerDetachSuccessful(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + d := testRunContainerSetup(t) + defer os.RemoveAll(d.tmpDir) + + // this flags is used to detect if createSandboxFunc was called + flagCreate := false + + // fake functions used to run containers + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + flagCreate = true + return d.sandbox, nil + } + + testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return d.sandbox, nil + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + // return an empty list on create + if !flagCreate { + return vc.ContainerStatus{}, nil + } + + // return a sandboxStatus with the container status + return vc.ContainerStatus{ + ID: d.sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + Spec: d.spec, + }, nil + } + + testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + // now we can kill the fake container workload + err := d.process.Kill() + assert.NoError(err) + + return d.sandbox.MockContainers[0], nil + } + + testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return d.sandbox, nil + } + + testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + return d.sandbox.MockContainers[0], nil + } + + defer func() { + testingImpl.CreateSandboxFunc = nil + testingImpl.StartSandboxFunc = nil + testingImpl.StatusContainerFunc = nil + testingImpl.StartContainerFunc = nil + testingImpl.DeleteSandboxFunc = nil + testingImpl.DeleteContainerFunc = nil + }() + + err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, true, true, d.runtimeConfig) + + // should not return ExitError + assert.NoError(err) +} + +func TestRunContainerDeleteFail(t *testing.T) { + assert := assert.New(t) + + d := testRunContainerSetup(t) + defer os.RemoveAll(d.tmpDir) + + // this flags is used to detect if createSandboxFunc was called + flagCreate := false + + // fake functions used to run containers + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + flagCreate = true + return d.sandbox, nil + } + + testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return d.sandbox, nil + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + // return an empty list on create + if !flagCreate { + return vc.ContainerStatus{}, nil + } + + // return a sandboxStatus with the container status + return vc.ContainerStatus{ + ID: d.sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + Spec: d.spec, + }, nil + } + + testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + // now we can kill the fake container workload + err := d.process.Kill() + assert.NoError(err) + + return d.sandbox.MockContainers[0], nil + } + + testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + // return an error to provoke a failure in delete + return nil, fmt.Errorf("DeleteSandboxFunc") + } + + testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + // return an error to provoke a failure in delete + return d.sandbox.MockContainers[0], fmt.Errorf("DeleteContainerFunc") + } + + defer func() { + testingImpl.CreateSandboxFunc = nil + testingImpl.StartSandboxFunc = nil + testingImpl.StatusContainerFunc = nil + testingImpl.StartContainerFunc = nil + testingImpl.DeleteSandboxFunc = nil + testingImpl.DeleteContainerFunc = nil + }() + + err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, true, d.runtimeConfig) + + // should not return ExitError + err, ok := err.(*cli.ExitError) + assert.False(ok, "error should not be a cli.ExitError: %s", err) +} + +func TestRunContainerWaitFail(t *testing.T) { + assert := assert.New(t) + + d := testRunContainerSetup(t) + defer os.RemoveAll(d.tmpDir) + + // this flags is used to detect if createSandboxFunc was called + flagCreate := false + + // fake functions used to run containers + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + flagCreate = true + return d.sandbox, nil + } + + testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return d.sandbox, nil + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + // return an empty list on create + if !flagCreate { + return vc.ContainerStatus{}, nil + } + + // return a sandboxStatus with the container status + return vc.ContainerStatus{ + ID: d.sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + Spec: d.spec, + }, nil + } + + testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + // now we can kill the fake container workload + err := d.process.Kill() + assert.NoError(err) + + // change PID to provoke a failure in Wait + d.sandbox.MockContainers[0].MockPid = -1 + + return d.sandbox.MockContainers[0], nil + } + + testingImpl.DeleteSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + // return an error to provoke a failure in delete + return nil, fmt.Errorf("DeleteSandboxFunc") + } + + testingImpl.DeleteContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + // return an error to provoke a failure in delete + return d.sandbox.MockContainers[0], fmt.Errorf("DeleteContainerFunc") + } + + defer func() { + testingImpl.CreateSandboxFunc = nil + testingImpl.StartSandboxFunc = nil + testingImpl.StatusContainerFunc = nil + testingImpl.StartContainerFunc = nil + testingImpl.DeleteSandboxFunc = nil + testingImpl.DeleteContainerFunc = nil + }() + + err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, true, d.runtimeConfig) + + // should not return ExitError + err, ok := err.(*cli.ExitError) + assert.False(ok, "error should not be a cli.ExitError: %s", err) +} + +func TestRunContainerStartFail(t *testing.T) { + assert := assert.New(t) + + d := testRunContainerSetup(t) + defer os.RemoveAll(d.tmpDir) + + // now we can kill the fake container workload + err := d.process.Kill() + assert.NoError(err) + + // this flags is used to detect if createSandboxFunc was called + flagCreate := false + + // fake functions used to run containers + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + flagCreate = true + return d.sandbox, nil + } + + testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + // start fails + return nil, fmt.Errorf("StartSandbox") + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + // return an empty list on create + if !flagCreate { + return vc.ContainerStatus{}, nil + } + + // return a sandboxStatus with the container status + return vc.ContainerStatus{ + ID: d.sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + Spec: d.spec, + }, nil + } + + defer func() { + testingImpl.CreateSandboxFunc = nil + testingImpl.StartSandboxFunc = nil + testingImpl.StatusContainerFunc = nil + }() + + err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, true, d.runtimeConfig) + + // should not return ExitError + err, ok := err.(*cli.ExitError) + assert.False(ok, "error should not be a cli.ExitError: %s", err) +} + +func TestRunContainerStartFailExistingContainer(t *testing.T) { + assert := assert.New(t) + + d := testRunContainerSetup(t) + defer os.RemoveAll(d.tmpDir) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: testContainerID, + MockSandbox: sandbox, + }, + } + + path, err := createTempContainerIDMapping(testContainerID, sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + // return the container status + return vc.ContainerStatus{ + ID: testContainerID, + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + }, nil + } + + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + return sandbox, nil + } + + testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + // force no containers + sandbox.MockContainers = nil + + return sandbox, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + testingImpl.CreateSandboxFunc = nil + testingImpl.StartSandboxFunc = nil + }() + + err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, true, d.runtimeConfig) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} diff --git a/src/runtime/cli/spec.go b/src/runtime/cli/spec.go new file mode 100644 index 000000000..3a2fc7f7e --- /dev/null +++ b/src/runtime/cli/spec.go @@ -0,0 +1,114 @@ +// Copyright (c) 2014,2015,2016,2017 Docker, Inc. +// Copyright (c) 2018 Huawei Corporation. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/opencontainers/runc/libcontainer/specconv" + "github.com/urfave/cli" +) + +var specCLICommand = cli.Command{ + Name: "spec", + Usage: "create a new specification file", + ArgsUsage: "", + Description: `The spec command creates the new specification file named "` + specConfig + `" 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 + kata-runtime spec + sed -i 's;"sh";"/hello";' ` + specConfig + ` + kata-runtime run container1 + +In the run 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 kata-runtime, kata-runtime needs root privilege. If not +already running as root, you can use sudo to give kata-runtime root privilege. For +example: "sudo kata-runtime start container1" will give kata-runtime 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.`, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "bundle, b", + Value: "", + Usage: "path to the root of the bundle directory", + }, + }, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + span, _ := katautils.Trace(ctx, "spec") + defer span.Finish() + + spec := specconv.Example() + + checkNoFile := func(name string) error { + _, err := os.Stat(name) + if err == nil { + return fmt.Errorf("File %s exists. Remove it first", name) + } + if !os.IsNotExist(err) { + return err + } + return nil + } + bundle := context.String("bundle") + if bundle != "" { + if err := os.Chdir(bundle); err != nil { + return err + } + } + if err := checkNoFile(specConfig); err != nil { + return err + } + data, err := json.MarshalIndent(spec, "", "\t") + if err != nil { + return err + } + if err := ioutil.WriteFile(specConfig, data, 0640); err != nil { + return err + } + return nil + }, +} diff --git a/src/runtime/cli/spec_test.go b/src/runtime/cli/spec_test.go new file mode 100644 index 000000000..6ebf5bdc5 --- /dev/null +++ b/src/runtime/cli/spec_test.go @@ -0,0 +1,35 @@ +// Copyright (c) 2018 Huawei Corporation. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "flag" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +func TestSpecCliAction(t *testing.T) { + assert := assert.New(t) + + actionFunc, ok := specCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + flagSet := flag.NewFlagSet("flag", flag.ContinueOnError) + ctx := createCLIContext(flagSet) + defer os.Remove(specConfig) + err := actionFunc(ctx) + assert.NoError(err) + + pattern := "gid=5" + patternRootless := "uidMappings" + err = grep(pattern, specConfig) + assert.NoError(err) + err = grep(patternRootless, specConfig) + assert.Error(err) +} diff --git a/src/runtime/cli/start.go b/src/runtime/cli/start.go new file mode 100644 index 000000000..249cc1faa --- /dev/null +++ b/src/runtime/cli/start.go @@ -0,0 +1,113 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "fmt" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnot "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +var startCLICommand = cli.Command{ + Name: "start", + Usage: "executes the user defined process in a created container", + ArgsUsage: ` [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 .`, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + args := context.Args() + if !args.Present() { + return fmt.Errorf("Missing container ID, should at least provide one") + } + + for _, cID := range []string(args) { + if _, err := start(ctx, cID); err != nil { + return err + } + } + + return nil + }, +} + +func start(ctx context.Context, containerID string) (vc.VCSandbox, error) { + span, _ := katautils.Trace(ctx, "start") + defer span.Finish() + + kataLog = kataLog.WithField("container", containerID) + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + + // Checks the MUST and MUST NOT from OCI runtime specification + status, sandboxID, err := getExistingContainerInfo(ctx, containerID) + if err != nil { + return nil, err + } + + containerID = status.ID + + kataLog = kataLog.WithFields(logrus.Fields{ + "container": containerID, + "sandbox": sandboxID, + }) + + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + span.SetTag("sandbox", sandboxID) + + containerType, err := oci.GetContainerType(status.Annotations) + if err != nil { + return nil, err + } + + ociSpec, err := oci.GetOCIConfig(status) + if err != nil { + return nil, err + } + + var sandbox vc.VCSandbox + + if containerType.IsSandbox() { + s, err := vci.StartSandbox(ctx, sandboxID) + if err != nil { + return nil, err + } + + sandbox = s + } else { + c, err := vci.StartContainer(ctx, sandboxID, containerID) + if err != nil { + return nil, err + } + + sandbox = c.Sandbox() + } + + // Run post-start OCI hooks. + err = katautils.EnterNetNS(sandbox.GetNetNs(), func() error { + return katautils.PostStartHooks(ctx, ociSpec, sandboxID, status.Annotations[vcAnnot.BundlePathKey]) + }) + if err != nil { + return nil, err + } + + return sandbox, nil +} diff --git a/src/runtime/cli/start_test.go b/src/runtime/cli/start_test.go new file mode 100644 index 000000000..bcf479398 --- /dev/null +++ b/src/runtime/cli/start_test.go @@ -0,0 +1,243 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "io/ioutil" + "os" + "testing" + + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" +) + +func TestStartInvalidArgs(t *testing.T) { + assert := assert.New(t) + + // Missing container id + _, err := start(context.Background(), "") + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + // Mock StatusContainer error + _, err = start(context.Background(), testContainerID) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + path, err = ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + katautils.SetCtrsMapTreePath(path) + + // Container missing in container mapping + _, err = start(context.Background(), testContainerID) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestStartSandbox(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + }, + Spec: &specs.Spec{}, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + _, err = start(context.Background(), sandbox.ID()) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.StartSandboxFunc = nil + }() + + _, err = start(context.Background(), sandbox.ID()) + assert.Nil(err) +} + +func TestStartMissingAnnotation(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{}, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + _, err = start(context.Background(), sandbox.ID()) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestStartContainerSucessFailure(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: testContainerID, + MockSandbox: sandbox, + }, + } + + path, err := createTempContainerIDMapping(testContainerID, sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: testContainerID, + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + Spec: &specs.Spec{}, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + _, err = start(context.Background(), testContainerID) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + + testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + return sandbox.MockContainers[0], nil + } + + defer func() { + testingImpl.StartContainerFunc = nil + }() + + _, err = start(context.Background(), testContainerID) + assert.Nil(err) +} + +func TestStartCLIFunction(t *testing.T) { + assert := assert.New(t) + + flagSet := &flag.FlagSet{} + ctx := createCLIContext(flagSet) + + fn, ok := startCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + // no container id in the Metadata + err := fn(ctx) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + + path, err := createTempContainerIDMapping("xyz", "xyz") + assert.NoError(err) + defer os.RemoveAll(path) + + flagSet = flag.NewFlagSet("container-id", flag.ContinueOnError) + flagSet.Parse([]string{"xyz"}) + ctx = createCLIContext(flagSet) + + err = fn(ctx) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) +} + +func TestStartCLIFunctionSuccess(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: testContainerID, + MockSandbox: sandbox, + }, + } + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: testContainerID, + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + Spec: &specs.Spec{}, + }, nil + } + + testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + return sandbox.MockContainers[0], nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + testingImpl.StartContainerFunc = nil + }() + + fn, ok := startCLICommand.Action.(func(context *cli.Context) error) + assert.True(ok) + + flagSet := flag.NewFlagSet("test", 0) + flagSet.Parse([]string{testContainerID}) + ctx := createCLIContext(flagSet) + assert.NotNil(ctx) + + err = fn(ctx) + assert.NoError(err) +} diff --git a/src/runtime/cli/state.go b/src/runtime/cli/state.go new file mode 100644 index 000000000..a2fcc12e1 --- /dev/null +++ b/src/runtime/cli/state.go @@ -0,0 +1,70 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/urfave/cli" +) + +var stateCLICommand = cli.Command{ + Name: "state", + Usage: "output the state of a container", + ArgsUsage: ` + + is your name for the instance of the container`, + Description: `The state command outputs current state information for the +instance of a container.`, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + args := context.Args() + if len(args) != 1 { + return fmt.Errorf("Expecting only one container ID, got %d: %v", len(args), []string(args)) + } + + return state(ctx, args.First()) + }, +} + +func state(ctx context.Context, containerID string) error { + span, _ := katautils.Trace(ctx, "state") + defer span.Finish() + + kataLog = kataLog.WithField("container", containerID) + span.SetTag("container", containerID) + + setExternalLoggers(ctx, kataLog) + + // Checks the MUST and MUST NOT from OCI runtime specification + status, _, err := getExistingContainerInfo(ctx, containerID) + if err != nil { + return err + } + + // Convert the status to the expected State structure + state := oci.StatusToOCIState(status) + + stateJSON, err := json.MarshalIndent(state, "", " ") + if err != nil { + return err + } + + // Print stateJSON to stdout + fmt.Fprintf(os.Stdout, "%s", stateJSON) + + return nil +} diff --git a/src/runtime/cli/state_test.go b/src/runtime/cli/state_test.go new file mode 100644 index 000000000..db0493103 --- /dev/null +++ b/src/runtime/cli/state_test.go @@ -0,0 +1,85 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "io/ioutil" + "os" + "testing" + + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +func TestStateCliAction(t *testing.T) { + assert := assert.New(t) + + actionFunc, ok := stateCLICommand.Action.(func(ctx *cli.Context) error) + assert.True(ok) + + flagSet := flag.NewFlagSet("flag", flag.ContinueOnError) + + // without container id + flagSet.Parse([]string{"runtime"}) + ctx := createCLIContext(flagSet) + err := actionFunc(ctx) + assert.Error(err) + + // with container id + flagSet.Parse([]string{"runtime", testContainerID}) + ctx = createCLIContext(flagSet) + err = actionFunc(ctx) + assert.Error(err) +} + +func TestStateSuccessful(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testContainerID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: sandbox.ID(), + MockSandbox: sandbox, + }, + } + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + // trying with an inexistent id + err = state(context.Background(), "123456789") + assert.Error(err) + + path, err = createTempContainerIDMapping(testContainerID, sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: testContainerID, + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + err = state(context.Background(), testContainerID) + assert.NoError(err) +} diff --git a/src/runtime/cli/update.go b/src/runtime/cli/update.go new file mode 100644 index 000000000..21a2e69e8 --- /dev/null +++ b/src/runtime/cli/update.go @@ -0,0 +1,288 @@ +// Copyright (c) 2016,2017 Docker, Inc. +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "encoding/json" + "fmt" + "os" + "strconv" + + "github.com/docker/go-units" + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/kata-containers/runtime/virtcontainers/types" + + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +func i64Ptr(i int64) *int64 { return &i } +func u64Ptr(i uint64) *uint64 { return &i } +func u16Ptr(i uint16) *uint16 { return &i } + +var updateCLICommand = cli.Command{ + Name: "update", + Usage: "update container resource constraints", + ArgsUsage: ``, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "resources, r", + Value: "", + Usage: `path to the file containing the resources to update or '-' to read from 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": { + "weight": 0 + }, + "pids": { + "limit": 0 + } +} + +Note: if data is to be read from a file or the standard input, all +other options are ignored. +`, + }, + + cli.IntFlag{ + Name: "blkio-weight", + Usage: "Specifies per cgroup weight, range is from 10 to 1000", + }, + cli.StringFlag{ + Name: "cpu-period", + Usage: "CPU CFS period to be used for hardcapping (in usecs). 0 to use system default", + }, + cli.StringFlag{ + Name: "cpu-quota", + Usage: "CPU CFS hardcap limit (in usecs). Allowed cpu time in a given period", + }, + cli.StringFlag{ + Name: "cpu-share", + Usage: "CPU shares (relative weight vs. other containers)", + }, + cli.StringFlag{ + Name: "cpu-rt-period", + Usage: "CPU realtime period to be used for hardcapping (in usecs). 0 to use system default", + }, + cli.StringFlag{ + Name: "cpu-rt-runtime", + Usage: "CPU realtime hardcap limit (in usecs). Allowed cpu time in a given period", + }, + cli.StringFlag{ + Name: "cpuset-cpus", + Usage: "CPU(s) to use", + }, + cli.StringFlag{ + Name: "cpuset-mems", + Usage: "Memory node(s) to use", + }, + cli.StringFlag{ + Name: "kernel-memory", + Usage: "Kernel memory limit (in bytes)", + }, + cli.StringFlag{ + Name: "kernel-memory-tcp", + Usage: "Kernel memory limit (in bytes) for tcp buffer", + }, + cli.StringFlag{ + Name: "memory", + Usage: "Memory limit (in bytes)", + }, + cli.StringFlag{ + Name: "memory-reservation", + Usage: "Memory reservation or soft_limit (in bytes)", + }, + cli.StringFlag{ + Name: "memory-swap", + Usage: "Total memory usage (memory + swap); set '-1' to enable unlimited swap", + }, + cli.IntFlag{ + Name: "pids-limit", + Usage: "Maximum number of pids allowed in the container", + }, + cli.StringFlag{ + Name: "l3-cache-schema", + Usage: "The string of Intel RDT/CAT L3 cache schema", + }, + }, + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + span, _ := katautils.Trace(ctx, "update") + defer span.Finish() + + if !context.Args().Present() { + return fmt.Errorf("Missing container ID, should at least provide one") + } + + containerID := context.Args().First() + + kataLog = kataLog.WithField("container", containerID) + setExternalLoggers(ctx, kataLog) + span.SetTag("container", containerID) + + status, sandboxID, err := getExistingContainerInfo(ctx, containerID) + if err != nil { + return err + } + + containerID = status.ID + + kataLog = kataLog.WithFields(logrus.Fields{ + "container": containerID, + "sandbox": sandboxID, + }) + + setExternalLoggers(ctx, kataLog) + + span.SetTag("container", containerID) + span.SetTag("sandbox", sandboxID) + + // container MUST be running + if state := status.State.State; !(state == types.StateRunning || state == types.StateReady) { + return fmt.Errorf("Container %s is not running or Ready, the state is %s", containerID, state) + } + + r := specs.LinuxResources{ + Memory: &specs.LinuxMemory{ + Limit: i64Ptr(0), + Reservation: i64Ptr(0), + Swap: i64Ptr(0), + Kernel: i64Ptr(0), + KernelTCP: i64Ptr(0), + }, + CPU: &specs.LinuxCPU{ + Shares: u64Ptr(0), + Quota: i64Ptr(0), + Period: u64Ptr(0), + RealtimeRuntime: i64Ptr(0), + RealtimePeriod: u64Ptr(0), + Cpus: "", + Mems: "", + }, + BlockIO: &specs.LinuxBlockIO{ + Weight: u16Ptr(0), + }, + Pids: &specs.LinuxPids{ + Limit: 0, + }, + } + + if in := context.String("resources"); in != "" { + var ( + f *os.File + err error + ) + switch in { + case "-": + f = os.Stdin + default: + f, err = os.Open(in) + if err != nil { + return err + } + } + err = json.NewDecoder(f).Decode(&r) + if err != nil { + return err + } + } else { + if val := context.Int("blkio-weight"); val != 0 { + r.BlockIO.Weight = u16Ptr(uint16(val)) + } + if val := context.String("cpuset-cpus"); val != "" { + r.CPU.Cpus = val + } + if val := context.String("cpuset-mems"); val != "" { + r.CPU.Mems = val + } + + for _, pair := range []struct { + opt string + dest *uint64 + }{ + + {"cpu-period", r.CPU.Period}, + {"cpu-rt-period", r.CPU.RealtimePeriod}, + {"cpu-share", r.CPU.Shares}, + } { + if val := context.String(pair.opt); val != "" { + var err error + *pair.dest, err = strconv.ParseUint(val, 10, 64) + if err != nil { + return fmt.Errorf("invalid value for %s: %s", pair.opt, err) + } + } + } + for _, pair := range []struct { + opt string + dest *int64 + }{ + + {"cpu-quota", r.CPU.Quota}, + {"cpu-rt-runtime", r.CPU.RealtimeRuntime}, + } { + if val := context.String(pair.opt); val != "" { + var err error + *pair.dest, err = strconv.ParseInt(val, 10, 64) + if err != nil { + return fmt.Errorf("invalid value for %s: %s", pair.opt, err) + } + } + } + for _, pair := range []struct { + opt string + dest *int64 + }{ + {"memory", r.Memory.Limit}, + {"memory-swap", r.Memory.Swap}, + {"kernel-memory", r.Memory.Kernel}, + {"kernel-memory-tcp", r.Memory.KernelTCP}, + {"memory-reservation", r.Memory.Reservation}, + } { + if val := context.String(pair.opt); val != "" { + var v int64 + + if val != "-1" { + v, err = units.RAMInBytes(val) + if err != nil { + return fmt.Errorf("invalid value for %s: %s", pair.opt, err) + } + } else { + v = -1 + } + *pair.dest = v + } + } + r.Pids.Limit = int64(context.Int("pids-limit")) + } + + return vci.UpdateContainer(ctx, sandboxID, containerID, r) + }, +} diff --git a/src/runtime/cli/update_test.go b/src/runtime/cli/update_test.go new file mode 100644 index 000000000..089d42171 --- /dev/null +++ b/src/runtime/cli/update_test.go @@ -0,0 +1,208 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "context" + "flag" + "io/ioutil" + "os" + "testing" + + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/kata-containers/runtime/virtcontainers/types" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +func TestUpdateCLIAction(t *testing.T) { + assert := assert.New(t) + + flagSet := flag.NewFlagSet("update", flag.ContinueOnError) + flagSet.Parse([]string{"resources"}) + + // create a new fake context + ctx := createCLIContext(flagSet) + + // get Action function + actionFunc, ok := updateCLICommand.Action.(func(ctx *cli.Context) error) + assert.True(ok) + + err := actionFunc(ctx) + assert.Error(err, "Missing container ID") +} + +func TestUpdateCLIFailure(t *testing.T) { + assert := assert.New(t) + + flagSet := flag.NewFlagSet("update", flag.ContinueOnError) + ctx := createCLIContext(flagSet) + + actionFunc, ok := updateCLICommand.Action.(func(ctx *cli.Context) error) + assert.True(ok) + + // missing container ID + err := actionFunc(ctx) + assert.Error(err) + + // container info + flagSet.Parse([]string{testContainerID}) + ctx = createCLIContext(flagSet) + err = actionFunc(ctx) + assert.Error(err) + + // not running + sandbox := &vcmock.Sandbox{ + MockID: testContainerID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: sandbox.ID(), + MockSandbox: sandbox, + }, + } + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + err = actionFunc(ctx) + assert.Error(err) + + // resources file does not exist + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + State: types.ContainerState{ + State: types.StateRunning, + }, + }, nil + } + testingImpl.UpdateContainerFunc = func(ctx context.Context, sandboxID, containerID string, resources specs.LinuxResources) error { + return nil + } + defer func() { + testingImpl.UpdateContainerFunc = nil + }() + flagSet.String("resources", "/abc/123/xyz/rgb", "") + ctx = createCLIContext(flagSet) + err = actionFunc(ctx) + assert.Error(err) + + // json decode error + f, err := ioutil.TempFile("", "resources") + defer os.Remove(f.Name()) + assert.NoError(err) + assert.NotNil(f) + f.WriteString("no json") + f.Close() + flagSet.Set("resources", f.Name()) + ctx = createCLIContext(flagSet) + err = actionFunc(ctx) + assert.Error(err) + + // ParseUint Error + flagSet = flag.NewFlagSet("update", flag.ContinueOnError) + flagSet.Parse([]string{testContainerID}) + flagSet.String("cpu-period", "abcxyz", "") + ctx = createCLIContext(flagSet) + err = actionFunc(ctx) + assert.Error(err) + + // ParseInt Error + flagSet = flag.NewFlagSet("update", flag.ContinueOnError) + flagSet.Parse([]string{testContainerID}) + flagSet.String("cpu-quota", "abcxyz", "") + ctx = createCLIContext(flagSet) + err = actionFunc(ctx) + assert.Error(err) + + // RAMInBytes Error + flagSet = flag.NewFlagSet("update", flag.ContinueOnError) + flagSet.Parse([]string{testContainerID}) + flagSet.String("memory", "abcxyz", "") + ctx = createCLIContext(flagSet) + err = actionFunc(ctx) + assert.Error(err) +} + +func TestUpdateCLISuccessful(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testContainerID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: sandbox.ID(), + MockSandbox: sandbox, + }, + } + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + State: types.ContainerState{ + State: types.StateRunning, + }, + }, nil + } + testingImpl.UpdateContainerFunc = func(ctx context.Context, sandboxID, containerID string, resources specs.LinuxResources) error { + return nil + } + defer func() { + testingImpl.StatusContainerFunc = nil + testingImpl.UpdateContainerFunc = nil + }() + + path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID()) + assert.NoError(err) + defer os.RemoveAll(path) + actionFunc, ok := updateCLICommand.Action.(func(ctx *cli.Context) error) + assert.True(ok) + + flagSet := flag.NewFlagSet("update", flag.ContinueOnError) + flagSet.Parse([]string{testContainerID}) + flagSet.Int("blkio-weight", 20, "") + flagSet.String("cpuset-cpus", "0-5", "") + flagSet.String("cpuset-mems", "0-5", "") + flagSet.String("cpu-period", "1000", "") + flagSet.String("cpu-rt-period", "1000", "") + flagSet.String("cpu-share", "1000", "") + flagSet.String("cpu-quota", "1000", "") + flagSet.String("cpu-rt-runtime", "1000", "") + flagSet.String("memory", "100M", "") + flagSet.String("memory-swap", "100M", "") + flagSet.String("kernel-memory", "100M", "") + flagSet.String("kernel-memory-tcp", "100M", "") + flagSet.String("memory-reservation", "100M", "") + ctx := createCLIContext(flagSet) + err = actionFunc(ctx) + assert.NoError(err) +} diff --git a/src/runtime/cli/utils.go b/src/runtime/cli/utils.go new file mode 100644 index 000000000..f6c855c48 --- /dev/null +++ b/src/runtime/cli/utils.go @@ -0,0 +1,203 @@ +// Copyright (c) 2014 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "os" + "strconv" + "strings" + + "github.com/blang/semver" + "github.com/kata-containers/runtime/pkg/katautils" +) + +const ( + unknown = "<>" +) + +// variables to allow tests to modify the values +var ( + procVersion = "/proc/version" + osRelease = "/etc/os-release" + + // Clear Linux has a different path (for stateless support) + osReleaseClr = "/usr/lib/os-release" + + unknownVersionInfo = VersionInfo{ + Semver: unknown, + Commit: unknown, + } +) + +func getKernelVersion() (string, error) { + contents, err := katautils.GetFileContents(procVersion) + if err != nil { + return "", err + } + + fields := strings.Fields(contents) + + if len(fields) < 3 { + return "", fmt.Errorf("unexpected contents in %v", procVersion) + } + + version := fields[2] + + return version, nil +} + +// getDistroDetails returns the distributions name and version string. +// If it is not possible to determine both values an error is +// returned. +func getDistroDetails() (name, version string, err error) { + files := []string{osRelease, osReleaseClr} + name = "" + version = "" + + for _, file := range files { + contents, err := katautils.GetFileContents(file) + if err != nil { + if os.IsNotExist(err) { + continue + } + + return "", "", err + } + + lines := strings.Split(contents, "\n") + + for _, line := range lines { + if strings.HasPrefix(line, "NAME=") && name == "" { + fields := strings.Split(line, "=") + name = strings.Trim(fields[1], `"`) + } else if strings.HasPrefix(line, "VERSION_ID=") && version == "" { + fields := strings.Split(line, "=") + version = strings.Trim(fields[1], `"`) + } + } + + if name != "" && version != "" { + return name, version, nil + } + } + + if name == "" { + name = unknown + } + + if version == "" { + version = unknown + } + + return name, version, nil +} + +// genericGetCPUDetails returns the vendor and model of the CPU. +// If it is not possible to determine both values an error is +// returned. +func genericGetCPUDetails() (vendor, model string, err error) { + cpuinfo, err := getCPUInfo(procCPUInfo) + if err != nil { + return "", "", err + } + + lines := strings.Split(cpuinfo, "\n") + + for _, line := range lines { + if archCPUVendorField != "" { + if strings.HasPrefix(line, archCPUVendorField) { + fields := strings.Split(line, ":") + if len(fields) > 1 { + vendor = strings.TrimSpace(fields[1]) + } + } + } + + if archCPUModelField != "" { + if strings.HasPrefix(line, archCPUModelField) { + fields := strings.Split(line, ":") + if len(fields) > 1 { + model = strings.TrimSpace(fields[1]) + } + } + } + } + + if archCPUVendorField != "" && vendor == "" { + return "", "", fmt.Errorf("cannot find vendor field in file %v", procCPUInfo) + } + + // model name is optional + if archCPUModelField != "" && model == "" { + return "", "", fmt.Errorf("cannot find model field in file %v", procCPUInfo) + } + + return vendor, model, nil +} + +// from runC +// parseBoolOrAuto returns (nil, nil) if s is empty or "auto" +func parseBoolOrAuto(s string) (*bool, error) { + if s == "" || strings.ToLower(s) == "auto" { + return nil, nil + } + b, err := strconv.ParseBool(s) + return &b, err +} + +// constructVersionInfo constructs VersionInfo-type value from a version string +// in the format of "Kata-Component version Major.Minor.Patch-rc_xxx-Commit". +func constructVersionInfo(version string) VersionInfo { + fields := strings.Split(version, " ") + realVersion := fields[len(fields)-1] + + sv, err := semver.Make(realVersion) + if err != nil { + return unknownVersionInfo + } + + pres := strings.Split(sv.Pre[0].VersionStr, "-") + + // version contains Commit info. + if len(pres) > 1 { + return VersionInfo{ + Semver: realVersion, + Major: sv.Major, + Minor: sv.Minor, + Patch: sv.Patch, + Commit: pres[1], + } + } + + return VersionInfo{ + Semver: realVersion, + Major: sv.Major, + Minor: sv.Minor, + Patch: sv.Patch, + Commit: unknown, + } + +} + +func versionEqual(a VersionInfo, b VersionInfo) bool { + av, err := semver.Make(a.Semver) + if err != nil { + return false + } + + bv, err := semver.Make(b.Semver) + if err != nil { + return false + } + + if av.Major == bv.Major && av.Minor == bv.Minor && av.Patch == bv.Patch { + return true + } + + return false +} diff --git a/src/runtime/cli/utils_arch_base.go b/src/runtime/cli/utils_arch_base.go new file mode 100644 index 000000000..a0c6a5e71 --- /dev/null +++ b/src/runtime/cli/utils_arch_base.go @@ -0,0 +1,10 @@ +// +build !s390x +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +func archConvertStatFs(cgroupFsType int) int64 { + return int64(cgroupFsType) +} diff --git a/src/runtime/cli/utils_s390x.go b/src/runtime/cli/utils_s390x.go new file mode 100644 index 000000000..6acd5627e --- /dev/null +++ b/src/runtime/cli/utils_s390x.go @@ -0,0 +1,10 @@ +// Copyright (c) 2018 IBM +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +func archConvertStatFs(cgroupFsType int) uint32 { + return uint32(cgroupFsType) +} diff --git a/src/runtime/cli/utils_test.go b/src/runtime/cli/utils_test.go new file mode 100644 index 000000000..7fa028b57 --- /dev/null +++ b/src/runtime/cli/utils_test.go @@ -0,0 +1,220 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/stretchr/testify/assert" +) + +func TestFileExists(t *testing.T) { + dir, err := ioutil.TempDir(testDir, "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + file := filepath.Join(dir, "foo") + + assert.False(t, katautils.FileExists(file), + fmt.Sprintf("File %q should not exist", file)) + + err = createEmptyFile(file) + if err != nil { + t.Fatal(err) + } + + assert.True(t, katautils.FileExists(file), + fmt.Sprintf("File %q should exist", file)) +} + +func TestGetKernelVersion(t *testing.T) { + type testData struct { + contents string + expectedVersion string + expectError bool + } + + const validVersion = "1.2.3-4.5.x86_64" + validContents := fmt.Sprintf("Linux version %s blah blah blah ...", validVersion) + + data := []testData{ + {"", "", true}, + {"invalid contents", "", true}, + {"a b c", "c", false}, + {validContents, validVersion, false}, + } + + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + subDir := filepath.Join(tmpdir, "subdir") + err = os.MkdirAll(subDir, testDirMode) + assert.NoError(t, err) + + _, err = getKernelVersion() + assert.Error(t, err) + + file := filepath.Join(tmpdir, "proc-version") + + // override + procVersion = file + + _, err = getKernelVersion() + // ENOENT + assert.Error(t, err) + assert.True(t, os.IsNotExist(err)) + + for _, d := range data { + err := createFile(file, d.contents) + assert.NoError(t, err) + + version, err := getKernelVersion() + if d.expectError { + assert.Error(t, err, fmt.Sprintf("%+v", d)) + continue + } else { + assert.NoError(t, err, fmt.Sprintf("%+v", d)) + assert.Equal(t, d.expectedVersion, version) + } + } +} + +func TestGetDistroDetails(t *testing.T) { + type testData struct { + clrContents string + nonClrContents string + expectedName string + expectedVersion string + expectError bool + } + + const unknown = "<>" + + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + panic(err) + } + defer os.RemoveAll(tmpdir) + + testOSRelease := filepath.Join(tmpdir, "os-release") + testOSReleaseClr := filepath.Join(tmpdir, "os-release-clr") + + const clrExpectedName = "clr" + const clrExpectedVersion = "1.2.3-4" + clrContents := fmt.Sprintf(` +HELLO=world +NAME="%s" +FOO=bar +VERSION_ID="%s" +`, clrExpectedName, clrExpectedVersion) + + const nonClrExpectedName = "not-clr" + const nonClrExpectedVersion = "999" + nonClrContents := fmt.Sprintf(` +HELLO=world +NAME="%s" +FOO=bar +VERSION_ID="%s" +`, nonClrExpectedName, nonClrExpectedVersion) + + subDir := filepath.Join(tmpdir, "subdir") + err = os.MkdirAll(subDir, testDirMode) + assert.NoError(t, err) + + // override + osRelease = subDir + + _, _, err = getDistroDetails() + assert.Error(t, err) + + // override + osRelease = testOSRelease + osReleaseClr = testOSReleaseClr + + _, _, err = getDistroDetails() + // ENOENT + assert.NoError(t, err) + + data := []testData{ + {"", "", unknown, unknown, false}, + {"invalid", "", unknown, unknown, false}, + {clrContents, "", clrExpectedName, clrExpectedVersion, false}, + {"", nonClrContents, nonClrExpectedName, nonClrExpectedVersion, false}, + {clrContents, nonClrContents, nonClrExpectedName, nonClrExpectedVersion, false}, + } + + for _, d := range data { + err := createFile(osRelease, d.nonClrContents) + assert.NoError(t, err) + + err = createFile(osReleaseClr, d.clrContents) + assert.NoError(t, err) + + name, version, err := getDistroDetails() + if d.expectError { + assert.Error(t, err, fmt.Sprintf("%+v", d)) + continue + } else { + assert.NoError(t, err, fmt.Sprintf("%+v", d)) + assert.Equal(t, d.expectedName, name) + assert.Equal(t, d.expectedVersion, version) + } + } +} + +func TestUtilsRunCommand(t *testing.T) { + output, err := katautils.RunCommand([]string{"true"}) + assert.NoError(t, err) + assert.Equal(t, "", output) +} + +func TestUtilsRunCommandCaptureStdout(t *testing.T) { + output, err := katautils.RunCommand([]string{"echo", "hello"}) + assert.NoError(t, err) + assert.Equal(t, "hello", output) +} + +func TestUtilsRunCommandIgnoreStderr(t *testing.T) { + args := []string{"/bin/sh", "-c", "echo foo >&2;exit 0"} + + output, err := katautils.RunCommand(args) + assert.NoError(t, err) + assert.Equal(t, "", output) +} + +func TestUtilsRunCommandInvalidCmds(t *testing.T) { + invalidCommands := [][]string{ + {""}, + {"", ""}, + {" "}, + {" ", " "}, + {" ", ""}, + {"\\"}, + {"/"}, + {"/.."}, + {"../"}, + {"/tmp"}, + {"\t"}, + {"\n"}, + {"false"}, + } + + for _, args := range invalidCommands { + output, err := katautils.RunCommand(args) + assert.Error(t, err) + assert.Equal(t, "", output) + } +} diff --git a/src/runtime/cli/version.go b/src/runtime/cli/version.go new file mode 100644 index 000000000..1862b4a1b --- /dev/null +++ b/src/runtime/cli/version.go @@ -0,0 +1,28 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/urfave/cli" +) + +var versionCLICommand = cli.Command{ + Name: "version", + Usage: "display version details", + Action: func(context *cli.Context) error { + ctx, err := cliContextToContext(context) + if err != nil { + return err + } + + span, _ := katautils.Trace(ctx, "version") + defer span.Finish() + + cli.VersionPrinter(context) + return nil + }, +} diff --git a/src/runtime/cli/version_test.go b/src/runtime/cli/version_test.go new file mode 100644 index 000000000..936392c29 --- /dev/null +++ b/src/runtime/cli/version_test.go @@ -0,0 +1,53 @@ +// Copyright (c) 2017 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli" +) + +func TestVersion(t *testing.T) { + const testAppName = "foo" + const testAppVersion = "0.1.0" + + resetCLIGlobals() + + savedRuntimeVersionFunc := runtimeVersion + + defer func() { + runtimeVersion = savedRuntimeVersionFunc + }() + + runtimeVersion := func() string { + return testAppVersion + } + + ctx := createCLIContext(nil) + ctx.App.Name = testAppName + ctx.App.Version = runtimeVersion() + + fn, ok := versionCLICommand.Action.(func(context *cli.Context) error) + assert.True(t, ok) + + tmpfile, err := ioutil.TempFile("", "") + assert.NoError(t, err) + defer os.Remove(tmpfile.Name()) + + ctx.App.Writer = tmpfile + + err = fn(ctx) + assert.NoError(t, err) + + pattern := fmt.Sprintf("%s.*version.*%s", testAppName, testAppVersion) + err = grep(pattern, tmpfile.Name()) + assert.NoError(t, err) +} diff --git a/src/runtime/containerd-shim-v2/container.go b/src/runtime/containerd-shim-v2/container.go new file mode 100644 index 000000000..6b5e99482 --- /dev/null +++ b/src/runtime/containerd-shim-v2/container.go @@ -0,0 +1,66 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "time" + + "github.com/containerd/containerd/api/types/task" + "github.com/containerd/containerd/errdefs" + taskAPI "github.com/containerd/containerd/runtime/v2/task" + "github.com/opencontainers/runtime-spec/specs-go" + + vc "github.com/kata-containers/runtime/virtcontainers" +) + +type container struct { + s *service + ttyio *ttyIO + spec *specs.Spec + exitTime time.Time + execs map[string]*exec + exitIOch chan struct{} + exitCh chan uint32 + id string + stdin string + stdout string + stderr string + bundle string + cType vc.ContainerType + exit uint32 + status task.Status + terminal bool + mounted bool +} + +func newContainer(s *service, r *taskAPI.CreateTaskRequest, containerType vc.ContainerType, spec *specs.Spec, mounted bool) (*container, error) { + if r == nil { + return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, " CreateTaskRequest points to nil") + } + + // in order to avoid deferencing a nil pointer in test + if spec == nil { + spec = &specs.Spec{} + } + + c := &container{ + s: s, + spec: spec, + id: r.ID, + bundle: r.Bundle, + stdin: r.Stdin, + stdout: r.Stdout, + stderr: r.Stderr, + terminal: r.Terminal, + cType: containerType, + execs: make(map[string]*exec), + status: task.StatusCreated, + exitIOch: make(chan struct{}), + exitCh: make(chan uint32, 1), + mounted: mounted, + } + return c, nil +} diff --git a/src/runtime/containerd-shim-v2/container_test.go b/src/runtime/containerd-shim-v2/container_test.go new file mode 100644 index 000000000..1cb098449 --- /dev/null +++ b/src/runtime/containerd-shim-v2/container_test.go @@ -0,0 +1,41 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "testing" + + taskAPI "github.com/containerd/containerd/runtime/v2/task" + "github.com/stretchr/testify/assert" +) + +func TestNewContainer(t *testing.T) { + assert := assert.New(t) + + _, err := newContainer(nil, nil, "", nil, false) + + assert.Error(err) +} + +func TestGetExec(t *testing.T) { + assert := assert.New(t) + + r := &taskAPI.CreateTaskRequest{} + + c, err := newContainer(nil, r, "", nil, true) + assert.NoError(err) + + _, err = c.getExec("") + assert.Error(err) + + c.execs = make(map[string]*exec) + _, err = c.getExec("") + assert.Error(err) + + c.execs[TestID] = &exec{} + _, err = c.getExec(TestID) + assert.NoError(err) +} diff --git a/src/runtime/containerd-shim-v2/create.go b/src/runtime/containerd-shim-v2/create.go new file mode 100644 index 000000000..affdbae2b --- /dev/null +++ b/src/runtime/containerd-shim-v2/create.go @@ -0,0 +1,220 @@ +// Copyright (c) 2014,2015,2016 Docker, Inc. +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "fmt" + "os" + "path/filepath" + + containerd_types "github.com/containerd/containerd/api/types" + "github.com/containerd/containerd/mount" + taskAPI "github.com/containerd/containerd/runtime/v2/task" + "github.com/containerd/typeurl" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + // only register the proto type + _ "github.com/containerd/containerd/runtime/linux/runctypes" + crioption "github.com/containerd/cri-containerd/pkg/api/runtimeoptions/v1" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" +) + +func create(ctx context.Context, s *service, r *taskAPI.CreateTaskRequest) (*container, error) { + rootFs := vc.RootFs{} + if len(r.Rootfs) == 1 { + m := r.Rootfs[0] + rootFs.Source = m.Source + rootFs.Type = m.Type + rootFs.Options = m.Options + } + + detach := !r.Terminal + ociSpec, bundlePath, err := loadSpec(r) + if err != nil { + return nil, err + } + + containerType, err := oci.ContainerType(*ociSpec) + if err != nil { + return nil, err + } + + disableOutput := noNeedForOutput(detach, ociSpec.Process.Terminal) + rootfs := filepath.Join(r.Bundle, "rootfs") + + switch containerType { + case vc.PodSandbox: + if s.sandbox != nil { + return nil, fmt.Errorf("cannot create another sandbox in sandbox: %s", s.sandbox.ID()) + } + + _, err := loadRuntimeConfig(s, r, ociSpec.Annotations) + if err != nil { + return nil, err + } + + if rootFs.Mounted, err = checkAndMount(s, r); err != nil { + return nil, err + } + + defer func() { + if err != nil && rootFs.Mounted { + if err2 := mount.UnmountAll(rootfs, 0); err2 != nil { + logrus.WithError(err2).Warn("failed to cleanup rootfs mount") + } + } + }() + + katautils.HandleFactory(ctx, vci, s.config) + + // Pass service's context instead of local ctx to CreateSandbox(), since local + // ctx will be canceled after this rpc service call, but the sandbox will live + // across multiple rpc service calls. + // + sandbox, _, err := katautils.CreateSandbox(s.ctx, vci, *ociSpec, *s.config, rootFs, r.ID, bundlePath, "", disableOutput, false, true) + if err != nil { + return nil, err + } + s.sandbox = sandbox + + case vc.PodContainer: + if s.sandbox == nil { + return nil, fmt.Errorf("BUG: Cannot start the container, since the sandbox hasn't been created") + } + + if rootFs.Mounted, err = checkAndMount(s, r); err != nil { + return nil, err + } + + defer func() { + if err != nil && rootFs.Mounted { + if err2 := mount.UnmountAll(rootfs, 0); err2 != nil { + logrus.WithError(err2).Warn("failed to cleanup rootfs mount") + } + } + }() + + _, err = katautils.CreateContainer(ctx, vci, s.sandbox, *ociSpec, rootFs, r.ID, bundlePath, "", disableOutput, true) + if err != nil { + return nil, err + } + } + + container, err := newContainer(s, r, containerType, ociSpec, rootFs.Mounted) + if err != nil { + return nil, err + } + + return container, nil +} + +func loadSpec(r *taskAPI.CreateTaskRequest) (*specs.Spec, string, error) { + // Checks the MUST and MUST NOT from OCI runtime specification + bundlePath, err := validBundle(r.ID, r.Bundle) + if err != nil { + return nil, "", err + } + + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + if err != nil { + return nil, "", err + } + + // Todo: + // Since there is a bug in kata for sharedPidNs, here to + // remove the pidns to disable the sharePidNs temporarily, + // once kata fixed this issue, we can remove this line. + // For the bug, please see: + // https://github.com/kata-containers/runtime/issues/930 + removeNamespace(&ociSpec, specs.PIDNamespace) + + return &ociSpec, bundlePath, nil +} + +// Config override ordering(high to low): +// 1. podsandbox annotation +// 2. shimv2 create task option +// 3. environment +func loadRuntimeConfig(s *service, r *taskAPI.CreateTaskRequest, anno map[string]string) (*oci.RuntimeConfig, error) { + configPath := oci.GetSandboxConfigPath(anno) + if configPath == "" && r.Options != nil { + v, err := typeurl.UnmarshalAny(r.Options) + if err != nil { + return nil, err + } + option, ok := v.(*crioption.Options) + // cri default runtime handler will pass a linux runc options, + // and we'll ignore it. + if ok { + configPath = option.ConfigPath + } + } + + // Try to get the config file from the env KATA_CONF_FILE + if configPath == "" { + configPath = os.Getenv("KATA_CONF_FILE") + } + + _, runtimeConfig, err := katautils.LoadConfiguration(configPath, false, true) + if err != nil { + return nil, err + } + + // For the unit test, the config will be predefined + if s.config == nil { + s.config = &runtimeConfig + } + + return &runtimeConfig, nil +} + +func checkAndMount(s *service, r *taskAPI.CreateTaskRequest) (bool, error) { + if len(r.Rootfs) == 1 { + m := r.Rootfs[0] + + if katautils.IsBlockDevice(m.Source) && !s.config.HypervisorConfig.DisableBlockDeviceUse { + return false, nil + } + } + rootfs := filepath.Join(r.Bundle, "rootfs") + if err := doMount(r.Rootfs, rootfs); err != nil { + return false, err + } + return true, nil +} + +func doMount(mounts []*containerd_types.Mount, rootfs string) error { + if len(mounts) == 0 { + return nil + } + + if _, err := os.Stat(rootfs); os.IsNotExist(err) { + if err := os.Mkdir(rootfs, 0711); err != nil { + return err + } + } + + for _, rm := range mounts { + m := &mount.Mount{ + Type: rm.Type, + Source: rm.Source, + Options: rm.Options, + } + if err := m.Mount(rootfs); err != nil { + return errors.Wrapf(err, "failed to mount rootfs component %v", m) + } + } + return nil +} diff --git a/src/runtime/containerd-shim-v2/create_test.go b/src/runtime/containerd-shim-v2/create_test.go new file mode 100644 index 000000000..bf7cd4321 --- /dev/null +++ b/src/runtime/containerd-shim-v2/create_test.go @@ -0,0 +1,498 @@ +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "testing" + + "github.com/containerd/containerd/namespaces" + taskAPI "github.com/containerd/containerd/runtime/v2/task" + crioption "github.com/containerd/cri-containerd/pkg/api/runtimeoptions/v1" + "github.com/containerd/typeurl" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" +) + +func TestCreateSandboxSuccess(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + MockContainers: []*vcmock.Container{ + {MockID: testContainerID}, + }, + } + + testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.CreateSandboxFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + // Force sandbox-type container + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeSandbox + + // Set a limit to ensure processCgroupsPath() considers the + // cgroup part of the spec + limit := int64(1024 * 1024) + spec.Linux.Resources.Memory = &specs.LinuxMemory{ + Limit: &limit, + } + + // Rewrite the file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + s := &service{ + id: testSandboxID, + containers: make(map[string]*container), + config: &runtimeConfig, + ctx: context.Background(), + } + + req := &taskAPI.CreateTaskRequest{ + ID: testSandboxID, + Bundle: bundlePath, + Terminal: true, + } + + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + _, err = s.Create(ctx, req) + assert.NoError(err) +} + +func TestCreateSandboxFail(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + s := &service{ + id: testSandboxID, + containers: make(map[string]*container), + config: &runtimeConfig, + ctx: context.Background(), + } + + req := &taskAPI.CreateTaskRequest{ + ID: testSandboxID, + Bundle: bundlePath, + Terminal: true, + } + + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + _, err = s.Create(ctx, req) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) +} + +func TestCreateSandboxConfigFail(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + quota := int64(0) + limit := int64(0) + + spec.Linux.Resources.Memory = &specs.LinuxMemory{ + Limit: &limit, + } + + // specify an invalid spec + spec.Linux.Resources.CPU = &specs.LinuxCPU{ + Quota: "a, + } + + s := &service{ + id: testSandboxID, + containers: make(map[string]*container), + config: &runtimeConfig, + ctx: context.Background(), + } + + req := &taskAPI.CreateTaskRequest{ + ID: testSandboxID, + Bundle: bundlePath, + Terminal: true, + } + + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + _, err = s.Create(ctx, req) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) +} + +func TestCreateContainerSuccess(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + testingImpl.CreateContainerFunc = func(ctx context.Context, sandboxID string, containerConfig vc.ContainerConfig) (vc.VCSandbox, vc.VCContainer, error) { + return sandbox, &vcmock.Container{}, nil + } + + defer func() { + testingImpl.CreateContainerFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + // set expected container type and sandboxID + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer + spec.Annotations[testSandboxIDAnnotation] = testSandboxID + + // rewrite file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + s := &service{ + id: testContainerID, + sandbox: sandbox, + containers: make(map[string]*container), + config: &runtimeConfig, + ctx: context.Background(), + } + + req := &taskAPI.CreateTaskRequest{ + ID: testContainerID, + Bundle: bundlePath, + Terminal: true, + } + + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + _, err = s.Create(ctx, req) + assert.NoError(err) +} + +func TestCreateContainerFail(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer + spec.Annotations[testSandboxIDAnnotation] = testSandboxID + + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + // doesn't create sandbox first + s := &service{ + id: testContainerID, + containers: make(map[string]*container), + config: &runtimeConfig, + ctx: context.Background(), + } + + req := &taskAPI.CreateTaskRequest{ + ID: testContainerID, + Bundle: bundlePath, + Terminal: true, + } + + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + _, err = s.Create(ctx, req) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestCreateContainerConfigFail(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + testingImpl.CreateContainerFunc = func(ctx context.Context, sandboxID string, containerConfig vc.ContainerConfig) (vc.VCSandbox, vc.VCContainer, error) { + return sandbox, &vcmock.Container{}, nil + } + + defer func() { + testingImpl.CreateContainerFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(katautils.FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + // set the error containerType + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = "errorType" + spec.Annotations[testSandboxIDAnnotation] = testSandboxID + + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + s := &service{ + id: testContainerID, + sandbox: sandbox, + containers: make(map[string]*container), + config: &runtimeConfig, + ctx: context.Background(), + } + + req := &taskAPI.CreateTaskRequest{ + ID: testContainerID, + Bundle: bundlePath, + Terminal: true, + } + + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + _, err = s.Create(ctx, req) + assert.Error(err) +} + +func createAllRuntimeConfigFiles(dir, hypervisor string) (config string, err error) { + if dir == "" { + return "", fmt.Errorf("BUG: need directory") + } + + if hypervisor == "" { + return "", fmt.Errorf("BUG: need hypervisor") + } + + hypervisorPath := path.Join(dir, "hypervisor") + kernelPath := path.Join(dir, "kernel") + kernelParams := "foo=bar xyz" + imagePath := path.Join(dir, "image") + shimPath := path.Join(dir, "shim") + proxyPath := path.Join(dir, "proxy") + netmonPath := path.Join(dir, "netmon") + logDir := path.Join(dir, "logs") + logPath := path.Join(logDir, "runtime.log") + machineType := "machineType" + disableBlockDevice := true + blockDeviceDriver := "virtio-scsi" + enableIOThreads := true + hotplugVFIOOnRootBus := true + pcieRootPort := uint32(2) + disableNewNetNs := false + sharedFS := "virtio-9p" + + configFileOptions := ktu.RuntimeConfigOptions{ + Hypervisor: "qemu", + HypervisorPath: hypervisorPath, + KernelPath: kernelPath, + ImagePath: imagePath, + KernelParams: kernelParams, + MachineType: machineType, + ShimPath: shimPath, + ProxyPath: proxyPath, + NetmonPath: netmonPath, + LogPath: logPath, + DisableBlock: disableBlockDevice, + BlockDeviceDriver: blockDeviceDriver, + EnableIOThreads: enableIOThreads, + HotplugVFIOOnRootBus: hotplugVFIOOnRootBus, + PCIeRootPort: pcieRootPort, + DisableNewNetNs: disableNewNetNs, + SharedFS: sharedFS, + } + + runtimeConfigFileData := ktu.MakeRuntimeConfigFileData(configFileOptions) + + configPath := path.Join(dir, "runtime.toml") + err = ioutil.WriteFile(configPath, []byte(runtimeConfigFileData), os.FileMode(0640)) + if err != nil { + return "", err + } + + files := []string{hypervisorPath, kernelPath, imagePath, shimPath, proxyPath} + + for _, file := range files { + // create the resource (which must be >0 bytes) + err := ioutil.WriteFile(file, []byte("foo"), os.FileMode(0640)) + if err != nil { + return "", err + } + } + + return configPath, nil +} + +func TestCreateLoadRuntimeConfig(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + config, err := createAllRuntimeConfigFiles(tmpdir, "qemu") + assert.NoError(err) + + s := &service{ + id: testSandboxID, + ctx: context.Background(), + } + r := &taskAPI.CreateTaskRequest{} + anno := make(map[string]string) + + // set all to fake path + fakeConfig := "foobar" + anno[vcAnnotations.SandboxConfigPathKey] = fakeConfig + option := &crioption.Options{ConfigPath: fakeConfig} + r.Options, err = typeurl.MarshalAny(option) + assert.NoError(err) + err = os.Setenv("KATA_CONF_FILE", fakeConfig) + assert.NoError(err) + defer os.Setenv("KATA_CONF_FILE", "") + + // fake config should fail + _, err = loadRuntimeConfig(s, r, anno) + assert.Error(err) + + // 1. podsandbox annotation + anno[vcAnnotations.SandboxConfigPathKey] = config + _, err = loadRuntimeConfig(s, r, anno) + assert.NoError(err) + anno[vcAnnotations.SandboxConfigPathKey] = "" + + // 2. shimv2 create task option + option.ConfigPath = config + r.Options, err = typeurl.MarshalAny(option) + assert.NoError(err) + _, err = loadRuntimeConfig(s, r, anno) + assert.NoError(err) + option.ConfigPath = "" + r.Options, err = typeurl.MarshalAny(option) + assert.NoError(err) + + // 3. environment + err = os.Setenv("KATA_CONF_FILE", config) + assert.NoError(err) + _, err = loadRuntimeConfig(s, r, anno) + assert.NoError(err) +} diff --git a/src/runtime/containerd-shim-v2/delete.go b/src/runtime/containerd-shim-v2/delete.go new file mode 100644 index 000000000..d1a47e43d --- /dev/null +++ b/src/runtime/containerd-shim-v2/delete.go @@ -0,0 +1,52 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "path" + + "github.com/containerd/containerd/mount" + "github.com/kata-containers/runtime/pkg/katautils" + "github.com/kata-containers/runtime/virtcontainers/types" + + "github.com/sirupsen/logrus" +) + +func deleteContainer(ctx context.Context, s *service, c *container) error { + status, err := s.sandbox.StatusContainer(c.id) + if err != nil && !isNotFound(err) { + return err + } + if !c.cType.IsSandbox() && err == nil { + if status.State.State != types.StateStopped { + _, err = s.sandbox.StopContainer(c.id, false) + if err != nil { + return err + } + } + + if _, err = s.sandbox.DeleteContainer(c.id); err != nil { + return err + } + } + + // Run post-stop OCI hooks. + if err := katautils.PostStopHooks(ctx, *c.spec, s.sandbox.ID(), c.bundle); err != nil { + return err + } + + if c.mounted { + rootfs := path.Join(c.bundle, "rootfs") + if err := mount.UnmountAll(rootfs, 0); err != nil { + logrus.WithError(err).Warn("failed to cleanup rootfs mount") + } + } + + delete(s.containers, c.id) + + return nil +} diff --git a/src/runtime/containerd-shim-v2/delete_test.go b/src/runtime/containerd-shim-v2/delete_test.go new file mode 100644 index 000000000..3ecc71c51 --- /dev/null +++ b/src/runtime/containerd-shim-v2/delete_test.go @@ -0,0 +1,61 @@ +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + taskAPI "github.com/containerd/containerd/runtime/v2/task" + "github.com/stretchr/testify/assert" + + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" +) + +func TestDeleteContainerSuccessAndFail(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + rootPath, bundlePath := testConfigSetup(t) + defer os.RemoveAll(rootPath) + _, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + s := &service{ + id: testSandboxID, + sandbox: sandbox, + containers: make(map[string]*container), + } + + reqCreate := &taskAPI.CreateTaskRequest{ + ID: testContainerID, + } + s.containers[testContainerID], err = newContainer(s, reqCreate, "", nil, true) + assert.NoError(err) +} + +func testConfigSetup(t *testing.T) (rootPath string, bundlePath string) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + + bundlePath = filepath.Join(tmpdir, "bundle") + err = os.MkdirAll(bundlePath, testDirMode) + assert.NoError(err) + + err = createOCIConfig(bundlePath) + assert.NoError(err) + + return tmpdir, bundlePath +} diff --git a/src/runtime/containerd-shim-v2/errors.go b/src/runtime/containerd-shim-v2/errors.go new file mode 100644 index 000000000..adfe5c052 --- /dev/null +++ b/src/runtime/containerd-shim-v2/errors.go @@ -0,0 +1,62 @@ +// Copyright (c) 2019 hyper.sh +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "strings" + "syscall" + + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + vc "github.com/kata-containers/runtime/virtcontainers/pkg/types" +) + +// toGRPC maps the virtcontainers error into a grpc error, +// using the original error message as a description. +func toGRPC(err error) error { + if err == nil { + return nil + } + + if isGRPCError(err) { + // error has already been mapped to grpc + return err + } + + err = errors.Cause(err) + switch { + case isInvalidArgument(err): + return status.Errorf(codes.InvalidArgument, err.Error()) + case isNotFound(err): + return status.Errorf(codes.NotFound, err.Error()) + } + + return err +} + +// toGRPCf maps the error to grpc error codes, assembling the formatting string +// and combining it with the target error string. +func toGRPCf(err error, format string, args ...interface{}) error { + return toGRPC(errors.Wrapf(err, format, args...)) +} + +func isGRPCError(err error) bool { + _, ok := status.FromError(err) + return ok +} + +func isInvalidArgument(err error) bool { + return err == vc.ErrNeedSandbox || err == vc.ErrNeedSandboxID || + err == vc.ErrNeedContainerID || err == vc.ErrNeedState || + err == syscall.EINVAL +} + +func isNotFound(err error) bool { + return err == vc.ErrNoSuchContainer || err == syscall.ENOENT || + strings.Contains(err.Error(), "not found") || strings.Contains(err.Error(), "not exist") +} diff --git a/src/runtime/containerd-shim-v2/errors_test.go b/src/runtime/containerd-shim-v2/errors_test.go new file mode 100644 index 000000000..99168f154 --- /dev/null +++ b/src/runtime/containerd-shim-v2/errors_test.go @@ -0,0 +1,29 @@ +// Copyright (c) 2019 hyper.sh +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "syscall" + "testing" + + vc "github.com/kata-containers/runtime/virtcontainers/pkg/types" + "github.com/stretchr/testify/assert" +) + +func TestToGRPC(t *testing.T) { + assert := assert.New(t) + + for _, err := range []error{vc.ErrNeedSandbox, vc.ErrNeedSandboxID, + vc.ErrNeedContainerID, vc.ErrNeedState, syscall.EINVAL, vc.ErrNoSuchContainer, syscall.ENOENT} { + assert.False(isGRPCError(err)) + err = toGRPC(err) + assert.True(isGRPCError(err)) + err = toGRPC(err) + assert.True(isGRPCError(err)) + err = toGRPCf(err, "appending") + assert.True(isGRPCError(err)) + } +} diff --git a/src/runtime/containerd-shim-v2/exec.go b/src/runtime/containerd-shim-v2/exec.go new file mode 100644 index 000000000..fcbb95e88 --- /dev/null +++ b/src/runtime/containerd-shim-v2/exec.go @@ -0,0 +1,135 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "fmt" + "strings" + "time" + + "github.com/containerd/containerd/api/types/task" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/typeurl" + googleProtobuf "github.com/gogo/protobuf/types" + "github.com/kata-containers/runtime/virtcontainers/types" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +type exec struct { + container *container + cmds *types.Cmd + tty *tty + ttyio *ttyIO + id string + + exitCode int32 + + status task.Status + + exitIOch chan struct{} + exitCh chan uint32 + + exitTime time.Time +} + +type tty struct { + stdin string + stdout string + stderr string + height uint32 + width uint32 + terminal bool +} + +func getEnvs(envs []string) []types.EnvVar { + var vcEnvs = []types.EnvVar{} + var env types.EnvVar + + for _, v := range envs { + pair := strings.SplitN(v, "=", 2) + + if len(pair) == 2 { + env = types.EnvVar{Var: pair[0], Value: pair[1]} + } else if len(pair) == 1 { + env = types.EnvVar{Var: pair[0], Value: ""} + } + + vcEnvs = append(vcEnvs, env) + } + + return vcEnvs +} + +func newExec(c *container, stdin, stdout, stderr string, terminal bool, jspec *googleProtobuf.Any) (*exec, error) { + var height uint32 + var width uint32 + + if jspec == nil { + return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "googleProtobuf.Any points to nil") + } + + // process exec request + var spec *specs.Process + v, err := typeurl.UnmarshalAny(jspec) + if err != nil { + return nil, err + } + spec, ok := v.(*specs.Process) + if !ok { + return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "Get an invalid spec type") + } + + if spec.ConsoleSize != nil { + height = uint32(spec.ConsoleSize.Height) + width = uint32(spec.ConsoleSize.Width) + } + + tty := &tty{ + stdin: stdin, + stdout: stdout, + stderr: stderr, + height: height, + width: width, + terminal: terminal, + } + + cmds := &types.Cmd{ + Args: spec.Args, + Envs: getEnvs(spec.Env), + User: fmt.Sprintf("%d", spec.User.UID), + PrimaryGroup: fmt.Sprintf("%d", spec.User.GID), + WorkDir: spec.Cwd, + Interactive: terminal, + Detach: !terminal, + NoNewPrivileges: spec.NoNewPrivileges, + } + + exec := &exec{ + container: c, + cmds: cmds, + tty: tty, + exitCode: exitCode255, + exitIOch: make(chan struct{}), + exitCh: make(chan uint32, 1), + status: task.StatusCreated, + } + + return exec, nil +} + +func (c *container) getExec(id string) (*exec, error) { + if c.execs == nil { + return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "exec does not exist %s", id) + } + + exec := c.execs[id] + + if exec == nil { + return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "exec does not exist %s", id) + } + + return exec, nil +} diff --git a/src/runtime/containerd-shim-v2/exec_test.go b/src/runtime/containerd-shim-v2/exec_test.go new file mode 100644 index 000000000..36106720d --- /dev/null +++ b/src/runtime/containerd-shim-v2/exec_test.go @@ -0,0 +1,50 @@ +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "testing" + + "github.com/containerd/containerd/namespaces" + + taskAPI "github.com/containerd/containerd/runtime/v2/task" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + + "github.com/stretchr/testify/assert" +) + +func TestExecNoSpecFail(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + s := &service{ + id: testSandboxID, + sandbox: sandbox, + containers: make(map[string]*container), + } + + reqCreate := &taskAPI.CreateTaskRequest{ + ID: testContainerID, + } + + var err error + s.containers[testContainerID], err = newContainer(s, reqCreate, "", nil, false) + assert.NoError(err) + + reqExec := &taskAPI.ExecProcessRequest{ + ID: testContainerID, + ExecID: testContainerID, + } + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + + _, err = s.Exec(ctx, reqExec) + assert.Error(err) +} diff --git a/src/runtime/containerd-shim-v2/metrics.go b/src/runtime/containerd-shim-v2/metrics.go new file mode 100644 index 000000000..c936966bd --- /dev/null +++ b/src/runtime/containerd-shim-v2/metrics.go @@ -0,0 +1,192 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "github.com/containerd/cgroups" + "github.com/containerd/typeurl" + + google_protobuf "github.com/gogo/protobuf/types" + vc "github.com/kata-containers/runtime/virtcontainers" +) + +func marshalMetrics(s *service, containerID string) (*google_protobuf.Any, error) { + stats, err := s.sandbox.StatsContainer(containerID) + if err != nil { + return nil, err + } + + metrics := statsToMetrics(&stats) + + data, err := typeurl.MarshalAny(metrics) + if err != nil { + return nil, err + } + + return data, nil +} + +func statsToMetrics(stats *vc.ContainerStats) *cgroups.Metrics { + metrics := &cgroups.Metrics{} + + if stats.CgroupStats != nil { + metrics = &cgroups.Metrics{ + Hugetlb: setHugetlbStats(stats.CgroupStats.HugetlbStats), + Pids: setPidsStats(stats.CgroupStats.PidsStats), + CPU: setCPUStats(stats.CgroupStats.CPUStats), + Memory: setMemoryStats(stats.CgroupStats.MemoryStats), + Blkio: setBlkioStats(stats.CgroupStats.BlkioStats), + } + } + + metrics.Network = setNetworkStats(stats.NetworkStats) + + return metrics +} + +func setHugetlbStats(vcHugetlb map[string]vc.HugetlbStats) []*cgroups.HugetlbStat { + var hugetlbStats []*cgroups.HugetlbStat + for _, v := range vcHugetlb { + hugetlbStats = append( + hugetlbStats, + &cgroups.HugetlbStat{ + Usage: v.Usage, + Max: v.MaxUsage, + Failcnt: v.Failcnt, + }) + } + + return hugetlbStats +} + +func setPidsStats(vcPids vc.PidsStats) *cgroups.PidsStat { + pidsStats := &cgroups.PidsStat{ + Current: vcPids.Current, + Limit: vcPids.Limit, + } + + return pidsStats +} + +func setCPUStats(vcCPU vc.CPUStats) *cgroups.CPUStat { + + var perCPU []uint64 + perCPU = append(perCPU, vcCPU.CPUUsage.PercpuUsage...) + + cpuStats := &cgroups.CPUStat{ + Usage: &cgroups.CPUUsage{ + Total: vcCPU.CPUUsage.TotalUsage, + Kernel: vcCPU.CPUUsage.UsageInKernelmode, + User: vcCPU.CPUUsage.UsageInUsermode, + PerCPU: perCPU, + }, + Throttling: &cgroups.Throttle{ + Periods: vcCPU.ThrottlingData.Periods, + ThrottledPeriods: vcCPU.ThrottlingData.ThrottledPeriods, + ThrottledTime: vcCPU.ThrottlingData.ThrottledTime, + }, + } + + return cpuStats +} + +func setMemoryStats(vcMemory vc.MemoryStats) *cgroups.MemoryStat { + memoryStats := &cgroups.MemoryStat{ + Usage: &cgroups.MemoryEntry{ + Limit: vcMemory.Usage.Limit, + Usage: vcMemory.Usage.Usage, + Max: vcMemory.Usage.MaxUsage, + Failcnt: vcMemory.Usage.Failcnt, + }, + Swap: &cgroups.MemoryEntry{ + Limit: vcMemory.SwapUsage.Limit, + Usage: vcMemory.SwapUsage.Usage, + Max: vcMemory.SwapUsage.MaxUsage, + Failcnt: vcMemory.SwapUsage.Failcnt, + }, + Kernel: &cgroups.MemoryEntry{ + Limit: vcMemory.KernelUsage.Limit, + Usage: vcMemory.KernelUsage.Usage, + Max: vcMemory.KernelUsage.MaxUsage, + Failcnt: vcMemory.KernelUsage.Failcnt, + }, + KernelTCP: &cgroups.MemoryEntry{ + Limit: vcMemory.KernelTCPUsage.Limit, + Usage: vcMemory.KernelTCPUsage.Usage, + Max: vcMemory.KernelTCPUsage.MaxUsage, + Failcnt: vcMemory.KernelTCPUsage.Failcnt, + }, + } + + if vcMemory.UseHierarchy { + memoryStats.Cache = vcMemory.Stats["total_cache"] + memoryStats.RSS = vcMemory.Stats["total_rss"] + memoryStats.MappedFile = vcMemory.Stats["total_mapped_file"] + } else { + memoryStats.Cache = vcMemory.Stats["cache"] + memoryStats.RSS = vcMemory.Stats["rss"] + memoryStats.MappedFile = vcMemory.Stats["mapped_file"] + } + if v, ok := vcMemory.Stats["pgfault"]; ok { + memoryStats.PgFault = v + } + if v, ok := vcMemory.Stats["pgmajfault"]; ok { + memoryStats.PgMajFault = v + } + if v, ok := vcMemory.Stats["total_inactive_file"]; ok { + memoryStats.TotalInactiveFile = v + } + + return memoryStats +} + +func setBlkioStats(vcBlkio vc.BlkioStats) *cgroups.BlkIOStat { + blkioStats := &cgroups.BlkIOStat{ + IoServiceBytesRecursive: copyBlkio(vcBlkio.IoServiceBytesRecursive), + IoServicedRecursive: copyBlkio(vcBlkio.IoServicedRecursive), + IoQueuedRecursive: copyBlkio(vcBlkio.IoQueuedRecursive), + SectorsRecursive: copyBlkio(vcBlkio.SectorsRecursive), + IoServiceTimeRecursive: copyBlkio(vcBlkio.IoServiceTimeRecursive), + IoWaitTimeRecursive: copyBlkio(vcBlkio.IoWaitTimeRecursive), + IoMergedRecursive: copyBlkio(vcBlkio.IoMergedRecursive), + IoTimeRecursive: copyBlkio(vcBlkio.IoTimeRecursive), + } + + return blkioStats +} + +func copyBlkio(s []vc.BlkioStatEntry) []*cgroups.BlkIOEntry { + ret := make([]*cgroups.BlkIOEntry, len(s)) + for i, v := range s { + ret[i] = &cgroups.BlkIOEntry{ + Op: v.Op, + Major: v.Major, + Minor: v.Minor, + Value: v.Value, + } + } + + return ret +} + +func setNetworkStats(vcNetwork []*vc.NetworkStats) []*cgroups.NetworkStat { + networkStats := make([]*cgroups.NetworkStat, len(vcNetwork)) + for i, v := range vcNetwork { + networkStats[i] = &cgroups.NetworkStat{ + Name: v.Name, + RxBytes: v.RxBytes, + RxPackets: v.RxPackets, + RxErrors: v.RxErrors, + RxDropped: v.RxDropped, + TxBytes: v.TxBytes, + TxPackets: v.TxPackets, + TxErrors: v.TxErrors, + TxDropped: v.TxDropped, + } + } + + return networkStats +} diff --git a/src/runtime/containerd-shim-v2/metrics_test.go b/src/runtime/containerd-shim-v2/metrics_test.go new file mode 100644 index 000000000..45f6709b1 --- /dev/null +++ b/src/runtime/containerd-shim-v2/metrics_test.go @@ -0,0 +1,56 @@ +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "testing" + + "github.com/containerd/cgroups" + "github.com/containerd/containerd/namespaces" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/stretchr/testify/assert" +) + +func TestStatNetworkMetric(t *testing.T) { + + assert := assert.New(t) + var err error + + mockNetwork := []*vc.NetworkStats{ + { + Name: "test-network", + RxBytes: 10, + TxBytes: 20, + }, + } + + expectedNetwork := []*cgroups.NetworkStat{ + { + Name: "test-network", + RxBytes: 10, + TxBytes: 20, + }, + } + + testingImpl.StatsContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStats, error) { + return vc.ContainerStats{ + NetworkStats: mockNetwork, + }, nil + } + + defer func() { + testingImpl.StatsContainerFunc = nil + }() + + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + resp, err := testingImpl.StatsContainer(ctx, testSandboxID, testContainerID) + assert.NoError(err) + + metrics := statsToMetrics(&resp) + assert.Equal(expectedNetwork, metrics.Network) +} diff --git a/src/runtime/containerd-shim-v2/pause_test.go b/src/runtime/containerd-shim-v2/pause_test.go new file mode 100644 index 000000000..081c70f7c --- /dev/null +++ b/src/runtime/containerd-shim-v2/pause_test.go @@ -0,0 +1,203 @@ +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "testing" + + "github.com/containerd/containerd/namespaces" + taskAPI "github.com/containerd/containerd/runtime/v2/task" + + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/kata-containers/runtime/virtcontainers/types" + + "github.com/stretchr/testify/assert" +) + +func TestPauseContainerSuccess(t *testing.T) { + assert := assert.New(t) + var err error + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + testingImpl.PauseContainerFunc = func(ctx context.Context, sandboxID, containerID string) error { + return nil + } + defer func() { + testingImpl.PauseContainerFunc = nil + }() + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: testContainerID, + Annotations: make(map[string]string), + State: types.ContainerState{ + State: types.StateRunning, + }, + }, nil + } + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + s := &service{ + id: testSandboxID, + sandbox: sandbox, + containers: make(map[string]*container), + } + + reqCreate := &taskAPI.CreateTaskRequest{ + ID: testContainerID, + } + s.containers[testContainerID], err = newContainer(s, reqCreate, "", nil, true) + assert.NoError(err) + + reqPause := &taskAPI.PauseRequest{ + ID: testContainerID, + } + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + + _, err = s.Pause(ctx, reqPause) + assert.NoError(err) +} + +func TestPauseContainerFail(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + testingImpl.PauseContainerFunc = func(ctx context.Context, sandboxID, containerID string) error { + return nil + } + defer func() { + testingImpl.PauseContainerFunc = nil + }() + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: testContainerID, + Annotations: make(map[string]string), + State: types.ContainerState{ + State: types.StateRunning, + }, + }, nil + } + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + s := &service{ + id: testSandboxID, + sandbox: sandbox, + containers: make(map[string]*container), + } + + reqPause := &taskAPI.PauseRequest{ + ID: testContainerID, + } + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + + _, err := s.Pause(ctx, reqPause) + assert.Error(err) +} + +func TestResumeContainerSuccess(t *testing.T) { + assert := assert.New(t) + var err error + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + testingImpl.ResumeContainerFunc = func(ctx context.Context, sandboxID, containerID string) error { + return nil + } + defer func() { + testingImpl.ResumeContainerFunc = nil + }() + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: testContainerID, + Annotations: make(map[string]string), + State: types.ContainerState{ + State: types.StateRunning, + }, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + s := &service{ + id: testSandboxID, + sandbox: sandbox, + containers: make(map[string]*container), + } + + reqCreate := &taskAPI.CreateTaskRequest{ + ID: testContainerID, + } + s.containers[testContainerID], err = newContainer(s, reqCreate, "", nil, true) + assert.NoError(err) + + reqResume := &taskAPI.ResumeRequest{ + ID: testContainerID, + } + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + + _, err = s.Resume(ctx, reqResume) + assert.NoError(err) +} + +func TestResumeContainerFail(t *testing.T) { + assert := assert.New(t) + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + testingImpl.ResumeContainerFunc = func(ctx context.Context, sandboxID, containerID string) error { + return nil + } + defer func() { + testingImpl.ResumeContainerFunc = nil + }() + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: testContainerID, + Annotations: make(map[string]string), + State: types.ContainerState{ + State: types.StateRunning, + }, + }, nil + } + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + s := &service{ + id: testSandboxID, + sandbox: sandbox, + containers: make(map[string]*container), + } + + reqResume := &taskAPI.ResumeRequest{ + ID: testContainerID, + } + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + + _, err := s.Resume(ctx, reqResume) + assert.Error(err) +} diff --git a/src/runtime/containerd-shim-v2/service.go b/src/runtime/containerd-shim-v2/service.go new file mode 100644 index 000000000..8e9b9499e --- /dev/null +++ b/src/runtime/containerd-shim-v2/service.go @@ -0,0 +1,936 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "io/ioutil" + "os" + sysexec "os/exec" + "sync" + "syscall" + "time" + + eventstypes "github.com/containerd/containerd/api/events" + "github.com/containerd/containerd/api/types/task" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/events" + "github.com/containerd/containerd/namespaces" + cdruntime "github.com/containerd/containerd/runtime" + cdshim "github.com/containerd/containerd/runtime/v2/shim" + taskAPI "github.com/containerd/containerd/runtime/v2/task" + "github.com/containerd/typeurl" + ptypes "github.com/gogo/protobuf/types" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" + + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/kata-containers/runtime/virtcontainers/types" +) + +const ( + // Define the service's channel size, which is used for + // reaping the exited processes exit state and forwarding + // it to containerd as the containerd event format. + bufferSize = 32 + + chSize = 128 + exitCode255 = 255 + + // A time span used to wait for publish a containerd event, + // once it costs a longer time than timeOut, it will be canceld. + timeOut = 5 * time.Second +) + +var ( + empty = &ptypes.Empty{} + _ taskAPI.TaskService = (taskAPI.TaskService)(&service{}) +) + +// concrete virtcontainer implementation +var vci vc.VC = &vc.VCImpl{} + +// New returns a new shim service that can be used via GRPC +func New(ctx context.Context, id string, publisher events.Publisher) (cdshim.Shim, error) { + logger := logrus.WithField("ID", id) + // Discard the log before shim init its log output. Otherwise + // it will output into stdio, from which containerd would like + // to get the shim's socket address. + logrus.SetOutput(ioutil.Discard) + opts := ctx.Value(cdshim.OptsKey{}).(cdshim.Opts) + if !opts.Debug { + logrus.SetLevel(logrus.WarnLevel) + } + vci.SetLogger(ctx, logger) + katautils.SetLogger(ctx, logger, logger.Logger.Level) + + ctx, cancel := context.WithCancel(ctx) + + s := &service{ + id: id, + pid: uint32(os.Getpid()), + ctx: ctx, + containers: make(map[string]*container), + events: make(chan interface{}, chSize), + ec: make(chan exit, bufferSize), + cancel: cancel, + } + + go s.processExits() + + go s.forward(publisher) + + return s, nil +} + +type exit struct { + id string + execid string + pid uint32 + status int + timestamp time.Time +} + +// service is the shim implementation of a remote shim over GRPC +type service struct { + mu sync.Mutex + eventSendMu sync.Mutex + + // pid Since this shimv2 cannot get the container processes pid from VM, + // thus for the returned values needed pid, just return this shim's + // pid directly. + pid uint32 + + ctx context.Context + sandbox vc.VCSandbox + containers map[string]*container + config *oci.RuntimeConfig + events chan interface{} + monitor chan error + + cancel func() + + ec chan exit + id string +} + +func newCommand(ctx context.Context, containerdBinary, id, containerdAddress string) (*sysexec.Cmd, error) { + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return nil, err + } + self, err := os.Executable() + if err != nil { + return nil, err + } + cwd, err := os.Getwd() + if err != nil { + return nil, err + } + args := []string{ + "-namespace", ns, + "-address", containerdAddress, + "-publish-binary", containerdBinary, + "-id", id, + } + opts := ctx.Value(cdshim.OptsKey{}).(cdshim.Opts) + if opts.Debug { + args = append(args, "-debug") + } + cmd := sysexec.Command(self, args...) + cmd.Dir = cwd + + // Set the go max process to 2 in case the shim forks too much process + cmd.Env = append(os.Environ(), "GOMAXPROCS=2") + + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } + + return cmd, nil +} + +// StartShim willl start a kata shimv2 daemon which will implemented the +// ShimV2 APIs such as create/start/update etc containers. +func (s *service) StartShim(ctx context.Context, id, containerdBinary, containerdAddress string) (string, error) { + bundlePath, err := os.Getwd() + if err != nil { + return "", err + } + + address, err := getAddress(ctx, bundlePath, id) + if err != nil { + return "", err + } + if address != "" { + if err := cdshim.WriteAddress("address", address); err != nil { + return "", err + } + return address, nil + } + + cmd, err := newCommand(ctx, containerdBinary, id, containerdAddress) + if err != nil { + return "", err + } + + address, err = cdshim.SocketAddress(ctx, id) + if err != nil { + return "", err + } + + socket, err := cdshim.NewSocket(address) + if err != nil { + return "", err + } + defer socket.Close() + f, err := socket.File() + if err != nil { + return "", err + } + defer f.Close() + + cmd.ExtraFiles = append(cmd.ExtraFiles, f) + + if err := cmd.Start(); err != nil { + return "", err + } + defer func() { + if err != nil { + cmd.Process.Kill() + } + }() + + // make sure to wait after start + go cmd.Wait() + if err := cdshim.WritePidFile("shim.pid", cmd.Process.Pid); err != nil { + return "", err + } + if err := cdshim.WriteAddress("address", address); err != nil { + return "", err + } + return address, nil +} + +func (s *service) forward(publisher events.Publisher) { + for e := range s.events { + ctx, cancel := context.WithTimeout(s.ctx, timeOut) + err := publisher.Publish(ctx, getTopic(e), e) + cancel() + if err != nil { + logrus.WithError(err).Error("post event") + } + } +} + +func (s *service) send(evt interface{}) { + // for unit test, it will not initialize s.events + if s.events != nil { + s.events <- evt + } +} + +func (s *service) sendL(evt interface{}) { + s.eventSendMu.Lock() + if s.events != nil { + s.events <- evt + } + s.eventSendMu.Unlock() +} + +func getTopic(e interface{}) string { + switch e.(type) { + case *eventstypes.TaskCreate: + return cdruntime.TaskCreateEventTopic + case *eventstypes.TaskStart: + return cdruntime.TaskStartEventTopic + case *eventstypes.TaskOOM: + return cdruntime.TaskOOMEventTopic + case *eventstypes.TaskExit: + return cdruntime.TaskExitEventTopic + case *eventstypes.TaskDelete: + return cdruntime.TaskDeleteEventTopic + case *eventstypes.TaskExecAdded: + return cdruntime.TaskExecAddedEventTopic + case *eventstypes.TaskExecStarted: + return cdruntime.TaskExecStartedEventTopic + case *eventstypes.TaskPaused: + return cdruntime.TaskPausedEventTopic + case *eventstypes.TaskResumed: + return cdruntime.TaskResumedEventTopic + case *eventstypes.TaskCheckpointed: + return cdruntime.TaskCheckpointedEventTopic + default: + logrus.Warnf("no topic for type %#v", e) + } + return cdruntime.TaskUnknownTopic +} + +func (s *service) Cleanup(ctx context.Context) (_ *taskAPI.DeleteResponse, err error) { + //Since the binary cleanup will return the DeleteResponse from stdout to + //containerd, thus we must make sure there is no any outputs in stdout except + //the returned response, thus here redirect the log to stderr in case there's + //any log output to stdout. + logrus.SetOutput(os.Stderr) + + defer func() { + err = toGRPC(err) + }() + + if s.id == "" { + return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "the container id is empty, please specify the container id") + } + + path, err := os.Getwd() + if err != nil { + return nil, err + } + + ociSpec, err := compatoci.ParseConfigJSON(path) + if err != nil { + return nil, err + } + + containerType, err := oci.ContainerType(ociSpec) + if err != nil { + return nil, err + } + + switch containerType { + case vc.PodSandbox: + err = cleanupContainer(ctx, s.id, s.id, path) + if err != nil { + return nil, err + } + case vc.PodContainer: + sandboxID, err := oci.SandboxID(ociSpec) + if err != nil { + return nil, err + } + + err = cleanupContainer(ctx, sandboxID, s.id, path) + if err != nil { + return nil, err + } + } + + return &taskAPI.DeleteResponse{ + ExitedAt: time.Now(), + ExitStatus: 128 + uint32(unix.SIGKILL), + }, nil +} + +// Create a new sandbox or container with the underlying OCI runtime +func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (_ *taskAPI.CreateTaskResponse, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + var c *container + + c, err = create(ctx, s, r) + if err != nil { + return nil, err + } + + c.status = task.StatusCreated + + s.containers[r.ID] = c + + s.send(&eventstypes.TaskCreate{ + ContainerID: r.ID, + Bundle: r.Bundle, + Rootfs: r.Rootfs, + IO: &eventstypes.TaskIO{ + Stdin: r.Stdin, + Stdout: r.Stdout, + Stderr: r.Stderr, + Terminal: r.Terminal, + }, + Checkpoint: r.Checkpoint, + Pid: s.pid, + }) + + return &taskAPI.CreateTaskResponse{ + Pid: s.pid, + }, nil +} + +// Start a process +func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (_ *taskAPI.StartResponse, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + c, err := s.getContainer(r.ID) + if err != nil { + return nil, err + } + + // hold the send lock so that the start events are sent before any exit events in the error case + s.eventSendMu.Lock() + defer s.eventSendMu.Unlock() + + //start a container + if r.ExecID == "" { + err = startContainer(ctx, s, c) + if err != nil { + return nil, errdefs.ToGRPC(err) + } + s.send(&eventstypes.TaskStart{ + ContainerID: c.id, + Pid: s.pid, + }) + } else { + //start an exec + _, err = startExec(ctx, s, r.ID, r.ExecID) + if err != nil { + return nil, errdefs.ToGRPC(err) + } + s.send(&eventstypes.TaskExecStarted{ + ContainerID: c.id, + ExecID: r.ExecID, + Pid: s.pid, + }) + } + + return &taskAPI.StartResponse{ + Pid: s.pid, + }, nil +} + +// Delete the initial process and container +func (s *service) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (_ *taskAPI.DeleteResponse, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + c, err := s.getContainer(r.ID) + if err != nil { + return nil, err + } + + if r.ExecID == "" { + if err = deleteContainer(ctx, s, c); err != nil { + return nil, err + } + + s.send(&eventstypes.TaskDelete{ + ContainerID: c.id, + Pid: s.pid, + ExitStatus: c.exit, + ExitedAt: c.exitTime, + }) + + return &taskAPI.DeleteResponse{ + ExitStatus: c.exit, + ExitedAt: c.exitTime, + Pid: s.pid, + }, nil + } + //deal with the exec case + execs, err := c.getExec(r.ExecID) + if err != nil { + return nil, err + } + + delete(c.execs, r.ExecID) + + return &taskAPI.DeleteResponse{ + ExitStatus: uint32(execs.exitCode), + ExitedAt: execs.exitTime, + Pid: s.pid, + }, nil +} + +// Exec an additional process inside the container +func (s *service) Exec(ctx context.Context, r *taskAPI.ExecProcessRequest) (_ *ptypes.Empty, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + c, err := s.getContainer(r.ID) + if err != nil { + return nil, err + } + + if execs := c.execs[r.ExecID]; execs != nil { + return nil, errdefs.ToGRPCf(errdefs.ErrAlreadyExists, "id %s", r.ExecID) + } + + execs, err := newExec(c, r.Stdin, r.Stdout, r.Stderr, r.Terminal, r.Spec) + if err != nil { + return nil, errdefs.ToGRPC(err) + } + + c.execs[r.ExecID] = execs + + s.send(&eventstypes.TaskExecAdded{ + ContainerID: c.id, + ExecID: r.ExecID, + }) + + return empty, nil +} + +// ResizePty of a process +func (s *service) ResizePty(ctx context.Context, r *taskAPI.ResizePtyRequest) (_ *ptypes.Empty, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + c, err := s.getContainer(r.ID) + if err != nil { + return nil, err + } + + processID := c.id + if r.ExecID != "" { + execs, err := c.getExec(r.ExecID) + if err != nil { + return nil, err + } + execs.tty.height = r.Height + execs.tty.width = r.Width + + processID = execs.id + + } + err = s.sandbox.WinsizeProcess(c.id, processID, r.Height, r.Width) + if err != nil { + return nil, err + } + + return empty, err +} + +// State returns runtime state information for a process +func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (_ *taskAPI.StateResponse, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + c, err := s.getContainer(r.ID) + if err != nil { + return nil, err + } + + if r.ExecID == "" { + return &taskAPI.StateResponse{ + ID: c.id, + Bundle: c.bundle, + Pid: s.pid, + Status: c.status, + Stdin: c.stdin, + Stdout: c.stdout, + Stderr: c.stderr, + Terminal: c.terminal, + ExitStatus: c.exit, + }, nil + } + + //deal with exec case + execs, err := c.getExec(r.ExecID) + if err != nil { + return nil, err + } + + return &taskAPI.StateResponse{ + ID: execs.id, + Bundle: c.bundle, + Pid: s.pid, + Status: execs.status, + Stdin: execs.tty.stdin, + Stdout: execs.tty.stdout, + Stderr: execs.tty.stderr, + Terminal: execs.tty.terminal, + ExitStatus: uint32(execs.exitCode), + }, nil + +} + +// Pause the container +func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (_ *ptypes.Empty, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + c, err := s.getContainer(r.ID) + if err != nil { + return nil, err + } + + c.status = task.StatusPausing + + err = s.sandbox.PauseContainer(r.ID) + if err == nil { + c.status = task.StatusPaused + s.send(&eventstypes.TaskPaused{ + ContainerID: c.id, + }) + return empty, nil + } + + if status, err := s.getContainerStatus(c.id); err != nil { + c.status = task.StatusUnknown + } else { + c.status = status + } + + return empty, err +} + +// Resume the container +func (s *service) Resume(ctx context.Context, r *taskAPI.ResumeRequest) (_ *ptypes.Empty, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + c, err := s.getContainer(r.ID) + if err != nil { + return nil, err + } + + err = s.sandbox.ResumeContainer(c.id) + if err == nil { + c.status = task.StatusRunning + s.send(&eventstypes.TaskResumed{ + ContainerID: c.id, + }) + return empty, nil + } + + if status, err := s.getContainerStatus(c.id); err != nil { + c.status = task.StatusUnknown + } else { + c.status = status + } + + return empty, err +} + +// Kill a process with the provided signal +func (s *service) Kill(ctx context.Context, r *taskAPI.KillRequest) (_ *ptypes.Empty, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + signum := syscall.Signal(r.Signal) + + c, err := s.getContainer(r.ID) + if err != nil { + return nil, err + } + + // According to CRI specs, kubelet will call StopPodSandbox() + // at least once before calling RemovePodSandbox, and this call + // is idempotent, and must not return an error if all relevant + // resources have already been reclaimed. And in that call it will + // send a SIGKILL signal first to try to stop the container, thus + // once the container has terminated, here should ignore this signal + // and return directly. + if signum == syscall.SIGKILL || signum == syscall.SIGTERM { + if c.status == task.StatusStopped { + logrus.WithField("sandbox", s.sandbox.ID()).WithField("Container", c.id).Debug("Container has already been stopped") + return empty, nil + } + } + + processID := c.id + if r.ExecID != "" { + execs, err := c.getExec(r.ExecID) + if err != nil { + return nil, err + } + processID = execs.id + if processID == "" { + logrus.WithFields(logrus.Fields{ + "sandbox": s.sandbox.ID(), + "Container": c.id, + "ExecID": r.ExecID, + }).Debug("Id of exec process to be signalled is empty") + return empty, errors.New("The exec process does not exist") + } + } + + return empty, s.sandbox.SignalProcess(c.id, processID, signum, r.All) +} + +// Pids returns all pids inside the container +// Since for kata, it cannot get the process's pid from VM, +// thus only return the Shim's pid directly. +func (s *service) Pids(ctx context.Context, r *taskAPI.PidsRequest) (_ *taskAPI.PidsResponse, err error) { + var processes []*task.ProcessInfo + + defer func() { + err = toGRPC(err) + }() + + pInfo := task.ProcessInfo{ + Pid: s.pid, + } + processes = append(processes, &pInfo) + + return &taskAPI.PidsResponse{ + Processes: processes, + }, nil +} + +// CloseIO of a process +func (s *service) CloseIO(ctx context.Context, r *taskAPI.CloseIORequest) (_ *ptypes.Empty, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + c, err := s.getContainer(r.ID) + if err != nil { + return nil, err + } + + tty := c.ttyio + if r.ExecID != "" { + execs, err := c.getExec(r.ExecID) + if err != nil { + return nil, err + } + tty = execs.ttyio + } + + if tty != nil && tty.Stdin != nil { + if err := tty.Stdin.Close(); err != nil { + return nil, errors.Wrap(err, "close stdin") + } + } + + return empty, nil +} + +// Checkpoint the container +func (s *service) Checkpoint(ctx context.Context, r *taskAPI.CheckpointTaskRequest) (_ *ptypes.Empty, err error) { + defer func() { + err = toGRPC(err) + }() + + return nil, errdefs.ToGRPCf(errdefs.ErrNotImplemented, "service Checkpoint") +} + +// Connect returns shim information such as the shim's pid +func (s *service) Connect(ctx context.Context, r *taskAPI.ConnectRequest) (_ *taskAPI.ConnectResponse, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + return &taskAPI.ConnectResponse{ + ShimPid: s.pid, + //Since kata cannot get the container's pid in VM, thus only return the shim's pid. + TaskPid: s.pid, + }, nil +} + +func (s *service) Shutdown(ctx context.Context, r *taskAPI.ShutdownRequest) (_ *ptypes.Empty, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + if len(s.containers) != 0 { + s.mu.Unlock() + return empty, nil + } + s.mu.Unlock() + + s.cancel() + + os.Exit(0) + + // This will never be called, but this is only there to make sure the + // program can compile. + return empty, nil +} + +func (s *service) Stats(ctx context.Context, r *taskAPI.StatsRequest) (_ *taskAPI.StatsResponse, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + c, err := s.getContainer(r.ID) + if err != nil { + return nil, err + } + + data, err := marshalMetrics(s, c.id) + if err != nil { + return nil, err + } + + return &taskAPI.StatsResponse{ + Stats: data, + }, nil +} + +// Update a running container +func (s *service) Update(ctx context.Context, r *taskAPI.UpdateTaskRequest) (_ *ptypes.Empty, err error) { + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + defer s.mu.Unlock() + + var resources *specs.LinuxResources + v, err := typeurl.UnmarshalAny(r.Resources) + if err != nil { + return nil, err + } + resources, ok := v.(*specs.LinuxResources) + if !ok { + return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "Invalid resources type for %s", s.id) + } + + err = s.sandbox.UpdateContainer(r.ID, *resources) + if err != nil { + return nil, errdefs.ToGRPC(err) + } + + return empty, nil +} + +// Wait for a process to exit +func (s *service) Wait(ctx context.Context, r *taskAPI.WaitRequest) (_ *taskAPI.WaitResponse, err error) { + var ret uint32 + + defer func() { + err = toGRPC(err) + }() + + s.mu.Lock() + c, err := s.getContainer(r.ID) + s.mu.Unlock() + + if err != nil { + return nil, err + } + + //wait for container + if r.ExecID == "" { + ret = <-c.exitCh + + // refill the exitCh with the container process's exit code in case + // there were other waits on this process. + c.exitCh <- ret + } else { //wait for exec + execs, err := c.getExec(r.ExecID) + if err != nil { + return nil, err + } + ret = <-execs.exitCh + + // refill the exitCh with the exec process's exit code in case + // there were other waits on this process. + execs.exitCh <- ret + } + + return &taskAPI.WaitResponse{ + ExitStatus: ret, + ExitedAt: c.exitTime, + }, nil +} + +func (s *service) processExits() { + for e := range s.ec { + s.checkProcesses(e) + } +} + +func (s *service) checkProcesses(e exit) { + s.mu.Lock() + defer s.mu.Unlock() + + id := e.execid + if id == "" { + id = e.id + } + + s.sendL(&eventstypes.TaskExit{ + ContainerID: e.id, + ID: id, + Pid: e.pid, + ExitStatus: uint32(e.status), + ExitedAt: e.timestamp, + }) +} + +func (s *service) getContainer(id string) (*container, error) { + c := s.containers[id] + + if c == nil { + return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "container does not exist %s", id) + } + + return c, nil +} + +func (s *service) getContainerStatus(containerID string) (task.Status, error) { + cStatus, err := s.sandbox.StatusContainer(containerID) + if err != nil { + return task.StatusUnknown, err + } + + var status task.Status + switch cStatus.State.State { + case types.StateReady: + status = task.StatusCreated + case types.StateRunning: + status = task.StatusRunning + case types.StatePaused: + status = task.StatusPaused + case types.StateStopped: + status = task.StatusStopped + } + + return status, nil +} diff --git a/src/runtime/containerd-shim-v2/start.go b/src/runtime/containerd-shim-v2/start.go new file mode 100644 index 000000000..173ca7c76 --- /dev/null +++ b/src/runtime/containerd-shim-v2/start.go @@ -0,0 +1,121 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "fmt" + + "github.com/containerd/containerd/api/types/task" + "github.com/kata-containers/runtime/pkg/katautils" +) + +func startContainer(ctx context.Context, s *service, c *container) error { + //start a container + if c.cType == "" { + err := fmt.Errorf("Bug, the container %s type is empty", c.id) + return err + } + + if s.sandbox == nil { + err := fmt.Errorf("Bug, the sandbox hasn't been created for this container %s", c.id) + return err + } + + if c.cType.IsSandbox() { + err := s.sandbox.Start() + if err != nil { + return err + } + // Start monitor after starting sandbox + s.monitor, err = s.sandbox.Monitor() + if err != nil { + return err + } + go watchSandbox(s) + } else { + _, err := s.sandbox.StartContainer(c.id) + if err != nil { + return err + } + } + + // Run post-start OCI hooks. + err := katautils.EnterNetNS(s.sandbox.GetNetNs(), func() error { + return katautils.PostStartHooks(ctx, *c.spec, s.sandbox.ID(), c.bundle) + }) + if err != nil { + return err + } + + c.status = task.StatusRunning + + stdin, stdout, stderr, err := s.sandbox.IOStream(c.id, c.id) + if err != nil { + return err + } + + if c.stdin != "" || c.stdout != "" || c.stderr != "" { + tty, err := newTtyIO(ctx, c.stdin, c.stdout, c.stderr, c.terminal) + if err != nil { + return err + } + c.ttyio = tty + go ioCopy(c.exitIOch, tty, stdin, stdout, stderr) + } else { + //close the io exit channel, since there is no io for this container, + //otherwise the following wait goroutine will hang on this channel. + close(c.exitIOch) + } + + go wait(s, c, "") + + return nil +} + +func startExec(ctx context.Context, s *service, containerID, execID string) (*exec, error) { + //start an exec + c, err := s.getContainer(containerID) + if err != nil { + return nil, err + } + + execs, err := c.getExec(execID) + if err != nil { + return nil, err + } + + _, proc, err := s.sandbox.EnterContainer(containerID, *execs.cmds) + if err != nil { + err := fmt.Errorf("cannot enter container %s, with err %s", containerID, err) + return nil, err + } + execs.id = proc.Token + + execs.status = task.StatusRunning + if execs.tty.height != 0 && execs.tty.width != 0 { + err = s.sandbox.WinsizeProcess(c.id, execs.id, execs.tty.height, execs.tty.width) + if err != nil { + return nil, err + } + } + + stdin, stdout, stderr, err := s.sandbox.IOStream(c.id, execs.id) + if err != nil { + return nil, err + } + tty, err := newTtyIO(ctx, execs.tty.stdin, execs.tty.stdout, execs.tty.stderr, execs.tty.terminal) + if err != nil { + return nil, err + } + execs.ttyio = tty + + go ioCopy(execs.exitIOch, tty, stdin, stdout, stderr) + + go wait(s, c, execID) + + return execs, nil +} diff --git a/src/runtime/containerd-shim-v2/start_test.go b/src/runtime/containerd-shim-v2/start_test.go new file mode 100644 index 000000000..bbc89b9de --- /dev/null +++ b/src/runtime/containerd-shim-v2/start_test.go @@ -0,0 +1,177 @@ +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "testing" + + "github.com/containerd/containerd/namespaces" + taskAPI "github.com/containerd/containerd/runtime/v2/task" + + vc "github.com/kata-containers/runtime/virtcontainers" + vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + + "github.com/stretchr/testify/assert" +) + +func TestStartStartSandboxSuccess(t *testing.T) { + assert := assert.New(t) + var err error + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodSandbox), + }, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + s := &service{ + id: testSandboxID, + sandbox: sandbox, + containers: make(map[string]*container), + } + + reqCreate := &taskAPI.CreateTaskRequest{ + ID: testSandboxID, + } + s.containers[testSandboxID], err = newContainer(s, reqCreate, vc.PodSandbox, nil, false) + assert.NoError(err) + + reqStart := &taskAPI.StartRequest{ + ID: testSandboxID, + } + + testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.StartSandboxFunc = nil + }() + + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + _, err = s.Start(ctx, reqStart) + assert.NoError(err) +} + +func TestStartMissingAnnotation(t *testing.T) { + assert := assert.New(t) + var err error + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: sandbox.ID(), + Annotations: map[string]string{}, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + s := &service{ + id: testSandboxID, + sandbox: sandbox, + containers: make(map[string]*container), + } + + reqCreate := &taskAPI.CreateTaskRequest{ + ID: testSandboxID, + } + s.containers[testSandboxID], err = newContainer(s, reqCreate, "", nil, false) + assert.NoError(err) + + reqStart := &taskAPI.StartRequest{ + ID: testSandboxID, + } + + testingImpl.StartSandboxFunc = func(ctx context.Context, sandboxID string) (vc.VCSandbox, error) { + return sandbox, nil + } + + defer func() { + testingImpl.StartSandboxFunc = nil + }() + + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + _, err = s.Start(ctx, reqStart) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) +} + +func TestStartStartContainerSucess(t *testing.T) { + assert := assert.New(t) + var err error + + sandbox := &vcmock.Sandbox{ + MockID: testSandboxID, + } + + sandbox.MockContainers = []*vcmock.Container{ + { + MockID: testContainerID, + MockSandbox: sandbox, + }, + } + + testingImpl.StatusContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.ContainerStatus, error) { + return vc.ContainerStatus{ + ID: testContainerID, + Annotations: map[string]string{ + vcAnnotations.ContainerTypeKey: string(vc.PodContainer), + }, + }, nil + } + + defer func() { + testingImpl.StatusContainerFunc = nil + }() + + testingImpl.StartContainerFunc = func(ctx context.Context, sandboxID, containerID string) (vc.VCContainer, error) { + return sandbox.MockContainers[0], nil + } + + defer func() { + testingImpl.StartContainerFunc = nil + }() + + s := &service{ + id: testSandboxID, + sandbox: sandbox, + containers: make(map[string]*container), + } + + reqCreate := &taskAPI.CreateTaskRequest{ + ID: testContainerID, + } + s.containers[testContainerID], err = newContainer(s, reqCreate, vc.PodContainer, nil, false) + assert.NoError(err) + + reqStart := &taskAPI.StartRequest{ + ID: testContainerID, + } + + ctx := namespaces.WithNamespace(context.Background(), "UnitTest") + _, err = s.Start(ctx, reqStart) + assert.NoError(err) +} diff --git a/src/runtime/containerd-shim-v2/stream.go b/src/runtime/containerd-shim-v2/stream.go new file mode 100644 index 000000000..a16cbba0c --- /dev/null +++ b/src/runtime/containerd-shim-v2/stream.go @@ -0,0 +1,127 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "io" + "sync" + "syscall" + + "github.com/containerd/fifo" +) + +// The buffer size used to specify the buffer for IO streams copy +const bufSize = 32 << 10 + +var ( + bufPool = sync.Pool{ + New: func() interface{} { + buffer := make([]byte, bufSize) + return &buffer + }, + } +) + +type ttyIO struct { + Stdin io.ReadCloser + Stdout io.Writer + Stderr io.Writer +} + +func (tty *ttyIO) close() { + + if tty.Stdin != nil { + tty.Stdin.Close() + } + cf := func(w io.Writer) { + if w == nil { + return + } + if c, ok := w.(io.WriteCloser); ok { + c.Close() + } + } + cf(tty.Stdout) + cf(tty.Stderr) +} + +func newTtyIO(ctx context.Context, stdin, stdout, stderr string, console bool) (*ttyIO, error) { + var in io.ReadCloser + var outw io.Writer + var errw io.Writer + var err error + + if stdin != "" { + in, err = fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0) + if err != nil { + return nil, err + } + } + + if stdout != "" { + outw, err = fifo.OpenFifo(ctx, stdout, syscall.O_RDWR, 0) + if err != nil { + return nil, err + } + } + + if !console && stderr != "" { + errw, err = fifo.OpenFifo(ctx, stderr, syscall.O_RDWR, 0) + if err != nil { + return nil, err + } + } + + ttyIO := &ttyIO{ + Stdin: in, + Stdout: outw, + Stderr: errw, + } + + return ttyIO, nil +} + +func ioCopy(exitch chan struct{}, tty *ttyIO, stdinPipe io.WriteCloser, stdoutPipe, stderrPipe io.Reader) { + var wg sync.WaitGroup + var closeOnce sync.Once + + if tty.Stdin != nil { + wg.Add(1) + go func() { + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + io.CopyBuffer(stdinPipe, tty.Stdin, *p) + wg.Done() + }() + } + + if tty.Stdout != nil { + wg.Add(1) + + go func() { + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + io.CopyBuffer(tty.Stdout, stdoutPipe, *p) + wg.Done() + closeOnce.Do(tty.close) + }() + } + + if tty.Stderr != nil && stderrPipe != nil { + wg.Add(1) + go func() { + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + io.CopyBuffer(tty.Stderr, stderrPipe, *p) + wg.Done() + }() + } + + wg.Wait() + closeOnce.Do(tty.close) + close(exitch) +} diff --git a/src/runtime/containerd-shim-v2/stream_test.go b/src/runtime/containerd-shim-v2/stream_test.go new file mode 100644 index 000000000..637a20b03 --- /dev/null +++ b/src/runtime/containerd-shim-v2/stream_test.go @@ -0,0 +1,91 @@ +// Copyright (c) 2020 Baidu Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "io" + "io/ioutil" + "path/filepath" + "syscall" + "testing" + "time" + + "github.com/containerd/fifo" + "github.com/stretchr/testify/assert" +) + +func TestNewTtyIOFifoReopen(t *testing.T) { + var outr io.ReadWriteCloser + var errr io.ReadWriteCloser + var tty *ttyIO + assert := assert.New(t) + ctx := context.TODO() + fifoPath, err := ioutil.TempDir(testDir, "fifo-path-") + assert.NoError(err) + stdout := filepath.Join(fifoPath, "stdout") + stderr := filepath.Join(fifoPath, "stderr") + + createReadFifo := func(f string) io.ReadWriteCloser { + rf, err := fifo.OpenFifo(ctx, f, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700) + if err != nil { + t.Fatal(err) + } + return rf + } + + outr = createReadFifo(stdout) + defer outr.Close() + errr = createReadFifo(stderr) + defer errr.Close() + tty, err = newTtyIO(ctx, "", stdout, stderr, false) + assert.NoError(err) + defer tty.close() + + testBytes := []byte("T") + checkFifoWrite := func(w io.Writer) { + _, err = w.Write(testBytes) + assert.NoError(err) + } + checkFifoRead := func(r io.Reader) { + var err error + buf := make([]byte, 1) + done := make(chan struct{}) + timer := time.NewTimer(2 * time.Second) + go func() { + _, err = r.Read(buf) + close(done) + }() + select { + case <-done: + assert.NoError(err) + assert.Equal(buf, testBytes) + case <-timer.C: + t.Fatal("read fifo timeout") + } + } + + checkFifoWrite(tty.Stdout) + checkFifoRead(outr) + checkFifoWrite(tty.Stderr) + checkFifoRead(errr) + + err = outr.Close() + assert.NoError(err) + err = errr.Close() + assert.NoError(err) + + // Make sure that writing to tty fifo will not get `EPIPE` + // when the read side is closed + checkFifoWrite(tty.Stdout) + checkFifoWrite(tty.Stderr) + + // Reopen the fifo + outr = createReadFifo(stdout) + errr = createReadFifo(stderr) + checkFifoRead(outr) + checkFifoRead(errr) +} diff --git a/src/runtime/containerd-shim-v2/utils.go b/src/runtime/containerd-shim-v2/utils.go new file mode 100644 index 000000000..cebac177b --- /dev/null +++ b/src/runtime/containerd-shim-v2/utils.go @@ -0,0 +1,135 @@ +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "context" + "fmt" + "os" + "path/filepath" + "time" + + "github.com/containerd/containerd/mount" + cdshim "github.com/containerd/containerd/runtime/v2/shim" + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/sirupsen/logrus" +) + +func cReap(s *service, status int, id, execid string, exitat time.Time) { + s.ec <- exit{ + timestamp: exitat, + pid: s.pid, + status: status, + id: id, + execid: execid, + } +} + +func cleanupContainer(ctx context.Context, sid, cid, bundlePath string) error { + logrus.WithField("Service", "Cleanup").WithField("container", cid).Info("Cleanup container") + + err := vci.CleanupContainer(ctx, sid, cid, true) + if err != nil { + logrus.WithError(err).WithField("container", cid).Warn("failed to cleanup container") + return err + } + + rootfs := filepath.Join(bundlePath, "rootfs") + + if err := mount.UnmountAll(rootfs, 0); err != nil { + logrus.WithError(err).WithField("container", cid).Warn("failed to cleanup container rootfs") + return err + } + + return nil +} + +func validBundle(containerID, bundlePath string) (string, error) { + // container ID MUST be provided. + if containerID == "" { + return "", fmt.Errorf("Missing container ID") + } + + // bundle path MUST be provided. + if bundlePath == "" { + return "", fmt.Errorf("Missing bundle path") + } + + // bundle path MUST be valid. + fileInfo, err := os.Stat(bundlePath) + if err != nil { + return "", fmt.Errorf("Invalid bundle path '%s': %s", bundlePath, err) + } + if !fileInfo.IsDir() { + return "", fmt.Errorf("Invalid bundle path '%s', it should be a directory", bundlePath) + } + + resolved, err := katautils.ResolvePath(bundlePath) + if err != nil { + return "", err + } + + return resolved, nil +} + +func getAddress(ctx context.Context, bundlePath, id string) (string, error) { + var err error + + // Checks the MUST and MUST NOT from OCI runtime specification + if bundlePath, err = validBundle(id, bundlePath); err != nil { + return "", err + } + + ociSpec, err := compatoci.ParseConfigJSON(bundlePath) + if err != nil { + return "", err + } + + containerType, err := oci.ContainerType(ociSpec) + if err != nil { + return "", err + } + + if containerType == vc.PodContainer { + sandboxID, err := oci.SandboxID(ociSpec) + if err != nil { + return "", err + } + address, err := cdshim.SocketAddress(ctx, sandboxID) + if err != nil { + return "", err + } + return address, nil + } + + return "", nil +} + +func noNeedForOutput(detach bool, tty bool) bool { + if !detach { + return false + } + + if !tty { + return false + } + + return true +} + +func removeNamespace(s *specs.Spec, nsType specs.LinuxNamespaceType) { + for i, n := range s.Linux.Namespaces { + if n.Type == nsType { + s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...) + return + } + } +} diff --git a/src/runtime/containerd-shim-v2/utils_test.go b/src/runtime/containerd-shim-v2/utils_test.go new file mode 100644 index 000000000..fd543affd --- /dev/null +++ b/src/runtime/containerd-shim-v2/utils_test.go @@ -0,0 +1,330 @@ +// Copyright (c) 2017 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os" + sysExec "os/exec" + "path" + "path/filepath" + "strings" + + "github.com/opencontainers/runtime-spec/specs-go" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + "github.com/kata-containers/runtime/pkg/katautils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" +) + +const ( + // specConf is the name of the file holding the containers configuration + specConf = "config.json" + + TestID = "container_test" + + testDirMode = os.FileMode(0750) + testFileMode = os.FileMode(0640) + // testExeFileMode = os.FileMode(0750) + + // small docker image used to create root filesystems from + testDockerImage = "busybox" + + testSandboxID = "777-77-77777777" + testContainerID = "42" + testBundle = "bundle" + testConsole = "/dev/pts/888" + + testContainerTypeAnnotation = "io.kubernetes.cri.container-type" + testSandboxIDAnnotation = "io.kubernetes.cri.sandbox-id" + testContainerTypeSandbox = "sandbox" + testContainerTypeContainer = "container" +) + +var ( + // package variables set by calling TestMain() + testDir = "" + testBundleDir = "" + tc ktu.TestConstraint + ctrEngine = katautils.CtrEngine{} +) + +// testingImpl is a concrete mock RVC implementation used for testing +var testingImpl = &vcmock.VCMock{} + +func init() { + fmt.Printf("INFO: running as actual user %v (effective %v), actual group %v (effective %v)\n", + os.Getuid(), os.Geteuid(), os.Getgid(), os.Getegid()) + + fmt.Printf("INFO: switching to fake virtcontainers implementation for testing\n") + vci = testingImpl + + var err error + + fmt.Printf("INFO: creating test directory\n") + testDir, err = ioutil.TempDir("", fmt.Sprintf("shimV2-")) + if err != nil { + panic(fmt.Sprintf("ERROR: failed to create test directory: %v", err)) + } + fmt.Printf("INFO: test directory is %v\n", testDir) + + var output string + for _, name := range katautils.DockerLikeCtrEngines { + fmt.Printf("INFO: checking for container engine: %s\n", name) + + output, err = ctrEngine.Init(name) + if err == nil { + break + } + } + + if ctrEngine.Name == "" { + panic(fmt.Sprintf("ERROR: Docker-like container engine not accessible to current user: %v (error %v)", + output, err)) + } + + // Do this now to avoid hitting the test timeout value due to + // slow network response. + fmt.Printf("INFO: ensuring required container image (%v) is available\n", testDockerImage) + // Only hit the network if the image doesn't exist locally + _, err = ctrEngine.Inspect(testDockerImage) + if err == nil { + fmt.Printf("INFO: container image %v already exists locally\n", testDockerImage) + } else { + fmt.Printf("INFO: pulling container image %v\n", testDockerImage) + _, err = ctrEngine.Pull(testDockerImage) + if err != nil { + panic(err) + } + } + + testBundleDir = filepath.Join(testDir, testBundle) + err = os.MkdirAll(testBundleDir, testDirMode) + if err != nil { + panic(fmt.Sprintf("ERROR: failed to create bundle directory %v: %v", testBundleDir, err)) + } + + fmt.Printf("INFO: creating OCI bundle in %v for tests to use\n", testBundleDir) + err = realMakeOCIBundle(testBundleDir) + if err != nil { + panic(fmt.Sprintf("ERROR: failed to create OCI bundle: %v", err)) + } + + tc = ktu.NewTestConstraint(false) +} + +// createOCIConfig creates an OCI configuration (spec) file in +// the bundle directory specified (which must exist). +func createOCIConfig(bundleDir string) error { + if bundleDir == "" { + return errors.New("BUG: Need bundle directory") + } + + if !katautils.FileExists(bundleDir) { + return fmt.Errorf("BUG: Bundle directory %s does not exist", bundleDir) + } + + var configCmd string + + // Search for a suitable version of runc to use to generate + // the OCI config file. + for _, cmd := range []string{"docker-runc", "runc"} { + fullPath, err := sysExec.LookPath(cmd) + if err == nil { + configCmd = fullPath + break + } + } + + if configCmd == "" { + return errors.New("Cannot find command to generate OCI config file") + } + + _, err := katautils.RunCommand([]string{configCmd, "spec", "--bundle", bundleDir}) + if err != nil { + return err + } + + specFile := filepath.Join(bundleDir, specConf) + if !katautils.FileExists(specFile) { + return fmt.Errorf("generated OCI config file does not exist: %v", specFile) + } + + return nil +} + +func createEmptyFile(path string) (err error) { + return ioutil.WriteFile(path, []byte(""), testFileMode) +} + +// newTestHypervisorConfig creaets a new virtcontainers +// HypervisorConfig, ensuring that the required resources are also +// created. +// +// Note: no parameter validation in case caller wishes to create an invalid +// object. +func newTestHypervisorConfig(dir string, create bool) (vc.HypervisorConfig, error) { + kernelPath := path.Join(dir, "kernel") + imagePath := path.Join(dir, "image") + hypervisorPath := path.Join(dir, "hypervisor") + + if create { + for _, file := range []string{kernelPath, imagePath, hypervisorPath} { + err := createEmptyFile(file) + if err != nil { + return vc.HypervisorConfig{}, err + } + } + } + + return vc.HypervisorConfig{ + KernelPath: kernelPath, + ImagePath: imagePath, + HypervisorPath: hypervisorPath, + HypervisorMachineType: "pc-lite", + }, nil +} + +// newTestRuntimeConfig creates a new RuntimeConfig +func newTestRuntimeConfig(dir, consolePath string, create bool) (oci.RuntimeConfig, error) { + if dir == "" { + return oci.RuntimeConfig{}, errors.New("BUG: need directory") + } + + hypervisorConfig, err := newTestHypervisorConfig(dir, create) + if err != nil { + return oci.RuntimeConfig{}, err + } + + return oci.RuntimeConfig{ + HypervisorType: vc.QemuHypervisor, + HypervisorConfig: hypervisorConfig, + AgentType: vc.KataContainersAgent, + ProxyType: vc.KataBuiltInProxyType, + ShimType: vc.KataBuiltInShimType, + Console: consolePath, + }, nil +} + +// realMakeOCIBundle will create an OCI bundle (including the "config.json" +// config file) in the directory specified (which must already exist). +// +// XXX: Note that tests should *NOT* call this function - they should +// XXX: instead call makeOCIBundle(). +func realMakeOCIBundle(bundleDir string) error { + if bundleDir == "" { + return errors.New("BUG: Need bundle directory") + } + + if !katautils.FileExists(bundleDir) { + return fmt.Errorf("BUG: Bundle directory %v does not exist", bundleDir) + } + + err := createOCIConfig(bundleDir) + if err != nil { + return err + } + + // Note the unusual parameter (a directory, not the config + // file to parse!) + spec, err := compatoci.ParseConfigJSON(bundleDir) + if err != nil { + return err + } + + // Determine the rootfs directory name the OCI config refers to + ociRootPath := spec.Root.Path + + rootfsDir := filepath.Join(bundleDir, ociRootPath) + + if strings.HasPrefix(ociRootPath, "/") { + return fmt.Errorf("Cannot handle absolute rootfs as bundle must be unique to each test") + } + + err = createRootfs(rootfsDir) + if err != nil { + return err + } + + return nil +} + +// Create an OCI bundle in the specified directory. +// +// Note that the directory will be created, but it's parent is expected to exist. +// +// This function works by copying the already-created test bundle. Ideally, +// the bundle would be recreated for each test, but createRootfs() uses +// docker which on some systems is too slow, resulting in the tests timing +// out. +func makeOCIBundle(bundleDir string) error { + from := testBundleDir + to := bundleDir + + // only the basename of bundleDir needs to exist as bundleDir + // will get created by cp(1). + base := filepath.Dir(bundleDir) + + for _, dir := range []string{from, base} { + if !katautils.FileExists(dir) { + return fmt.Errorf("BUG: directory %v should exist", dir) + } + } + + output, err := katautils.RunCommandFull([]string{"cp", "-a", from, to}, true) + if err != nil { + return fmt.Errorf("failed to copy test OCI bundle from %v to %v: %v (output: %v)", from, to, err, output) + } + + return nil +} + +// createRootfs creates a minimal root filesystem below the specified +// directory. +func createRootfs(dir string) error { + err := os.MkdirAll(dir, testDirMode) + if err != nil { + return err + } + + container, err := ctrEngine.Create(testDockerImage) + if err != nil { + return err + } + + err = ctrEngine.GetRootfs(container, dir) + if err != nil { + return err + } + + // Clean up + _, err = ctrEngine.Rm(container) + if err != nil { + return err + } + + return nil +} + +func writeOCIConfigFile(spec specs.Spec, configPath string) error { + if configPath == "" { + return errors.New("BUG: need config file path") + } + + bytes, err := json.MarshalIndent(spec, "", "\t") + if err != nil { + return err + } + + return ioutil.WriteFile(configPath, bytes, testFileMode) +} diff --git a/src/runtime/containerd-shim-v2/wait.go b/src/runtime/containerd-shim-v2/wait.go new file mode 100644 index 000000000..88b0d198f --- /dev/null +++ b/src/runtime/containerd-shim-v2/wait.go @@ -0,0 +1,128 @@ +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package containerdshim + +import ( + "path" + "time" + + "github.com/containerd/containerd/api/types/task" + "github.com/containerd/containerd/mount" + "github.com/sirupsen/logrus" +) + +func wait(s *service, c *container, execID string) (int32, error) { + var execs *exec + var err error + + processID := c.id + + if execID == "" { + //wait until the io closed, then wait the container + <-c.exitIOch + } else { + execs, err = c.getExec(execID) + if err != nil { + return exitCode255, err + } + <-execs.exitIOch + //This wait could be triggered before exec start which + //will get the exec's id, thus this assignment must after + //the exec exit, to make sure it get the exec's id. + processID = execs.id + } + + ret, err := s.sandbox.WaitProcess(c.id, processID) + if err != nil { + logrus.WithError(err).WithFields(logrus.Fields{ + "container": c.id, + "pid": processID, + }).Error("Wait for process failed") + } + + timeStamp := time.Now() + + s.mu.Lock() + if execID == "" { + // Take care of the use case where it is a sandbox. + // Right after the container representing the sandbox has + // been deleted, let's make sure we stop and delete the + // sandbox. + + if c.cType.IsSandbox() { + // cancel watcher + if s.monitor != nil { + s.monitor <- nil + } + if err = s.sandbox.Stop(true); err != nil { + logrus.WithField("sandbox", s.sandbox.ID()).Error("failed to stop sandbox") + } + + if err = s.sandbox.Delete(); err != nil { + logrus.WithField("sandbox", s.sandbox.ID()).Error("failed to delete sandbox") + } + } else { + if _, err = s.sandbox.StopContainer(c.id, false); err != nil { + logrus.WithError(err).WithField("container", c.id).Warn("stop container failed") + } + } + c.status = task.StatusStopped + c.exit = uint32(ret) + c.exitTime = timeStamp + + c.exitCh <- uint32(ret) + + } else { + execs.status = task.StatusStopped + execs.exitCode = ret + execs.exitTime = timeStamp + + execs.exitCh <- uint32(ret) + } + s.mu.Unlock() + + go cReap(s, int(ret), c.id, execID, timeStamp) + + return ret, nil +} + +func watchSandbox(s *service) { + if s.monitor == nil { + return + } + err := <-s.monitor + if err == nil { + return + } + s.monitor = nil + + s.mu.Lock() + defer s.mu.Unlock() + // sandbox malfunctioning, cleanup as much as we can + logrus.WithError(err).Warn("sandbox stopped unexpectedly") + err = s.sandbox.Stop(true) + if err != nil { + logrus.WithError(err).Warn("stop sandbox failed") + } + err = s.sandbox.Delete() + if err != nil { + logrus.WithError(err).Warn("delete sandbox failed") + } + + for _, c := range s.containers { + if !c.mounted { + continue + } + rootfs := path.Join(c.bundle, "rootfs") + logrus.WithField("rootfs", rootfs).WithField("id", c.id).Debug("container umount rootfs") + if err := mount.UnmountAll(rootfs, 0); err != nil { + logrus.WithError(err).Warn("failed to cleanup rootfs mount") + } + } + + // Existing container/exec will be cleaned up by its waiters. + // No need to send async events here. +} diff --git a/src/runtime/data/completions/bash/kata-runtime b/src/runtime/data/completions/bash/kata-runtime new file mode 100644 index 000000000..c293483d6 --- /dev/null +++ b/src/runtime/data/completions/bash/kata-runtime @@ -0,0 +1,138 @@ +#!/bin/bash +# +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +#--------------------------------------------------------------------- +# Description: Bash tab completion script. +#--------------------------------------------------------------------- + +_kataruntime='kata-runtime' + +# Return a list of sub-commands +_kata_get_subcmds() +{ + "$_kataruntime" --generate-bash-completion +} + +# Return a list of options for the specified sub-command +# +# Limitation: Note that this only supports long-options. +_kata_get_subcmd_options() +{ + local subcmd="$1" + + "$_kataruntime" "$subcmd" --help |\ + egrep -- "^ *--[^ ]*[ ][^ ]*" |\ + awk '{print $1}' |\ + tr -d \, |\ + sort +} + +# Return a list of global options +_kata_get_global_options() +{ + _kata_get_subcmd_options "" +} + +# Return name of subcmd already seen, or "" +_kata_subcmd_seen() +{ + local subcmds=$(_kata_get_subcmds) + local cmd + + for cmd in $subcmds; do + local word + for word in ${COMP_WORDS[@]}; do + [ "$cmd" = "$word" ] && echo "$cmd" + done + done + + echo "" +} + +# Return 0 if the specified sub-command requires the name of an +# *existing* container, else 1. +_kata_subcmd_needs_existing_container() +{ + local subcmd="$1" + local cmd + + for cmd in \ + 'kata-check' \ + 'kata-env' \ + 'create' \ + 'help' \ + 'list' \ + 'version'; do + [ "$cmd" = "$subcmd" ] && return 1 + done + + return 0 +} + +# Returns a list of container names +_kata_get_containers() +{ + # Commands that manipulate containers need root privileges. + # If the user isn't running as root, don't attempt to obtain a list + # as it will result in an error. + [ $(id -u) -eq 0 ] || return + + "$_kataruntime" list --quiet +} + +_kata_bash_autocomplete() { + COMPREPLY=() + + local opts opt + + local cur="${COMP_WORDS[COMP_CWORD]}" + + for opt in \ + '-h' '--help' 'help' \ + '-v' '--version' 'version'; + do + # No further completions possible for these commands + [ "$cur" = "$opt" ] && return 0 + done + + local subcmd_seen=$(_kata_subcmd_seen) + + if [ -n "$subcmd_seen" ]; then + _kata_subcmd_needs_existing_container "$subcmd_seen" + local container_cmd=$? + + if [ -n "$cur" ]; then + # Complete with local options and maybe container names + opts=$(_kata_get_subcmd_options "$subcmd_seen") + [ $container_cmd -eq 0 ] && opts="$opts $(_kata_get_containers)" + elif [[ "${cur}" == -* ]]; then + # Complete with local options + opts=$(_kata_get_subcmd_options "$subcmd_seen") + else + # Potentially complete with container names + [ $container_cmd -eq 0 ] && opts="$(_kata_get_containers)" + fi + else + if [ -n "$cur" ]; then + # Complete with global options and subcmds + opts="$opts $(_kata_get_global_options)" + opts="$opts $(_kata_get_subcmds)" + elif [[ "${cur}" == -* ]]; then + # Complete with global options + opts=$(_kata_get_global_options) + else + # Complete with subcmds + opts=$(_kata_get_subcmds) + fi + fi + + [ -n "$opts" ] && COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + + return 0 +} + +complete -F _kata_bash_autocomplete "$_kataruntime" diff --git a/src/runtime/data/kata-collect-data.sh.in b/src/runtime/data/kata-collect-data.sh.in new file mode 100644 index 000000000..b6f003db3 --- /dev/null +++ b/src/runtime/data/kata-collect-data.sh.in @@ -0,0 +1,754 @@ +#!/bin/bash +# +# Copyright (c) 2017-2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +typeset -r script_name=${0##*/} +typeset -r runtime_name="@RUNTIME_NAME@" +typeset -r runtime_path=$(command -v "$runtime_name" 2>/dev/null) +typeset -r runtime_snap_name="kata-containers.runtime" +typeset -r runtime_snap_path=$(command -v "$runtime_snap_name" 2>/dev/null) +typeset -r runtime=${runtime_path:-"$runtime_snap_path"} +typeset -r issue_url="@PROJECT_BUG_URL@" +typeset -r script_version="@VERSION@ (commit @COMMIT@)" + +typeset -r unknown="unknown" + +typeset -r osbuilder_file="/var/lib/osbuilder/osbuilder.yaml" + +# Maximum number of errors to show for a single system component +# (such as runtime or proxy). +PROBLEM_LIMIT=${PROBLEM_LIMIT:-50} + +# List of patterns used to detect problems in logfiles. +problem_pattern="(" +problem_pattern+="\&2 "ERROR: $script_name: $msg" + exit 1 +} + +msg() +{ + local msg="$*" + echo "$msg" +} + +heading() +{ + local name="$*" + echo -e "\n# $name\n" +} + +subheading() +{ + local name="$*" + echo -e "\n## $name\n" +} + +separator() +{ + echo -e '\n---\n' +} + +have_cmd() +{ + local cmd="$1" + + command -v "$cmd" &>/dev/null + local ret=$? + + if [ $ret -eq 0 ]; then + msg "Have \`$cmd\`" + else + msg "No \`$cmd\`" + fi + + [ $ret -eq 0 ] +} + +have_service() +{ + local service="$1" + + systemctl status "${service}" >/dev/null 2>&1 +} + +show_quoted_text() +{ + local language="$1" + + shift + + local text="$*" + + echo "\`\`\`${language}" + echo "$text" + echo "\`\`\`" +} + +run_cmd_and_show_quoted_output() +{ + local language="$1" + + shift + + local cmd="$*" + + local output + + msg "Output of \"\`$cmd\`\":" + output=$(eval "$cmd" 2>&1) + show_quoted_text "${language}" "$output" +} + +show_runtime_configs() +{ + local configs config + + heading "Runtime config files" + + configs=$($runtime --@PROJECT_TYPE@-show-default-config-paths) + if [ $? -ne 0 ]; then + version=$($runtime --version|tr '\n' ' ') + die "failed to check config files - runtime is probably too old ($version)" + fi + + subheading "Runtime default config files" + + show_quoted_text "" "$configs" + + # add in the standard defaults for good measure "just in case" + configs+=" /etc/@PROJECT_TAG@/configuration.toml" + configs+=" /usr/share/defaults/@PROJECT_TAG@/configuration.toml" + configs+=" @CONFIG_PATH@" + configs+=" @SYSCONFIG@" + + # create a unique list of config files + configs=$(echo $configs|tr ' ' '\n'|sort -u) + + subheading "Runtime config file contents" + + for config in $configs; do + if [ -e "$config" ]; then + run_cmd_and_show_quoted_output "toml" "cat \"$config\"" + else + msg "Config file \`$config\` not found" + fi + done + + separator +} + +show_log_details() +{ + heading "Logfiles" + + show_runtime_log_details + show_proxy_log_details + show_shim_log_details + show_throttler_log_details + + separator +} + +show_runtime_log_details() +{ + subheading "Runtime logs" + + find_system_journal_problems "runtime" "@RUNTIME_NAME@" "" +} + +find_system_journal_problems() +{ + local name="$1" + local program="$2" + local unit="$3" + + # select by identifier + local selector="-t" + + local data_source="system journal" + + local problems=$(journalctl -q -o cat -a "$selector" "$program" |\ + grep "time=" |\ + egrep -i "$problem_pattern" |\ + egrep -iv "$problem_exclude_pattern" |\ + tail -n ${PROBLEM_LIMIT}) + + if [ -n "$problems" ]; then + msg "Recent $name problems found in $data_source:" + show_quoted_text "" "$problems" + else + msg "No recent $name problems found in $data_source." + fi +} + +show_proxy_log_details() +{ + subheading "Proxy logs" + + find_system_journal_problems "proxy" "@PROJECT_TYPE@-proxy" "" +} + +show_shim_log_details() +{ + subheading "Shim logs" + + find_system_journal_problems "shim" "@PROJECT_TYPE@-shim" "" +} + +show_throttler_log_details() +{ + subheading "Throttler logs" + + find_system_journal_problems "throttler" "" "@PROJECT_TYPE@-ksm-throttler" +} + +show_package_versions() +{ + heading "Packages" + + local pattern="(" + local project + + # CC 2.x, 3.0 and runv runtimes. They shouldn't be installed but let's + # check anyway. + pattern+="cc-oci-runtime" + pattern+="cc-runtime" + pattern+="runv" + + # core components + for project in @PROJECT_TYPE@ + do + pattern+="|${project}-proxy" + pattern+="|${project}-runtime" + pattern+="|${project}-shim" + pattern+="|${project}-ksm-throttler" + pattern+="|${project}-containers-image" + done + + # assets + pattern+="|linux-container" + + # hypervisor name prefix + pattern+="|qemu-" + + pattern+=")" + + if have_cmd "dpkg"; then + run_cmd_and_show_quoted_output "" "dpkg -l|egrep \"$pattern\"" + fi + + if have_cmd "rpm"; then + run_cmd_and_show_quoted_output "" "rpm -qa|egrep \"$pattern\"" + fi + + separator +} + +show_container_mgr_details() +{ + heading "Container manager details" + + if have_cmd "docker"; then + subheading "Docker" + run_cmd_and_show_quoted_output "" "docker version" + run_cmd_and_show_quoted_output "" "docker info" + run_cmd_and_show_quoted_output "" "systemctl show docker" + fi + + if have_cmd "kubectl"; then + subheading "Kubernetes" + run_cmd_and_show_quoted_output "" "kubectl version" + run_cmd_and_show_quoted_output "" "kubectl config view" + run_cmd_and_show_quoted_output "" "systemctl show kubelet" + fi + + if have_cmd "crio"; then + subheading "crio" + run_cmd_and_show_quoted_output "" "crio --version" + run_cmd_and_show_quoted_output "" "systemctl show crio" + run_cmd_and_show_quoted_output "" "cat /etc/crio/crio.conf" + fi + + + if have_cmd "containerd"; then + subheading "containerd" + run_cmd_and_show_quoted_output "" "containerd --version" + run_cmd_and_show_quoted_output "" "systemctl show containerd" + run_cmd_and_show_quoted_output "" "cat /etc/containerd/config.toml" + fi + + separator +} + +show_meta() +{ + local date + + heading "Meta details" + + date=$(date '+%Y-%m-%d.%H:%M:%S.%N%z') + msg "Running \`$script_name\` version \`$script_version\` at \`$date\`." + + separator +} + +show_runtime() +{ + local cmd + + msg "Runtime is \`$runtime\`." + + cmd="@PROJECT_TYPE@-env" + heading "\`$cmd\`" + run_cmd_and_show_quoted_output "toml" "$runtime $cmd" + + separator +} + +# Parameter 1: Path to disk image file. +# Returns: Details of the image, or "$unknown" on error. +get_image_details() +{ + local img="$1" + + [ -z "$img" ] && { echo "$unknown"; return;} + [ -e "$img" ] || { echo "$unknown"; return;} + + local loop_device + local partition_path + local partitions + local partition + local count + local mountpoint + local contents + local expected + + loop_device=$(loopmount_image "$img") + if [ -z "$loop_device" ]; then + echo "$unknown" + return + fi + + partitions=$(get_partitions "$loop_device") + count=$(echo "$partitions"|wc -l) + + expected=1 + + if [ "$count" -ne "$expected" ]; then + release_device "$loop_device" + echo "$unknown" + return + fi + + partition="$partitions" + + partition_path="/dev/${partition}" + if [ ! -e "$partition_path" ]; then + release_device "$loop_device" + echo "$unknown" + return + fi + + mountpoint=$(mount_partition "$partition_path") + + contents=$(read_osbuilder_file "${mountpoint}") + [ -z "$contents" ] && contents="$unknown" + + unmount_partition "$mountpoint" + release_device "$loop_device" + + echo "$contents" +} + +# Parameter 1: Path to the initrd file. +# Returns: Details of the initrd, or "$unknown" on error. +get_initrd_details() +{ + local initrd="$1" + + [ -z "$initrd" ] && { echo "$unknown"; return;} + [ -e "$initrd" ] || { echo "$unknown"; return;} + + local file + local relative_file="" + local tmp + + file="${osbuilder_file}" + + # All files in the cpio archive are relative so remove leading slash + relative_file=$(echo "$file"|sed 's!^/!!g') + + local tmpdir=$(mktemp -d) + + # Note: 'cpio --directory' seems to be non-portable, so cd(1) instead. + (cd "$tmpdir" && gzip -dc "$initrd" | cpio \ + --extract \ + --make-directories \ + --no-absolute-filenames \ + $relative_file 2>/dev/null) + + contents=$(read_osbuilder_file "${tmpdir}") + [ -z "$contents" ] && contents="$unknown" + + tmp="${tmpdir}/${file}" + [ -d "$tmp" ] && rm -rf "$tmp" + + echo "$contents" +} + +# Returns: Full path to the image file. +get_image_file() +{ + local cmd="@PROJECT_TYPE@-env" + local cmdline="$runtime $cmd" + + local image=$(eval "$cmdline" 2>/dev/null |\ + grep -A 1 '^\[Image\]' |\ + egrep "\ =" |\ + awk '{print $3}' |\ + tr -d '"') + + echo "$image" +} + +# Returns: Full path to the initrd file. +get_initrd_file() +{ + local cmd="@PROJECT_TYPE@-env" + local cmdline="$runtime $cmd" + + local initrd=$(eval "$cmdline" 2>/dev/null |\ + grep -A 1 '^\[Initrd\]' |\ + egrep "\ =" |\ + awk '{print $3}' |\ + tr -d '"') + + echo "$initrd" +} + +# Parameter 1: Path to disk image file. +# Returns: Path to loop device. +loopmount_image() +{ + local img="$1" + [ -n "$img" ] || die "need image file" + + local device_path + + losetup -fP "$img" + + device_path=$(losetup -j "$img" |\ + cut -d: -f1 |\ + sort -k1,1 |\ + tail -1) + + echo "$device_path" +} + +# Parameter 1: Path to loop device. +# Returns: Partition names. +get_partitions() +{ + local device_path="$1" + [ -n "$device_path" ] || die "need device path" + + local device + local partitions + + device=${device_path/\/dev\//} + + partitions=$(lsblk -nli -o NAME "${device_path}" |\ + grep -v "^${device}$") + + echo "$partitions" +} + +# Parameter 1: Path to disk partition device. +# Returns: Mountpoint. +mount_partition() +{ + local partition="$1" + [ -n "$partition" ] || die "need partition" + [ -e "$partition" ] || die "partition does not exist: $partition" + + local mountpoint + + mountpoint=$(mktemp -d) + + mount -oro,noload "$partition" "$mountpoint" + + echo "$mountpoint" +} + +# Parameter 1: Mountpoint. +unmount_partition() +{ + local mountpoint="$1" + [ -n "$mountpoint" ] || die "need mountpoint" + [ -n "$mountpoint" ] || die "mountpoint does not exist: $mountpoint" + + umount "$mountpoint" +} + +# Parameter 1: Loop device path. +release_device() +{ + local device="$1" + [ -n "$device" ] || die "need device" + [ -e "$device" ] || die "device does not exist: $device" + + losetup -d "$device" +} + +show_throttler_details() +{ + heading "KSM throttler" + + subheading "version" + + local throttlers + local throttler + + throttlers=$(find /usr/libexec /usr/lib* -type f |\ + grep -v trigger |\ + egrep "(cc|kata)-ksm-throttler" |\ + sort -u) + + echo "$throttlers" | while read throttler + do + local cmd + cmd="$throttler --version" + run_cmd_and_show_quoted_output "" "$cmd" + done + + subheading "systemd service" + + local unit + + # Note: "vc-throttler" is the old CC service, replaced by + # "kata-vc-throttler". + for unit in \ + "cc-ksm-throttler" \ + "kata-ksm-throttler" \ + "kata-vc-throttler" \ + "vc-throttler" + do + have_service "${unit}" && \ + run_cmd_and_show_quoted_output "" "systemctl show ${unit}" + done +} + +# Retrieve details of the image containing +# the rootfs used to boot the virtual machine. +show_image_details() +{ + local image + local details + + image=$(get_image_file) + + heading "Image details" + + if [ -n "$image" ] + then + details=$(get_image_details "$image") + show_quoted_text "yaml" "$details" + else + msg "No image" + fi + + separator +} + +# Retrieve details of the initrd containing +# the rootfs used to boot the virtual machine. +show_initrd_details() +{ + local initrd + local details + + initrd=$(get_initrd_file) + + heading "Initrd details" + + if [ -n "$initrd" ] + then + details=$(get_initrd_details "$initrd") + show_quoted_text "yaml" "$details" + else + msg "No initrd" + fi + + separator +} + +read_osbuilder_file() +{ + local rootdir="$1" + + [ -n "$rootdir" ] || die "need root directory" + + local file="${rootdir}/${osbuilder_file}" + + [ ! -e "$file" ] && return + + cat "$file" +} + +show_header() +{ + cat < +Show $script_name details +

+EOT +} + +show_footer() +{ + cat < + +EOT +} + +show_details() +{ + show_header + + show_meta + show_runtime + show_runtime_configs + show_throttler_details + show_image_details + show_initrd_details + show_log_details + show_container_mgr_details + show_package_versions + + show_footer +} + +main() +{ + args=$(getopt \ + -n "$script_name" \ + -a \ + --options="dhv" \ + --longoptions="debug help version" \ + -- "$@") + + eval set -- "$args" + [ $? -ne 0 ] && { usage && exit 1; } + [ $# -eq 0 ] && { usage && exit 0; } + + while [ $# -gt 1 ] + do + case "$1" in + -d|--debug) + set -x + ;; + + -h|--help) + usage && exit 0 + ;; + + -v|--version) + version && exit 0 + ;; + + --) + shift + break + ;; + esac + shift + done + + [ $(id -u) -eq 0 ] || die "Need to run as root" + [ -n "$runtime" ] || die "cannot find runtime '$runtime_name'" + + show_details +} + +main "$@" diff --git a/src/runtime/go.mod b/src/runtime/go.mod new file mode 100644 index 000000000..3dab0226b --- /dev/null +++ b/src/runtime/go.mod @@ -0,0 +1,62 @@ +module github.com/kata-containers/kata-containers/src/runtime + +go 1.14 + +require ( + github.com/BurntSushi/toml v0.3.1 + github.com/blang/semver v0.0.0-20190414102917-ba2c2ddd8906 + github.com/cilium/ebpf v0.0.0-20200421083123-d05ecd062fb1 // indirect + github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601 + github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e + github.com/containerd/containerd v1.2.1-0.20181210191522-f05672357f56 + github.com/containerd/cri-containerd v1.11.1-0.20190125013620-4dd6735020f5 + github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448 + github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328 // indirect + github.com/containerd/ttrpc v1.0.0 // indirect + github.com/containerd/typeurl v1.0.1-0.20190228175220-2a93cfde8c20 + github.com/containernetworking/plugins v0.8.2 + github.com/cri-o/cri-o v1.0.0-rc2.0.20170928185954-3394b3b2d6af + github.com/dlespiau/covertool v0.0.0-20180314162135-b0c4c6d0583a + github.com/docker/go-units v0.3.3 + github.com/go-ini/ini v1.28.2 + github.com/go-openapi/errors v0.18.0 + github.com/go-openapi/runtime v0.18.0 + github.com/go-openapi/strfmt v0.18.0 + github.com/go-openapi/swag v0.18.0 + github.com/go-openapi/validate v0.18.0 + github.com/gogo/protobuf v1.2.0 + github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect + github.com/hashicorp/go-multierror v1.0.0 + github.com/hashicorp/yamux v0.0.0-20190923154419-df201c70410d // indirect + github.com/intel/govmm v0.0.0-20200304142514-e969afbec52c + github.com/kata-containers/agent v0.0.0-20200220202609-d26a505efd33 + github.com/kata-containers/runtime v0.0.0-00010101000000-000000000000 + github.com/mdlayher/vsock v0.0.0-20191108225356-d9c65923cb8f // indirect + github.com/mitchellh/mapstructure v1.1.2 + github.com/opencontainers/go-digest v1.0.0-rc1 // indirect + github.com/opencontainers/runc v1.0.0-rc9.0.20200102164712-2b52db75279c + github.com/opencontainers/runtime-spec v1.0.2-0.20190408193819-a1b50f621a48 + github.com/opencontainers/selinux v1.4.0 + github.com/opentracing/opentracing-go v1.1.0 + github.com/pkg/errors v0.8.1 + github.com/prometheus/procfs v0.0.0-20190328153300-af7bedc223fb + github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 + github.com/seccomp/libseccomp-golang v0.9.1 // indirect + github.com/sirupsen/logrus v1.4.2 + github.com/stretchr/testify v1.4.0 + github.com/uber/jaeger-client-go v0.0.0-20200422204034-e1cd868603cb + github.com/uber/jaeger-lib v2.2.0+incompatible // indirect + github.com/urfave/cli v1.20.1-0.20170926034118-ac249472b7de + github.com/vishvananda/netlink v1.0.1-0.20190604022042-c8c507c80ea2 + github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc + go.uber.org/atomic v1.6.0 // indirect + golang.org/x/net v0.0.0-20191108221443-4ba9e2ef068c + golang.org/x/oauth2 v0.0.0-20191122200657-5d9234df094c + golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f + google.golang.org/grpc v1.19.0 +) + +replace ( + github.com/kata-containers/runtime => ./ + github.com/uber-go/atomic => go.uber.org/atomic v1.5.1 +) diff --git a/src/runtime/go.sum b/src/runtime/go.sum new file mode 100644 index 000000000..dde77cb33 --- /dev/null +++ b/src/runtime/go.sum @@ -0,0 +1,293 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/blang/semver v0.0.0-20190414102917-ba2c2ddd8906 h1:KGe2go3VELJLcQfKBUlviUzERqg79dO6VYzCvQxF01w= +github.com/blang/semver v0.0.0-20190414102917-ba2c2ddd8906/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/cilium/ebpf v0.0.0-20200421083123-d05ecd062fb1 h1:50o2sirbVATmvMz+i8+fGLgSDGd91hozN5wvHgiyXh8= +github.com/cilium/ebpf v0.0.0-20200421083123-d05ecd062fb1/go.mod h1:XT+cAw5wfvsodedcijoh1l9cf7v1x9FlFB/3VmF/O8s= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601 h1:6xW3ogNpFIly0umJGEKzFfGDNUk5rXFE1lJ3/gBmz3U= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e h1:GdiIYd8ZDOrT++e1NjhSD4rGt9zaJukHm4rt5F4mRQc= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/containerd v1.2.1-0.20181210191522-f05672357f56 h1:KBZ3QBVr3gBySdyLzDs5Yom/oBH2hzhS03h6lxgkv7U= +github.com/containerd/containerd v1.2.1-0.20181210191522-f05672357f56/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb h1:nXPkFq8X1a9ycY3GYQpFNxHh3j2JgY7zDZfq2EXMIzk= +github.com/containerd/continuity v0.0.0-20200413184840-d3ef23f19fbb/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= +github.com/containerd/cri-containerd v1.11.1-0.20190125013620-4dd6735020f5 h1:/srF029I+oDfm/qeltxCGJyJ8urmlqWGOQmQ7HvwrRc= +github.com/containerd/cri-containerd v1.11.1-0.20190125013620-4dd6735020f5/go.mod h1:wxbGdReWGCalzGOEpifoHeYCK4xAgnj4o/4bVB+9voU= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448 h1:PUD50EuOMkXVcpBIA/R95d56duJR9VxhwncsFbNnxW4= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328 h1:PRTagVMbJcCezLcHXe8UJvR1oBzp2lG3CEumeFOLOds= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/ttrpc v1.0.0 h1:NY8Zk2i7TpkLxrkOASo+KTFq9iNCEmMH2/ZG9OuOw6k= +github.com/containerd/ttrpc v1.0.0/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v1.0.1-0.20190228175220-2a93cfde8c20 h1:ZPfpTtR1qYoir10vhnnnQYGpnf1ZGiTGG+YGfPieydk= +github.com/containerd/typeurl v1.0.1-0.20190228175220-2a93cfde8c20/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containernetworking/cni v0.7.0 h1:1Qy7EwdC08mx5wUB0DpjCuBrk6e/uXg9yI9TvAvgox8= +github.com/containernetworking/cni v0.7.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.2 h1:5lnwfsAYO+V7yXhysJKy3E1A2Gy9oVut031zfdOzI9w= +github.com/containernetworking/plugins v0.8.2/go.mod h1:TxALKWZpWL79BC3GOYKJzzXr7U8R23PdhwaLp6F3adc= +github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/cri-o/cri-o v1.0.0-rc2.0.20170928185954-3394b3b2d6af h1:H6nLV96F1LkWizYLQtrMtqJBrlJxnpjgisHsTsOS2HU= +github.com/cri-o/cri-o v1.0.0-rc2.0.20170928185954-3394b3b2d6af/go.mod h1:POmDVglzQ2jWTlL9ZCfZ8d1QjLhmk0oB36O8T0oG75Y= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlespiau/covertool v0.0.0-20180314162135-b0c4c6d0583a h1:+cYgqwB++gEE09SluRYGqJyDhWmLmdWZ2cXlOXSGV8w= +github.com/dlespiau/covertool v0.0.0-20180314162135-b0c4c6d0583a/go.mod h1:/eQMcW3eA1bzKx23ZYI2H3tXPdJB5JWYTHzoUPBvQY4= +github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-ini/ini v1.28.2 h1:drmmYv7psRpoGZkPtPKKTB+ZFSnvmwCMfNj5o1nLh2Y= +github.com/go-ini/ini v1.28.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.17.2 h1:eYp14J1o8TTSCzndHBtsNuckikV1PfZOSnx4BcBeu0c= +github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0 h1:+RnmJ5MQccF7jwWAoMzwOpzJEspZ18ZIWfg9Z2eiXq8= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.17.2 h1:3ekBy41gar/iJi2KSh/au/PrC2vpLr85upF/UZmm3W0= +github.com/go-openapi/jsonpointer v0.17.2/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.17.2 h1:lF3z7AH8dd0IKXc1zEBi1dj0B4XgVb5cVjn39dCK3Ls= +github.com/go-openapi/jsonreference v0.17.2/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.17.2 h1:tEXYu6Xc0pevpzzQx5ghrMN9F7IVpN/+u4iD3rkYE5o= +github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.18.0 h1:ddoL4Uo/729XbNAS9UIsG7Oqa8R8l2edBe6Pq/i8AHM= +github.com/go-openapi/runtime v0.18.0/go.mod h1:uI6pHuxWYTy94zZxgcwJkUWa9wbIlhteGfloI10GD4U= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.17.2 h1:eb2NbuCnoe8cWAxhtK6CfMWUYmiFEZJ9Hx3Z2WRwJ5M= +github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.17.2/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0 h1:FqqmmVCKn3di+ilU/+1m957T1CnMz3IteVUcV3aGXWA= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0 h1:1DU8Km1MRGv9Pj7BNLmkA+umwTStwDHttXvx3NhJA70= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/validate v0.17.2/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.18.0 h1:PVXYcP1GkTl+XIAJnyJxOmK6CSG5Q1UcvoCvNO++5Kg= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c h1:RBUpb2b14UnmRHNd2uHz20ZHLDK+SW5Us/vWF5IHRaY= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= +github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/yamux v0.0.0-20190923154419-df201c70410d h1:W+SIwDdl3+jXWeidYySAgzytE3piq6GumXeBjFBG67c= +github.com/hashicorp/yamux v0.0.0-20190923154419-df201c70410d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/intel/govmm v0.0.0-20200304142514-e969afbec52c h1:hbbnB7xG1bSzUBqSTeNATPODx3CXM/omWUF8RMfFY5s= +github.com/intel/govmm v0.0.0-20200304142514-e969afbec52c/go.mod h1:QKGWoQtjvkvFtzP6ybiM3lxUHqf83Sv3oLqyELUKH4g= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v0.0.0-20180806074554-22422dad46e1/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20190613124551-e81189438503/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= +github.com/kata-containers/agent v0.0.0-20200220202609-d26a505efd33 h1:dAGWEirqmPkwhfXdvJUoqJ42iBeKiftOOSRtyh2nAyk= +github.com/kata-containers/agent v0.0.0-20200220202609-d26a505efd33/go.mod h1:1fRxT0ia2SxN0kQA6j0GWheULRihC4OPhsNj6i4OJ8M= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mdlayher/vsock v0.0.0-20191108225356-d9c65923cb8f h1:t9bhAC/9+wqdIb49Jamux+Sxqa7MhkyuTtsHkmVg6tk= +github.com/mdlayher/vsock v0.0.0-20191108225356-d9c65923cb8f/go.mod h1:4GtNxrXX+cNil8xnCdz0zGYemDZDDHSsXbopCRZrRRw= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b h1:Ey6yH0acn50T/v6CB75bGP4EMJqnv9WvnjN7oZaj+xE= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a h1:KfNOeFvoAssuZLT7IntKZElKwi/5LRuxY71k+t6rfaM= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/runc v1.0.0-rc9.0.20200102164712-2b52db75279c h1:9EMFehIYZPnCFOz8NJ5d3DyBUY51q/G91WsGBK304jY= +github.com/opencontainers/runc v1.0.0-rc9.0.20200102164712-2b52db75279c/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190408193819-a1b50f621a48 h1:746IARoYp4pp60xZnIH1/BkXyGb+jCulLAjMYECA9wk= +github.com/opencontainers/runtime-spec v1.0.2-0.20190408193819-a1b50f621a48/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.4.0 h1:cpiX/2wWIju/6My60T6/z9CxNG7c8xTQyEmA9fChpUo= +github.com/opencontainers/selinux v1.4.0/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/procfs v0.0.0-20190328153300-af7bedc223fb h1:LvNCMEj0FFZQYsxZb7o3xQPrtqOOB6lrTUOWshC+ZTs= +github.com/prometheus/procfs v0.0.0-20190328153300-af7bedc223fb/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/uber/jaeger-client-go v0.0.0-20200422204034-e1cd868603cb h1:2pjwLvZD3lEkfIyf6+0hgRjNhCM0Db1qtSEuUWbkcp4= +github.com/uber/jaeger-client-go v0.0.0-20200422204034-e1cd868603cb/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= +github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/urfave/cli v1.20.1-0.20170926034118-ac249472b7de h1:JdxpV8v2ICFdGFFFUI7X6xgG2X97lc9ncYGstss+MqE= +github.com/urfave/cli v1.20.1-0.20170926034118-ac249472b7de/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.0.1-0.20190604022042-c8c507c80ea2 h1:CKtRQjq4fvkVq45jsctU8zNQGve2HHR5dGEhl0Sndt8= +github.com/vishvananda/netlink v1.0.1-0.20190604022042-c8c507c80ea2/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc h1:R83G5ikgLMxrBvLh22JhdfI8K6YXEPHx5P03Uu3DRs4= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191108221443-4ba9e2ef068c h1:SRpq/kuj/xNci/RdvEs+RSvpfxqvLAzTKuKGlzoGdZQ= +golang.org/x/net v0.0.0-20191108221443-4ba9e2ef068c/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20191122200657-5d9234df094c h1:HjRaKPaiWks0f5tA6ELVF7ZfqSppfPwOEEAvsrKUTO4= +golang.org/x/oauth2 v0.0.0-20191122200657-5d9234df094c/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.3.0+incompatible h1:oTCKjb7ZuVfn37AQodk7UysjQQL/S5Dep3pzi59u1NQ= +gotest.tools v2.3.0+incompatible/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/src/runtime/golang.mk b/src/runtime/golang.mk new file mode 100644 index 000000000..202c0bacd --- /dev/null +++ b/src/runtime/golang.mk @@ -0,0 +1,62 @@ +# +# Copyright (c) 2018 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# +# Check that the system golang version is within the required version range +# for this project. + +golang_version_raw=$(shell go version 2>/dev/null) +not_check_version= +ifeq (,$(GOPATH)) + golang_version_raw= +endif +ifeq (,$(golang_version_raw)) + not_check_version=y +endif +ifneq (,$(SKIP_GO_VERSION_CHECK)) + not_check_version=y +endif + +ifeq (,$(not_check_version)) + have_yq=$(shell if [ -x "$(GOPATH)/bin/yq" ]; then echo "true"; else echo ""; fi) + ifeq (,$(have_yq)) + $(info INFO: yq was not found, installing it) + install_yq=$(shell .ci/install-yq.sh) + endif + ifneq (,$(install_yq)) + $(error "ERROR: install yq failed") + endif + golang_version_min=$(shell $(GOPATH)/bin/yq r versions.yaml languages.golang.version) + + ifeq (,$(golang_version_min)) + $(error "ERROR: cannot determine minimum golang version") + endif + + golang_version_min_fields=$(subst ., ,$(golang_version_min)) + + golang_version_min_major=$(word 1,$(golang_version_min_fields)) + golang_version_min_minor=$(word 2,$(golang_version_min_fields)) + + # for error messages + golang_version_needed=$(golang_version_min_major).$(golang_version_min_minor) + + # determine actual version of golang + golang_version=$(subst go,,$(word 3,$(golang_version_raw))) + + golang_version_fields=$(subst ., ,$(golang_version)) + + golang_version_major=$(word 1,$(golang_version_fields)) + golang_version_minor=$(word 2,$(golang_version_fields)) + + golang_major_ok=$(shell test $(golang_version_major) -ge $(golang_version_min_major) && echo ok) + golang_minor_ok=$(shell test $(golang_version_major) -eq $(golang_version_min_major) -a $(golang_version_minor) -ge $(golang_version_min_minor) && echo ok) + + ifeq (,$(golang_major_ok)) + $(error "ERROR: golang major version too old: got $(golang_version), need atleast $(golang_version_needed)") + endif + + ifeq (,$(golang_minor_ok)) + $(error "ERROR: golang minor version too old: got $(golang_version), need atleast $(golang_version_needed)") + endif +endif diff --git a/src/runtime/hack/selinux_tag.sh b/src/runtime/hack/selinux_tag.sh new file mode 100755 index 000000000..5710ef2c5 --- /dev/null +++ b/src/runtime/hack/selinux_tag.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# +# Copyright 2020 Red Hat Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +pkg-config libselinux 2> /dev/null && echo selinux diff --git a/src/runtime/hack/update-generated-runtime-proto.sh b/src/runtime/hack/update-generated-runtime-proto.sh new file mode 100755 index 000000000..d2881a48b --- /dev/null +++ b/src/runtime/hack/update-generated-runtime-proto.sh @@ -0,0 +1,14 @@ +# +# Copyright 2019 HyperHQ Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +protoc \ + -I=$GOPATH/src \ + -I=$GOPATH/src/github.com/gogo/protobuf/protobuf \ + --proto_path=protocols/cache \ + --gogofast_out=\ +Mgoogle/protobuf/empty.proto=github.com/gogo/protobuf/types,\ +plugins=grpc:protocols/cache \ + protocols/cache/cache.proto diff --git a/src/runtime/netmon/netmon.go b/src/runtime/netmon/netmon.go new file mode 100644 index 000000000..94305ce2c --- /dev/null +++ b/src/runtime/netmon/netmon.go @@ -0,0 +1,681 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "encoding/json" + "errors" + "flag" + "fmt" + "io/ioutil" + "log/syslog" + "net" + "os" + "os/exec" + "os/signal" + "path/filepath" + "strings" + "syscall" + "time" + + "github.com/kata-containers/runtime/pkg/signals" + vcTypes "github.com/kata-containers/runtime/virtcontainers/pkg/types" + "github.com/sirupsen/logrus" + lSyslog "github.com/sirupsen/logrus/hooks/syslog" + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" +) + +const ( + netmonName = "kata-netmon" + + kataCmd = "kata-network" + kataCLIAddIfaceCmd = "add-iface" + kataCLIDelIfaceCmd = "del-iface" + kataCLIUpdtRoutesCmd = "update-routes" + + kataSuffix = "kata" + + // sharedFile is the name of the file that will be used to share + // the data between this process and the kata-runtime process + // responsible for updating the network. + sharedFile = "shared.json" + storageFilePerm = os.FileMode(0640) + storageDirPerm = os.FileMode(0750) +) + +var ( + // version is the netmon version. This variable is populated at build time. + version = "unknown" + + // For simplicity the code will only focus on IPv4 addresses for now. + netlinkFamily = netlink.FAMILY_ALL + + storageParentPath = "/var/run/kata-containers/netmon/sbs" +) + +type netmonParams struct { + sandboxID string + runtimePath string + debug bool + logLevel string +} + +type netmon struct { + netmonParams + + storagePath string + sharedFile string + + netIfaces map[int]vcTypes.Interface + + linkUpdateCh chan netlink.LinkUpdate + linkDoneCh chan struct{} + + rtUpdateCh chan netlink.RouteUpdate + rtDoneCh chan struct{} + + netHandler *netlink.Handle +} + +var netmonLog = logrus.New() + +func printVersion() { + fmt.Printf("%s version %s\n", netmonName, version) +} + +const componentDescription = `is a network monitoring process that is intended to be started in the +appropriate network namespace so that it can listen to any event related to +link and routes. Whenever a new interface or route is created/updated, it is +responsible for calling into the kata-runtime CLI to ask for the actual +creation/update of the given interface or route. +` + +func printComponentDescription() { + fmt.Printf("\n%s %s\n", netmonName, componentDescription) +} + +func parseOptions() netmonParams { + var version, help bool + + params := netmonParams{} + + flag.BoolVar(&help, "h", false, "describe component usage") + flag.BoolVar(&help, "help", false, "") + flag.BoolVar(¶ms.debug, "d", false, "enable debug mode") + flag.BoolVar(&version, "v", false, "display program version and exit") + flag.BoolVar(&version, "version", false, "") + flag.StringVar(¶ms.sandboxID, "s", "", "sandbox id (required)") + flag.StringVar(¶ms.runtimePath, "r", "", "runtime path (required)") + flag.StringVar(¶ms.logLevel, "log", "warn", + "log messages above specified level: debug, warn, error, fatal or panic") + + flag.Parse() + + if help { + printComponentDescription() + flag.PrintDefaults() + os.Exit(0) + } + + if version { + printVersion() + os.Exit(0) + } + + if params.sandboxID == "" { + fmt.Fprintf(os.Stderr, "Error: sandbox id is empty, one must be provided\n") + flag.PrintDefaults() + os.Exit(1) + } + + if params.runtimePath == "" { + fmt.Fprintf(os.Stderr, "Error: runtime path is empty, one must be provided\n") + flag.PrintDefaults() + os.Exit(1) + } + + return params +} + +func newNetmon(params netmonParams) (*netmon, error) { + handler, err := netlink.NewHandle(netlinkFamily) + if err != nil { + return nil, err + } + + n := &netmon{ + netmonParams: params, + storagePath: filepath.Join(storageParentPath, params.sandboxID), + sharedFile: filepath.Join(storageParentPath, params.sandboxID, sharedFile), + netIfaces: make(map[int]vcTypes.Interface), + linkUpdateCh: make(chan netlink.LinkUpdate), + linkDoneCh: make(chan struct{}), + rtUpdateCh: make(chan netlink.RouteUpdate), + rtDoneCh: make(chan struct{}), + netHandler: handler, + } + + if err := os.MkdirAll(n.storagePath, storageDirPerm); err != nil { + return nil, err + } + + return n, nil +} + +func (n *netmon) cleanup() { + os.RemoveAll(n.storagePath) + n.netHandler.Delete() + close(n.linkDoneCh) + close(n.rtDoneCh) +} + +// setupSignalHandler sets up signal handling, starting a go routine to deal +// with signals as they arrive. +func (n *netmon) setupSignalHandler() { + signals.SetLogger(n.logger()) + + sigCh := make(chan os.Signal, 8) + + for _, sig := range signals.HandledSignals() { + signal.Notify(sigCh, sig) + } + + go func() { + for { + sig := <-sigCh + + nativeSignal, ok := sig.(syscall.Signal) + if !ok { + err := errors.New("unknown signal") + netmonLog.WithError(err).WithField("signal", sig.String()).Error() + continue + } + + if signals.FatalSignal(nativeSignal) { + netmonLog.WithField("signal", sig).Error("received fatal signal") + signals.Die(nil) + } else if n.debug && signals.NonFatalSignal(nativeSignal) { + netmonLog.WithField("signal", sig).Debug("handling signal") + signals.Backtrace() + } + } + }() +} + +func (n *netmon) logger() *logrus.Entry { + fields := logrus.Fields{ + "name": netmonName, + "pid": os.Getpid(), + "source": "netmon", + } + + if n.sandboxID != "" { + fields["sandbox"] = n.sandboxID + } + + return netmonLog.WithFields(fields) +} + +func (n *netmon) setupLogger() error { + level, err := logrus.ParseLevel(n.logLevel) + if err != nil { + return err + } + + netmonLog.SetLevel(level) + + netmonLog.Formatter = &logrus.TextFormatter{TimestampFormat: time.RFC3339Nano} + + hook, err := lSyslog.NewSyslogHook("", "", syslog.LOG_INFO|syslog.LOG_USER, netmonName) + if err != nil { + return err + } + + netmonLog.AddHook(hook) + + announceFields := logrus.Fields{ + "runtime-path": n.runtimePath, + "debug": n.debug, + "log-level": n.logLevel, + } + + n.logger().WithFields(announceFields).Info("announce") + + return nil +} + +func (n *netmon) listenNetlinkEvents() error { + if err := netlink.LinkSubscribe(n.linkUpdateCh, n.linkDoneCh); err != nil { + return err + } + + return netlink.RouteSubscribe(n.rtUpdateCh, n.rtDoneCh) +} + +// convertInterface converts a link and its IP addresses as defined by netlink +// package, into the Interface structure format expected by kata-runtime to +// describe an interface and its associated IP addresses. +func convertInterface(linkAttrs *netlink.LinkAttrs, linkType string, addrs []netlink.Addr) vcTypes.Interface { + if linkAttrs == nil { + netmonLog.Warn("Link attributes are nil") + return vcTypes.Interface{} + } + + var ipAddrs []*vcTypes.IPAddress + + for _, addr := range addrs { + if addr.IPNet == nil { + continue + } + + netMask, _ := addr.Mask.Size() + + ipAddr := &vcTypes.IPAddress{ + Address: addr.IP.String(), + Mask: fmt.Sprintf("%d", netMask), + } + + if addr.IP.To4() != nil { + ipAddr.Family = netlink.FAMILY_V4 + } else { + ipAddr.Family = netlink.FAMILY_V6 + } + + ipAddrs = append(ipAddrs, ipAddr) + } + + iface := vcTypes.Interface{ + Device: linkAttrs.Name, + Name: linkAttrs.Name, + IPAddresses: ipAddrs, + Mtu: uint64(linkAttrs.MTU), + HwAddr: linkAttrs.HardwareAddr.String(), + LinkType: linkType, + } + + netmonLog.WithField("interface", iface).Debug("Interface converted") + + return iface +} + +// convertRoutes converts a list of routes as defined by netlink package, +// into a list of Route structure format expected by kata-runtime to +// describe a set of routes. +func convertRoutes(netRoutes []netlink.Route) []vcTypes.Route { + var routes []vcTypes.Route + + for _, netRoute := range netRoutes { + dst := "" + + if netRoute.Protocol == unix.RTPROT_KERNEL { + continue + } + + if netRoute.Dst != nil { + dst = netRoute.Dst.String() + if netRoute.Dst.IP.To4() != nil || netRoute.Dst.IP.To16() != nil { + dst = netRoute.Dst.String() + } else { + netmonLog.WithField("destination", netRoute.Dst.IP.String()).Warn("Unexpected network address format") + } + } + + src := "" + if netRoute.Src != nil { + if netRoute.Src.To4() != nil || netRoute.Src.To16() != nil { + src = netRoute.Src.String() + } else { + netmonLog.WithField("source", netRoute.Src.String()).Warn("Unexpected network address format") + } + } + + gw := "" + if netRoute.Gw != nil { + if netRoute.Gw.To4() != nil || netRoute.Gw.To16() != nil { + gw = netRoute.Gw.String() + } else { + netmonLog.WithField("gateway", netRoute.Gw.String()).Warn("Unexpected network address format") + } + } + + dev := "" + iface, err := net.InterfaceByIndex(netRoute.LinkIndex) + if err == nil { + dev = iface.Name + } + + route := vcTypes.Route{ + Dest: dst, + Gateway: gw, + Device: dev, + Source: src, + Scope: uint32(netRoute.Scope), + } + + routes = append(routes, route) + } + + netmonLog.WithField("routes", routes).Debug("Routes converted") + + return routes +} + +// scanNetwork lists all the interfaces it can find inside the current +// network namespace, and store them in-memory to keep track of them. +func (n *netmon) scanNetwork() error { + links, err := n.netHandler.LinkList() + if err != nil { + return err + } + + for _, link := range links { + addrs, err := n.netHandler.AddrList(link, netlinkFamily) + if err != nil { + return err + } + + linkAttrs := link.Attrs() + if linkAttrs == nil { + continue + } + + iface := convertInterface(linkAttrs, link.Type(), addrs) + n.netIfaces[linkAttrs.Index] = iface + } + + n.logger().Debug("Network scanned") + + return nil +} + +func (n *netmon) storeDataToSend(data interface{}) error { + // Marshal the data structure into a JSON bytes array. + jsonArray, err := json.Marshal(data) + if err != nil { + return err + } + + // Store the JSON bytes array at the specified path. + return ioutil.WriteFile(n.sharedFile, jsonArray, storageFilePerm) +} + +func (n *netmon) execKataCmd(subCmd string) error { + execCmd := exec.Command(n.runtimePath, kataCmd, subCmd, n.sandboxID, n.sharedFile) + + n.logger().WithField("command", execCmd).Debug("Running runtime command") + + // Make use of Run() to ensure the kata-runtime process has correctly + // terminated before to go further. + if err := execCmd.Run(); err != nil { + return err + } + + // Remove the shared file after the command returned. At this point + // we know the content of the file is not going to be used anymore, + // and the file path can be reused for further commands. + return os.Remove(n.sharedFile) +} + +func (n *netmon) addInterfaceCLI(iface vcTypes.Interface) error { + if err := n.storeDataToSend(iface); err != nil { + return err + } + + return n.execKataCmd(kataCLIAddIfaceCmd) +} + +func (n *netmon) delInterfaceCLI(iface vcTypes.Interface) error { + if err := n.storeDataToSend(iface); err != nil { + return err + } + + return n.execKataCmd(kataCLIDelIfaceCmd) +} + +func (n *netmon) updateRoutesCLI(routes []vcTypes.Route) error { + if err := n.storeDataToSend(routes); err != nil { + return err + } + + return n.execKataCmd(kataCLIUpdtRoutesCmd) +} + +func (n *netmon) updateRoutes() error { + // Get all the routes. + netlinkRoutes, err := n.netHandler.RouteList(nil, netlinkFamily) + if err != nil { + return err + } + + // Translate them into Route structures. + routes := convertRoutes(netlinkRoutes) + + // Update the routes through the Kata CLI. + return n.updateRoutesCLI(routes) +} + +func (n *netmon) handleRTMNewAddr(ev netlink.LinkUpdate) error { + n.logger().Debug("Interface update not supported") + return nil +} + +func (n *netmon) handleRTMDelAddr(ev netlink.LinkUpdate) error { + n.logger().Debug("Interface update not supported") + return nil +} + +func (n *netmon) handleRTMNewLink(ev netlink.LinkUpdate) error { + // NEWLINK might be a lot of different things. We're interested in + // adding the interface (both to our list and by calling into the + // Kata CLI API) only if this has the flags UP and RUNNING, meaning + // we don't expect any further change on the interface, and that we + // are ready to add it. + + linkAttrs := ev.Link.Attrs() + if linkAttrs == nil { + n.logger().Warn("The link attributes are nil") + return nil + } + + // First, ignore if the interface name contains "kata". This way we + // are preventing from adding interfaces created by Kata Containers. + if strings.HasSuffix(linkAttrs.Name, kataSuffix) { + n.logger().Debugf("Ignore the interface %s because found %q", + linkAttrs.Name, kataSuffix) + return nil + } + + // Check if the interface exist in the internal list. + if _, exist := n.netIfaces[int(ev.Index)]; exist { + n.logger().Debugf("Ignoring interface %s because already exist", + linkAttrs.Name) + return nil + } + + // Now, check if the interface has been enabled to UP and RUNNING. + if (ev.Flags&unix.IFF_UP) != unix.IFF_UP || + (ev.Flags&unix.IFF_RUNNING) != unix.IFF_RUNNING { + n.logger().Debugf("Ignore the interface %s because not UP and RUNNING", + linkAttrs.Name) + return nil + } + + // Get the list of IP addresses associated with this interface. + addrs, err := n.netHandler.AddrList(ev.Link, netlinkFamily) + if err != nil { + return err + } + + // Convert the interfaces in the appropriate structure format. + iface := convertInterface(linkAttrs, ev.Link.Type(), addrs) + + // Add the interface through the Kata CLI. + if err := n.addInterfaceCLI(iface); err != nil { + return err + } + + // Add the interface to the internal list. + n.netIfaces[linkAttrs.Index] = iface + + // Complete by updating the routes. + return n.updateRoutes() +} + +func (n *netmon) handleRTMDelLink(ev netlink.LinkUpdate) error { + // It can only delete if identical interface is found in the internal + // list of interfaces. Otherwise, the deletion will be ignored. + linkAttrs := ev.Link.Attrs() + if linkAttrs == nil { + n.logger().Warn("Link attributes are nil") + return nil + } + + // First, ignore if the interface name contains "kata". This way we + // are preventing from deleting interfaces created by Kata Containers. + if strings.Contains(linkAttrs.Name, kataSuffix) { + n.logger().Debugf("Ignore the interface %s because found %q", + linkAttrs.Name, kataSuffix) + return nil + } + + // Check if the interface exist in the internal list. + iface, exist := n.netIfaces[int(ev.Index)] + if !exist { + n.logger().Debugf("Ignoring interface %s because not found", + linkAttrs.Name) + return nil + } + + if err := n.delInterfaceCLI(iface); err != nil { + return err + } + + // Delete the interface from the internal list. + delete(n.netIfaces, linkAttrs.Index) + + // Complete by updating the routes. + return n.updateRoutes() +} + +func (n *netmon) handleRTMNewRoute(ev netlink.RouteUpdate) error { + // Add the route through updateRoutes(), only if the route refer to an + // interface that already exists in the internal list of interfaces. + if _, exist := n.netIfaces[ev.Route.LinkIndex]; !exist { + n.logger().Debugf("Ignoring route %+v since interface %d not found", + ev.Route, ev.Route.LinkIndex) + return nil + } + + return n.updateRoutes() +} + +func (n *netmon) handleRTMDelRoute(ev netlink.RouteUpdate) error { + // Remove the route through updateRoutes(), only if the route refer to + // an interface that already exists in the internal list of interfaces. + return n.updateRoutes() +} + +func (n *netmon) handleLinkEvent(ev netlink.LinkUpdate) error { + n.logger().Debug("handleLinkEvent: netlink event received") + + switch ev.Header.Type { + case unix.NLMSG_DONE: + n.logger().Debug("NLMSG_DONE") + return nil + case unix.NLMSG_ERROR: + n.logger().Error("NLMSG_ERROR") + return fmt.Errorf("Error while listening on netlink socket") + case unix.RTM_NEWADDR: + n.logger().Debug("RTM_NEWADDR") + return n.handleRTMNewAddr(ev) + case unix.RTM_DELADDR: + n.logger().Debug("RTM_DELADDR") + return n.handleRTMDelAddr(ev) + case unix.RTM_NEWLINK: + n.logger().Debug("RTM_NEWLINK") + return n.handleRTMNewLink(ev) + case unix.RTM_DELLINK: + n.logger().Debug("RTM_DELLINK") + return n.handleRTMDelLink(ev) + default: + n.logger().Warnf("Unknown msg type %v", ev.Header.Type) + } + + return nil +} + +func (n *netmon) handleRouteEvent(ev netlink.RouteUpdate) error { + n.logger().Debug("handleRouteEvent: netlink event received") + + switch ev.Type { + case unix.RTM_NEWROUTE: + n.logger().Debug("RTM_NEWROUTE") + return n.handleRTMNewRoute(ev) + case unix.RTM_DELROUTE: + n.logger().Debug("RTM_DELROUTE") + return n.handleRTMDelRoute(ev) + default: + n.logger().Warnf("Unknown msg type %v", ev.Type) + } + + return nil +} + +func (n *netmon) handleEvents() (err error) { + for { + select { + case ev := <-n.linkUpdateCh: + if err = n.handleLinkEvent(ev); err != nil { + return err + } + case ev := <-n.rtUpdateCh: + if err = n.handleRouteEvent(ev); err != nil { + return err + } + } + } +} + +func main() { + // Parse parameters. + params := parseOptions() + + // Create netmon handler. + n, err := newNetmon(params) + if err != nil { + netmonLog.WithError(err).Fatal("newNetmon()") + os.Exit(1) + } + defer n.cleanup() + + // Init logger. + if err := n.setupLogger(); err != nil { + netmonLog.WithError(err).Fatal("setupLogger()") + os.Exit(1) + } + + // Setup signal handlers + n.setupSignalHandler() + + // Scan the current interfaces. + if err := n.scanNetwork(); err != nil { + n.logger().WithError(err).Fatal("scanNetwork()") + os.Exit(1) + } + + // Subscribe to the link listener. + if err := n.listenNetlinkEvents(); err != nil { + n.logger().WithError(err).Fatal("listenNetlinkEvents()") + os.Exit(1) + } + + // Go into the main loop. + if err := n.handleEvents(); err != nil { + n.logger().WithError(err).Fatal("handleEvents()") + os.Exit(1) + } +} diff --git a/src/runtime/netmon/netmon_test.go b/src/runtime/netmon/netmon_test.go new file mode 100644 index 000000000..e21488064 --- /dev/null +++ b/src/runtime/netmon/netmon_test.go @@ -0,0 +1,699 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net" + "os" + "os/exec" + "path/filepath" + "reflect" + "runtime" + "testing" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + vcTypes "github.com/kata-containers/runtime/virtcontainers/pkg/types" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" + "golang.org/x/sys/unix" +) + +const ( + testSandboxID = "123456789" + testRuntimePath = "/foo/bar/test-runtime" + testLogLevel = "info" + testStorageParentPath = "/tmp/netmon" + testSharedFile = "foo-shared.json" + testWrongNetlinkFamily = -1 + testIfaceName = "test_eth0" + testMTU = 12345 + testHwAddr = "02:00:ca:fe:00:48" + testIPAddress = "192.168.0.15" + testIPAddressWithMask = "192.168.0.15/32" + testIP6Address = "2001:db8:1::242:ac11:2" + testIP6AddressWithMask = "2001:db8:1::/64" + testScope = 1 + testTxQLen = -1 + testIfaceIndex = 5 +) + +func skipUnlessRoot(t *testing.T) { + tc := ktu.NewTestConstraint(false) + + if tc.NotValid(ktu.NeedRoot()) { + t.Skip("Test disabled as requires root user") + } +} + +func TestNewNetmon(t *testing.T) { + skipUnlessRoot(t) + + // Override storageParentPath + savedStorageParentPath := storageParentPath + storageParentPath = testStorageParentPath + defer func() { + storageParentPath = savedStorageParentPath + }() + + params := netmonParams{ + sandboxID: testSandboxID, + runtimePath: testRuntimePath, + debug: true, + logLevel: testLogLevel, + } + + expected := &netmon{ + netmonParams: params, + storagePath: filepath.Join(storageParentPath, params.sandboxID), + sharedFile: filepath.Join(storageParentPath, params.sandboxID, sharedFile), + } + + os.RemoveAll(expected.storagePath) + + got, err := newNetmon(params) + assert.Nil(t, err) + assert.True(t, reflect.DeepEqual(expected.netmonParams, got.netmonParams), + "Got %+v\nExpected %+v", got.netmonParams, expected.netmonParams) + assert.True(t, reflect.DeepEqual(expected.storagePath, got.storagePath), + "Got %+v\nExpected %+v", got.storagePath, expected.storagePath) + assert.True(t, reflect.DeepEqual(expected.sharedFile, got.sharedFile), + "Got %+v\nExpected %+v", got.sharedFile, expected.sharedFile) + + _, err = os.Stat(got.storagePath) + assert.Nil(t, err) + + os.RemoveAll(got.storagePath) +} + +func TestNewNetmonErrorWrongFamilyType(t *testing.T) { + // Override netlinkFamily + savedNetlinkFamily := netlinkFamily + netlinkFamily = testWrongNetlinkFamily + defer func() { + netlinkFamily = savedNetlinkFamily + }() + + n, err := newNetmon(netmonParams{}) + assert.NotNil(t, err) + assert.Nil(t, n) +} + +func TestCleanup(t *testing.T) { + skipUnlessRoot(t) + + // Override storageParentPath + savedStorageParentPath := storageParentPath + storageParentPath = testStorageParentPath + defer func() { + storageParentPath = savedStorageParentPath + }() + + handler, err := netlink.NewHandle(netlinkFamily) + assert.Nil(t, err) + + n := &netmon{ + storagePath: filepath.Join(storageParentPath, testSandboxID), + linkDoneCh: make(chan struct{}), + rtDoneCh: make(chan struct{}), + netHandler: handler, + } + + err = os.MkdirAll(n.storagePath, storageDirPerm) + assert.Nil(t, err) + _, err = os.Stat(n.storagePath) + assert.Nil(t, err) + + n.cleanup() + + _, err = os.Stat(n.storagePath) + assert.NotNil(t, err) + _, ok := (<-n.linkDoneCh) + assert.False(t, ok) + _, ok = (<-n.rtDoneCh) + assert.False(t, ok) +} + +func TestLogger(t *testing.T) { + fields := logrus.Fields{ + "name": netmonName, + "pid": os.Getpid(), + "source": "netmon", + "sandbox": testSandboxID, + } + + expected := netmonLog.WithFields(fields) + + n := &netmon{ + netmonParams: netmonParams{ + sandboxID: testSandboxID, + }, + } + + got := n.logger() + assert.True(t, reflect.DeepEqual(*expected, *got), + "Got %+v\nExpected %+v", *got, *expected) +} + +func TestConvertInterface(t *testing.T) { + hwAddr, err := net.ParseMAC(testHwAddr) + assert.Nil(t, err) + + addrs := []netlink.Addr{ + { + IPNet: &net.IPNet{ + IP: net.ParseIP(testIPAddress), + }, + }, + { + IPNet: &net.IPNet{ + IP: net.ParseIP(testIP6Address), + }, + }, + } + + linkAttrs := &netlink.LinkAttrs{ + Name: testIfaceName, + MTU: testMTU, + HardwareAddr: hwAddr, + } + + linkType := "link_type_test" + + expected := vcTypes.Interface{ + Device: testIfaceName, + Name: testIfaceName, + Mtu: uint64(testMTU), + HwAddr: testHwAddr, + IPAddresses: []*vcTypes.IPAddress{ + { + Family: netlink.FAMILY_V4, + Address: testIPAddress, + Mask: "0", + }, + { + Family: netlink.FAMILY_V6, + Address: testIP6Address, + Mask: "0", + }, + }, + LinkType: linkType, + } + + got := convertInterface(linkAttrs, linkType, addrs) + + assert.True(t, reflect.DeepEqual(expected, got), + "Got %+v\nExpected %+v", got, expected) +} + +func TestConvertRoutes(t *testing.T) { + ip, ipNet, err := net.ParseCIDR(testIPAddressWithMask) + assert.Nil(t, err) + assert.NotNil(t, ipNet) + + _, ip6Net, err := net.ParseCIDR(testIP6AddressWithMask) + assert.Nil(t, err) + assert.NotNil(t, ipNet) + + routes := []netlink.Route{ + { + Dst: ipNet, + Src: ip, + Gw: ip, + LinkIndex: -1, + Scope: testScope, + }, + { + Dst: ip6Net, + Src: nil, + Gw: nil, + LinkIndex: -1, + Scope: testScope, + }, + } + + expected := []vcTypes.Route{ + { + Dest: testIPAddressWithMask, + Gateway: testIPAddress, + Source: testIPAddress, + Scope: uint32(testScope), + }, + { + Dest: testIP6AddressWithMask, + Gateway: "", + Source: "", + Scope: uint32(testScope), + }, + } + + got := convertRoutes(routes) + assert.True(t, reflect.DeepEqual(expected, got), + "Got %+v\nExpected %+v", got, expected) +} + +type testTeardownNetwork func() + +func testSetupNetwork(t *testing.T) testTeardownNetwork { + skipUnlessRoot(t) + + // new temporary namespace so we don't pollute the host + // lock thread since the namespace is thread local + runtime.LockOSThread() + var err error + ns, err := netns.New() + if err != nil { + t.Fatal("Failed to create newns", ns) + } + + return func() { + ns.Close() + runtime.UnlockOSThread() + } +} + +func testCreateDummyNetwork(t *testing.T, handler *netlink.Handle) (int, vcTypes.Interface) { + hwAddr, err := net.ParseMAC(testHwAddr) + assert.Nil(t, err) + + link := &netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + MTU: testMTU, + TxQLen: testTxQLen, + Name: testIfaceName, + HardwareAddr: hwAddr, + }, + } + + err = handler.LinkAdd(link) + assert.Nil(t, err) + err = handler.LinkSetUp(link) + assert.Nil(t, err) + + attrs := link.Attrs() + assert.NotNil(t, attrs) + + addrs, err := handler.AddrList(link, netlinkFamily) + assert.Nil(t, err) + + var ipAddrs []*vcTypes.IPAddress + + // Scan addresses for ipv6 link local address which is automatically assigned + for _, addr := range addrs { + if addr.IPNet == nil { + continue + } + + netMask, _ := addr.Mask.Size() + + ipAddr := &vcTypes.IPAddress{ + Address: addr.IP.String(), + Mask: fmt.Sprintf("%d", netMask), + } + + if addr.IP.To4() != nil { + ipAddr.Family = netlink.FAMILY_V4 + } else { + ipAddr.Family = netlink.FAMILY_V6 + } + + ipAddrs = append(ipAddrs, ipAddr) + } + + iface := vcTypes.Interface{ + Device: testIfaceName, + Name: testIfaceName, + Mtu: uint64(testMTU), + HwAddr: testHwAddr, + LinkType: link.Type(), + IPAddresses: ipAddrs, + } + + return attrs.Index, iface +} + +func TestScanNetwork(t *testing.T) { + tearDownNetworkCb := testSetupNetwork(t) + defer tearDownNetworkCb() + + handler, err := netlink.NewHandle(netlinkFamily) + assert.Nil(t, err) + assert.NotNil(t, handler) + defer handler.Delete() + + idx, expected := testCreateDummyNetwork(t, handler) + + n := &netmon{ + netIfaces: make(map[int]vcTypes.Interface), + netHandler: handler, + } + + err = n.scanNetwork() + assert.Nil(t, err) + assert.True(t, reflect.DeepEqual(expected, n.netIfaces[idx]), + "Got %+v\nExpected %+v", n.netIfaces[idx], expected) +} + +func TestStoreDataToSend(t *testing.T) { + var got vcTypes.Interface + + expected := vcTypes.Interface{ + Device: testIfaceName, + Name: testIfaceName, + Mtu: uint64(testMTU), + HwAddr: testHwAddr, + } + + n := &netmon{ + sharedFile: filepath.Join(testStorageParentPath, testSharedFile), + } + + err := os.MkdirAll(testStorageParentPath, storageDirPerm) + defer os.RemoveAll(testStorageParentPath) + assert.Nil(t, err) + + err = n.storeDataToSend(expected) + assert.Nil(t, err) + + // Check the file has been created, check the content, and delete it. + _, err = os.Stat(n.sharedFile) + assert.Nil(t, err) + byteArray, err := ioutil.ReadFile(n.sharedFile) + assert.Nil(t, err) + err = json.Unmarshal(byteArray, &got) + assert.Nil(t, err) + assert.True(t, reflect.DeepEqual(expected, got), + "Got %+v\nExpected %+v", got, expected) +} + +func TestExecKataCmdSuccess(t *testing.T) { + trueBinPath, err := exec.LookPath("true") + assert.Nil(t, err) + assert.NotEmpty(t, trueBinPath) + + params := netmonParams{ + runtimePath: trueBinPath, + } + + n := &netmon{ + netmonParams: params, + sharedFile: filepath.Join(testStorageParentPath, testSharedFile), + } + + err = os.MkdirAll(testStorageParentPath, storageDirPerm) + assert.Nil(t, err) + defer os.RemoveAll(testStorageParentPath) + + file, err := os.Create(n.sharedFile) + assert.Nil(t, err) + assert.NotNil(t, file) + file.Close() + + _, err = os.Stat(n.sharedFile) + assert.Nil(t, err) + + err = n.execKataCmd("") + assert.Nil(t, err) + _, err = os.Stat(n.sharedFile) + assert.NotNil(t, err) +} + +func TestExecKataCmdFailure(t *testing.T) { + falseBinPath, err := exec.LookPath("false") + assert.Nil(t, err) + assert.NotEmpty(t, falseBinPath) + + params := netmonParams{ + runtimePath: falseBinPath, + } + + n := &netmon{ + netmonParams: params, + } + + err = n.execKataCmd("") + assert.NotNil(t, err) +} + +func TestActionsCLI(t *testing.T) { + trueBinPath, err := exec.LookPath("true") + assert.Nil(t, err) + assert.NotEmpty(t, trueBinPath) + + params := netmonParams{ + runtimePath: trueBinPath, + } + + n := &netmon{ + netmonParams: params, + sharedFile: filepath.Join(testStorageParentPath, testSharedFile), + } + + err = os.MkdirAll(testStorageParentPath, storageDirPerm) + assert.Nil(t, err) + defer os.RemoveAll(testStorageParentPath) + + // Test addInterfaceCLI + err = n.addInterfaceCLI(vcTypes.Interface{}) + assert.Nil(t, err) + + // Test delInterfaceCLI + err = n.delInterfaceCLI(vcTypes.Interface{}) + assert.Nil(t, err) + + // Test updateRoutesCLI + err = n.updateRoutesCLI([]vcTypes.Route{}) + assert.Nil(t, err) + + tearDownNetworkCb := testSetupNetwork(t) + defer tearDownNetworkCb() + + handler, err := netlink.NewHandle(netlinkFamily) + assert.Nil(t, err) + assert.NotNil(t, handler) + defer handler.Delete() + + n.netHandler = handler + + // Test updateRoutes + err = n.updateRoutes() + assert.Nil(t, err) + + // Test handleRTMDelRoute + err = n.handleRTMDelRoute(netlink.RouteUpdate{}) + assert.Nil(t, err) +} + +func TestHandleRTMNewAddr(t *testing.T) { + n := &netmon{} + + err := n.handleRTMNewAddr(netlink.LinkUpdate{}) + assert.Nil(t, err) +} + +func TestHandleRTMDelAddr(t *testing.T) { + n := &netmon{} + + err := n.handleRTMDelAddr(netlink.LinkUpdate{}) + assert.Nil(t, err) +} + +func TestHandleRTMNewLink(t *testing.T) { + n := &netmon{} + ev := netlink.LinkUpdate{ + Link: &netlink.Dummy{}, + } + + // LinkAttrs is nil + err := n.handleRTMNewLink(ev) + assert.Nil(t, err) + + // Link name contains "kata" suffix + ev = netlink.LinkUpdate{ + Link: &netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: "foo_kata", + }, + }, + } + err = n.handleRTMNewLink(ev) + assert.Nil(t, err) + + // Interface already exist in list + n.netIfaces = make(map[int]vcTypes.Interface) + n.netIfaces[testIfaceIndex] = vcTypes.Interface{} + ev = netlink.LinkUpdate{ + Link: &netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: "foo0", + }, + }, + } + ev.Index = testIfaceIndex + err = n.handleRTMNewLink(ev) + assert.Nil(t, err) + + // Flags are not up and running + n.netIfaces = make(map[int]vcTypes.Interface) + ev = netlink.LinkUpdate{ + Link: &netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: "foo0", + }, + }, + } + ev.Index = testIfaceIndex + err = n.handleRTMNewLink(ev) + assert.Nil(t, err) + + // Invalid link + n.netIfaces = make(map[int]vcTypes.Interface) + ev = netlink.LinkUpdate{ + Link: &netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: "foo0", + }, + }, + } + ev.Index = testIfaceIndex + ev.Flags = unix.IFF_UP | unix.IFF_RUNNING + handler, err := netlink.NewHandle(netlinkFamily) + assert.Nil(t, err) + assert.NotNil(t, handler) + defer handler.Delete() + n.netHandler = handler + err = n.handleRTMNewLink(ev) + assert.NotNil(t, err) +} + +func TestHandleRTMDelLink(t *testing.T) { + n := &netmon{} + ev := netlink.LinkUpdate{ + Link: &netlink.Dummy{}, + } + + // LinkAttrs is nil + err := n.handleRTMDelLink(ev) + assert.Nil(t, err) + + // Link name contains "kata" suffix + ev = netlink.LinkUpdate{ + Link: &netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: "foo_kata", + }, + }, + } + err = n.handleRTMDelLink(ev) + assert.Nil(t, err) + + // Interface does not exist in list + n.netIfaces = make(map[int]vcTypes.Interface) + ev = netlink.LinkUpdate{ + Link: &netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: "foo0", + }, + }, + } + ev.Index = testIfaceIndex + err = n.handleRTMDelLink(ev) + assert.Nil(t, err) +} + +func TestHandleRTMNewRouteIfaceNotFound(t *testing.T) { + n := &netmon{ + netIfaces: make(map[int]vcTypes.Interface), + } + + err := n.handleRTMNewRoute(netlink.RouteUpdate{}) + assert.Nil(t, err) +} + +func TestHandleLinkEvent(t *testing.T) { + n := &netmon{} + ev := netlink.LinkUpdate{} + + // Unknown event + err := n.handleLinkEvent(ev) + assert.Nil(t, err) + + // DONE event + ev.Header.Type = unix.NLMSG_DONE + err = n.handleLinkEvent(ev) + assert.Nil(t, err) + + // ERROR event + ev.Header.Type = unix.NLMSG_ERROR + err = n.handleLinkEvent(ev) + assert.NotNil(t, err) + + // NEWADDR event + ev.Header.Type = unix.RTM_NEWADDR + err = n.handleLinkEvent(ev) + assert.Nil(t, err) + + // DELADDR event + ev.Header.Type = unix.RTM_DELADDR + err = n.handleLinkEvent(ev) + assert.Nil(t, err) + + // NEWLINK event + ev.Header.Type = unix.RTM_NEWLINK + ev.Link = &netlink.Dummy{} + err = n.handleLinkEvent(ev) + assert.Nil(t, err) + + // DELLINK event + ev.Header.Type = unix.RTM_DELLINK + ev.Link = &netlink.Dummy{} + err = n.handleLinkEvent(ev) + assert.Nil(t, err) +} + +func TestHandleRouteEvent(t *testing.T) { + n := &netmon{} + ev := netlink.RouteUpdate{} + + // Unknown event + err := n.handleRouteEvent(ev) + assert.Nil(t, err) + + // RTM_NEWROUTE event + ev.Type = unix.RTM_NEWROUTE + err = n.handleRouteEvent(ev) + assert.Nil(t, err) + + trueBinPath, err := exec.LookPath("true") + assert.Nil(t, err) + assert.NotEmpty(t, trueBinPath) + + n.runtimePath = trueBinPath + n.sharedFile = filepath.Join(testStorageParentPath, testSharedFile) + + err = os.MkdirAll(testStorageParentPath, storageDirPerm) + assert.Nil(t, err) + defer os.RemoveAll(testStorageParentPath) + + tearDownNetworkCb := testSetupNetwork(t) + defer tearDownNetworkCb() + + handler, err := netlink.NewHandle(netlinkFamily) + assert.Nil(t, err) + assert.NotNil(t, handler) + defer handler.Delete() + + n.netHandler = handler + + // RTM_DELROUTE event + ev.Type = unix.RTM_DELROUTE + err = n.handleRouteEvent(ev) + assert.Nil(t, err) +} diff --git a/src/runtime/pkg/README.md b/src/runtime/pkg/README.md new file mode 100644 index 000000000..b5b0d07d3 --- /dev/null +++ b/src/runtime/pkg/README.md @@ -0,0 +1,10 @@ +# Kata golang packages + +This repository contains a number of packages in addition to the +[`virtcontainers`](../virtcontainers) package: + +| Package name | Description | +|-|-| +| [`katatestutils`](katatestutils) | Unit test utilities. | +| [`katautils`](katautils) | Utilities. | +| [`signals`](signals) | Signal handling functions. | diff --git a/src/runtime/pkg/katatestutils/README.md b/src/runtime/pkg/katatestutils/README.md new file mode 100644 index 000000000..ef1c6f926 --- /dev/null +++ b/src/runtime/pkg/katatestutils/README.md @@ -0,0 +1,178 @@ +# Kata test utilities + +* [Test Constraints](#test-constraints) + * [Usage](#usage) + * [Displaying the `TestConstraint`](#displaying-the-testconstraint) + * [Associating an issue with a constraint](#associating-an-issue-with-a-constraint) + * [Examples](#examples) + * [Skip tests based on user](#skip-tests-based-on-user) + * [Skip tests based on distro](#skip-tests-based-on-distro) + * [Skip tests based on kernel version](#skip-tests-based-on-kernel-version) + * [Full details](#full-details) + +This package provides a small set of test utilities. See the +[GoDoc](https://godoc.org/github.com/kata-containers/runtime/pkg/katatestutils) +for full details. + +## Test Constraints + +This package provides helper functions that accept user-specified constraints +that allow you to skip tests. + +### Usage + +Create a `TestConstraint` object using the `NewTestConstraint()` constructor. +This takes a single boolean parameter that specifies if debug output is generated. + +In each test that has particular test constraints, call the `NotValid()` +method on the `TestConstraint` object, passing one or more constraints that +you want to be valid. + +The `NotValid()` function returns `true` if any of the specified constraints +are not available. This allows for a more natural way to code an arbitrarily +complex test skip as shown in the following example. + +The main object is created in the `init()` function to make it available for +all tests: + +```go + +import ktu "katatestutils" + +var tc ktu.TestConstraint + +func init() { + tc = NewTestConstraint(true) +} + +func TestFoo(t *testing.T) { + + // Specify one or more constraint functions. If not satisfied, the test + // will be skipped. + if tc.NotValid(...) { + t.Skip("skipping test") + } + + // Test code ... +} +``` + +#### Displaying the `TestConstraint` + +Note that you could add the `TestConstraint` object to the `Skip()` call as it +will provide details of why the skip occurred: + +```go +if tc.NotValid(...) { + t.Skipf("skipping test as requirements not met: %v", tc) +} +``` + +#### Associating an issue with a constraint + +You can add a constraint which specifies an issue URL for the skip. No +checking is performed on the issue but if specified, it will be added to the +`TestConstraint` and recorded in error messages and when that object is +displayed: + +```go +if tc.NotValid(WithIssue("https://github.com/kata-containers/runtime/issues/1586"), ...) { + t.Skipf("skipping test as requirements not met: %v", tc) +} +``` + +### Examples + +#### Skip tests based on user + +Use the `NeedRoot()` constraint to skip a test unless running as `root`: + +```go +func TestOnlyRunWhenRoot(t *testing.T) { + + if tc.NotValid(ktu.NeedRoot()) { + t.Skip("skipping test as not running as root user") + } + + // Test code to run as root user ... +} +``` + +Use the `NeedNonRoot()` constraint to skip a test unless running as a +non-`root` user: + +```go +func TestOnlyRunWhenNotRoot(t *testing.T) { + + if tc.NotValid(ktu.NeedNonRoot()) { + t.Skip("skipping test as running as root user") + } + + // Test code to run as non-root user ... +} +``` + +#### Skip tests based on distro + +Use the `NeedDistro()` constraint to skip a test unless running on a +particular Linux distribution: + +```go +func TestOnlyRunOnUbuntu(t *testing.T) { + + if tc.NotValid(ktu.NeedDistro("ubuntu")) { + t.Skip("skipping test as not running on ubuntu") + } + + // Test code to run on Ubuntu only ... +} +``` + +Use the `NeedDistroNotEquals()` constraint to skip a test unless running +on a Linux distribution other than the one specified: + +```go +func TestDontRunOnFedora(t *testing.T) { + + if tc.NotValid(ktu.NeedDistroNotEquals("fedora")) { + t.Skip("skipping test as running on fedora") + } + + // Test code to run on any distro apart from Fedora ... +} +``` + +#### Skip tests based on kernel version + +Use the `NeedKernelVersionGE()` constraint to skip a test unless running on a +system with at least the specified kernel version: + +```go +func TestNewKernelVersion(t *testing.T) { + + if tc.NotValid(ktu.NeedKernelVersionGE("5.0.10")) { + t.Skip("skipping test as kernel is too old") + } + + // Test code to run on specified kernel version (or newer) ... +} +``` + +Use the `NeedKernelVersionLT()` constraint to skip a test unless running on a +system whose kernel is older than the specified kernel version: + +```go +func TestOldKernelVersion(t *testing.T) { + + if tc.NotValid(ktu.NeedKernelVersionLT("4.14.114")) { + t.Skip("skipping test as kernel is too new") + } + + // Test code to run on specified kernel version (or newer) ... +} +``` + +### Full details + +The public API is shown in [`constraints_api.go`](constraints_api.go) or +the [GoDoc](https://godoc.org/github.com/kata-containers/runtime/pkg/katatestutils). diff --git a/src/runtime/pkg/katatestutils/constraints.go b/src/runtime/pkg/katatestutils/constraints.go new file mode 100644 index 000000000..5ac50de1d --- /dev/null +++ b/src/runtime/pkg/katatestutils/constraints.go @@ -0,0 +1,484 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katatestutils + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "strconv" + "strings" + + "github.com/blang/semver" +) + +const ( + TestDisabledNeedRoot = "Test disabled as requires root user" + TestDisabledNeedNonRoot = "Test disabled as requires non-root user" + + // See https://www.freedesktop.org/software/systemd/man/os-release.html + osRelease = "/etc/os-release" + osReleaseAlternative = "/usr/lib/os-release" +) + +var ( + errUnknownDistroName = errors.New("unknown distro name") + errUnknownDistroVersion = errors.New("unknown distro version") + errInvalidOpForConstraint = errors.New("invalid operator for constraint type") +) + +// String converts the operator to a human-readable value. +func (o Operator) String() (s string) { + switch o { + case eqOperator: + s = "==" + case geOperator: + s = ">=" + case gtOperator: + s = ">" + case leOperator: + s = "<=" + case ltOperator: + s = "<" + case neOperator: + s = "!=" + } + + return s +} + +// Result is the outcome of a Constraint test +type Result struct { + // Details of the constraint + // (human-readable result of testing for a Constraint). + Description string + + // true if constraint was valid + Success bool +} + +// GetFileContents return the file contents as a string. +func getFileContents(file string) (string, error) { + bytes, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + + return string(bytes), nil +} + +func getKernelVersion() (string, error) { + const procVersion = "/proc/version" + + contents, err := getFileContents(procVersion) + if err != nil { + return "", err + } + + fields := strings.Fields(contents) + l := len(fields) + if l < 3 { + return "", fmt.Errorf("unexpected contents in %v", procVersion) + } + + return fixKernelVersion(fields[2]), nil +} + +// getDistroDetails returns the distributions name and version string. +// If it is not possible to determine both values an error is +// returned. +func getDistroDetails() (name, version string, err error) { + files := []string{osRelease, osReleaseAlternative} + name = "" + version = "" + + for _, file := range files { + contents, err := getFileContents(file) + if err != nil { + if os.IsNotExist(err) { + continue + } + + return "", "", err + } + + lines := strings.Split(contents, "\n") + + for _, line := range lines { + if strings.HasPrefix(line, "ID=") && name == "" { + fields := strings.Split(line, "=") + name = strings.Trim(fields[1], `"`) + name = strings.ToLower(name) + } else if strings.HasPrefix(line, "VERSION_ID=") && version == "" { + fields := strings.Split(line, "=") + version = strings.Trim(fields[1], `"`) + version = strings.ToLower(version) + } + } + + if name != "" && version != "" { + return name, version, nil + } + } + + if name == "" { + return "", "", errUnknownDistroName + } + + if version == "" { + return "", "", errUnknownDistroVersion + } + + return name, version, nil +} + +// fixKernelVersion replaces underscores with dashes in a version string. +// This change is primarily for Fedora, RHEL and CentOS version numbers which +// can contain underscores. By replacing them with dashes, a valid semantic +// version string is created. +// +// Examples of actual kernel versions which can be made into valid semver +// format by calling this function: +// +// centos: 3.10.0-957.12.1.el7.x86_64 +// fedora: 5.0.9-200.fc29.x86_64 +// +func fixKernelVersion(version string) string { + return strings.Replace(version, "_", "-", -1) +} + +// handleDistroName checks that the current distro is compatible with +// the constraint specified by the arguments. +func (tc *TestConstraint) handleDistroName(name string, op Operator) (result Result, err error) { + if name == "" { + return Result{}, fmt.Errorf("distro name cannot be blank") + } + + name = strings.ToLower(name) + + var success bool + + switch op { + case eqOperator: + success = name == tc.DistroName + case neOperator: + success = name != tc.DistroName + default: + return Result{}, errInvalidOpForConstraint + } + + descr := fmt.Sprintf("need distro %s %q, got distro %q", op, name, tc.DistroName) + + result = Result{ + Description: descr, + Success: success, + } + + return result, nil +} + +// handleDistroVersion checks that the current distro version is compatible with +// the constraint specified by the arguments. +func (tc *TestConstraint) handleDistroVersion(version string, op Operator) (result Result, err error) { + return handleVersionType("distro", tc.DistroVersion, op, version) +} + +// handleKernelVersion checks that the current kernel version is compatible with +// the constraint specified by the arguments. +func (tc *TestConstraint) handleKernelVersion(version string, op Operator) (result Result, err error) { + return handleVersionType("kernel", tc.KernelVersion, op, version) +} + +// handleVersionType checks that the current and new versions are compatible with +// the constraint specified by the arguments. The versionName argument is a +// human-readable value to represent the currentVersion. +func handleVersionType(versionName, newVersion string, op Operator, currentVersion string) (result Result, err error) { + if versionName == "" { + return Result{}, fmt.Errorf("version name cannot be blank") + } + + if newVersion == "" { + return Result{}, fmt.Errorf("new version cannot be blank") + } + + if currentVersion == "" { + return Result{}, fmt.Errorf("current version cannot be blank") + } + + newVersion = strings.ToLower(newVersion) + currentVersion = strings.ToLower(currentVersion) + + newVersionElements := len(strings.Split(newVersion, ".")) + currentVersionElements := len(strings.Split(currentVersion, ".")) + + var success bool + + // Determine the type of version string based on the current version + switch currentVersionElements { + case 1: + // A simple integer version number. + if newVersionElements != 1 { + return Result{}, fmt.Errorf("%s version type (%q) is integer, but specified version (%s) is not", + versionName, currentVersion, newVersion) + } + + success, err = evalIntVersion(newVersion, op, currentVersion) + case 2: + // A "floating point" version number in format "a.b". + if newVersionElements > 2 { + return Result{}, fmt.Errorf("%s version type (%q) is float, but specified version (%s) is not float or int", + versionName, currentVersion, newVersion) + } + + success, err = evalFloatVersion(newVersion, op, currentVersion) + default: + // Assumed to be a semver format version string + // in format "a.b.c." + // + // Cannot check specified version here as semver is more + // complex - let the eval function detail with it. + + success, err = evalSemverVersion(newVersion, op, currentVersion) + } + + if err != nil { + return Result{}, err + } + + descr := fmt.Sprintf("need %s version %s %q, got version %q", + versionName, op, currentVersion, newVersion) + + result = Result{ + Description: descr, + Success: success, + } + + return result, nil +} + +// evalIntVersion deals with integer version numbers +// (in format "a"). +func evalIntVersion(newVersionStr string, op Operator, currentVersionStr string) (success bool, err error) { + newVersion, err := strconv.Atoi(newVersionStr) + if err != nil { + return false, err + } + + currentVersion, err := strconv.Atoi(currentVersionStr) + if err != nil { + return false, err + } + + switch op { + case eqOperator: + success = newVersion == currentVersion + case geOperator: + success = newVersion >= currentVersion + case gtOperator: + success = newVersion > currentVersion + case leOperator: + success = newVersion <= currentVersion + case ltOperator: + success = newVersion < currentVersion + case neOperator: + success = newVersion != currentVersion + default: + return false, errInvalidOpForConstraint + } + + return success, err +} + +// evalFloatVersion deals with "floating point" version numbers +// (in format "a.b"). +// +// Note that (implicitly) the specified version number provided by the user +// may in fact be an integer value which will be converted into a float. +func evalFloatVersion(newVersionStr string, op Operator, currentVersionStr string) (success bool, err error) { + // If this many bits is insufficient to represent a version number, we + // have problems...! + const bitSize = 32 + + newVersion, err := strconv.ParseFloat(newVersionStr, bitSize) + if err != nil { + return false, err + } + + currentVersion, err := strconv.ParseFloat(currentVersionStr, bitSize) + if err != nil { + return false, err + } + + switch op { + case eqOperator: + success = newVersion == currentVersion + case geOperator: + success = newVersion >= currentVersion + case gtOperator: + success = newVersion > currentVersion + case leOperator: + success = newVersion <= currentVersion + case ltOperator: + success = newVersion < currentVersion + case neOperator: + success = newVersion != currentVersion + default: + return false, errInvalidOpForConstraint + } + + return success, err +} + +// evalSemverVersion deals with semantic versioning format version strings +// (in version "a.b.c"). +// +// See: https://semver.org +func evalSemverVersion(newVersionStr string, op Operator, currentVersionStr string) (success bool, err error) { + newVersion, err := semver.Make(newVersionStr) + if err != nil { + return false, err + } + + currentVersion, err := semver.Make(currentVersionStr) + if err != nil { + return false, err + } + + switch op { + case eqOperator: + success = newVersion.EQ(currentVersion) + case geOperator: + success = newVersion.GE(currentVersion) + case gtOperator: + success = newVersion.GT(currentVersion) + case leOperator: + success = newVersion.LE(currentVersion) + case ltOperator: + success = newVersion.LT(currentVersion) + case neOperator: + success = newVersion.NE(currentVersion) + default: + return false, errInvalidOpForConstraint + } + + return success, err +} + +// handleUID checks that the current UID is compatible with the constraint +// specified by the arguments. +func (tc *TestConstraint) handleUID(uid int, op Operator) (result Result, err error) { + if uid < 0 { + return Result{}, fmt.Errorf("uid must be >= 0, got %d", uid) + } + + var success bool + + switch op { + case eqOperator: + success = tc.ActualEUID == uid + case neOperator: + success = tc.ActualEUID != uid + default: + return Result{}, errInvalidOpForConstraint + } + + descr := fmt.Sprintf("need uid %s %d, got euid %d", op, uid, tc.ActualEUID) + + result = Result{ + Description: descr, + Success: success, + } + + return result, nil +} + +// handleResults is the common handler for all constraint types. It deals with +// errors found trying to check constraints, stores results and displays +// details of valid constraints. +func (tc *TestConstraint) handleResults(result Result, err error) { + if err != nil { + var extra string + + if tc.Issue != "" { + extra = fmt.Sprintf(" (issue %s)", tc.Issue) + } + + // Display the TestConstraint object as it's may provide + // helpful information for the caller. + panic(fmt.Sprintf("%+v: failed to check test constraints: error: %s%s\n", + tc, err, extra)) + } + + if !result.Success { + tc.Failed = append(tc.Failed, result) + } else { + tc.Passed = append(tc.Passed, result) + } + + if tc.Debug { + var outcome string + + if result.Success { + outcome = "valid" + } else { + outcome = "invalid" + } + + fmt.Printf("Constraint %s: %s\n", outcome, result.Description) + } +} + +// constraintValid handles the specified constraint, returning true if the +// constraint is valid, else false. +func (tc *TestConstraint) constraintValid(fn Constraint) bool { + c := Constraints{} + + // Call the constraint function that sets the Constraints values + fn(&c) + + if c.Issue != "" { + // Just record it + tc.Issue = c.Issue + } + + if c.UIDSet { + result, err := tc.handleUID(c.UID, c.Operator) + tc.handleResults(result, err) + if !result.Success { + return false + } + } + + if c.DistroName != "" { + result, err := tc.handleDistroName(c.DistroName, c.Operator) + tc.handleResults(result, err) + if !result.Success { + return false + } + } + + if c.DistroVersion != "" { + result, err := tc.handleDistroVersion(c.DistroVersion, c.Operator) + tc.handleResults(result, err) + if !result.Success { + return false + } + + } + + if c.KernelVersion != "" { + result, err := tc.handleKernelVersion(c.KernelVersion, c.Operator) + tc.handleResults(result, err) + if !result.Success { + return false + } + + } + + // Constraint is valid + return true +} diff --git a/src/runtime/pkg/katatestutils/constraints_api.go b/src/runtime/pkg/katatestutils/constraints_api.go new file mode 100644 index 000000000..ce4af56f2 --- /dev/null +++ b/src/runtime/pkg/katatestutils/constraints_api.go @@ -0,0 +1,309 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// This file contains the public API for the test constraints facility. + +package katatestutils + +import ( + "os" + "strings" +) + +// Operator represents an operator to apply to a test constraint value. +type Operator int + +const ( + eqOperator Operator = iota + geOperator Operator = iota + gtOperator Operator = iota + leOperator Operator = iota + ltOperator Operator = iota + neOperator Operator = iota +) + +// Constraints encapsulates all information about a test constraint. +type Constraints struct { + Issue string + + UID int + + // Not ideal: set when UID needs to be checked. This allows + // a test for UID 0 to be detected. + UIDSet bool + + // DistroName is the name of a distro in all lower-case letters. + DistroName string + + // DistroVersion is the version of the particular distro in string + // format. It may contain periods and dashes. + DistroVersion string + + // KernelVersion is the version of a particular kernel. + KernelVersion string + + // Operator is the operator to apply to one of the constraints. + Operator Operator +} + +// Constraint is a function that operates on a Constraints object to set +// particular values. +type Constraint func(c *Constraints) + +// TestConstraint records details about test constraints. +type TestConstraint struct { + Debug bool + + // Effective user ID of running test + ActualEUID int + + DistroName string + DistroVersion string + KernelVersion string + + // Used to record all passed and failed constraints in + // human-readable form. + Passed []Result + Failed []Result + + // Optionally used to record an issue number that relates to the + // constraint. + Issue string +} + +// NewKataTest creates a new TestConstraint object and is the main interface +// to the test constraints feature. +func NewTestConstraint(debug bool) TestConstraint { + distroName, distroVersion, err := getDistroDetails() + if err != nil { + panic(err) + } + + kernelVersion, err := getKernelVersion() + if err != nil { + panic(err) + } + + return TestConstraint{ + Debug: debug, + + ActualEUID: os.Geteuid(), + DistroName: distroName, + DistroVersion: distroVersion, + KernelVersion: kernelVersion, + } +} + +// NotValid checks if the specified list of constraints are all valid, +// returning true if any _fail_. +// +// Notes: +// +// - Constraints are applied in the order specified. +// - A constraint type (user, distro) can only be specified once. +// - If the function fails to determine whether it can check the constraints, +// it will panic. Since this is facility is used for testing, this seems like +// the best approach as it unburdens the caller from checking for an error +// (which should never be ignored). +func (tc *TestConstraint) NotValid(constraints ...Constraint) bool { + if len(constraints) == 0 { + panic("need atleast one constraint") + } + + // Reset in case of a previous call + tc.Passed = nil + tc.Failed = nil + tc.Issue = "" + + for _, c := range constraints { + valid := tc.constraintValid(c) + if !valid { + return true + } + } + + return false +} + +// NeedUID skips the test unless running as a user with the specified user ID. +func NeedUID(uid int, op Operator) Constraint { + return func(c *Constraints) { + c.Operator = op + c.UID = uid + c.UIDSet = true + } +} + +// NeedNonRoot skips the test unless running as root. +func NeedRoot() Constraint { + return NeedUID(0, eqOperator) +} + +// NeedNonRoot skips the test if running as the root user. +func NeedNonRoot() Constraint { + return NeedUID(0, neOperator) +} + +// NeedDistroWithOp skips the test unless the distro constraint specified by +// the arguments is true. +func NeedDistroWithOp(distro string, op Operator) Constraint { + return func(c *Constraints) { + c.DistroName = strings.ToLower(distro) + c.Operator = op + } +} + +// NeedDistroEquals will skip the test unless running on the specified distro. +func NeedDistroEquals(distro string) Constraint { + return NeedDistroWithOp(distro, eqOperator) +} + +// NeedDistroNotEquals will skip the test unless run a distro that does not +// match the specified name. +func NeedDistroNotEquals(distro string) Constraint { + return NeedDistroWithOp(distro, neOperator) +} + +// NeedDistro will skip the test unless running on the specified distro. +func NeedDistro(distro string) Constraint { + return NeedDistroEquals(distro) +} + +// NeedDistroVersionWithOp skips the test unless the distro version constraint +// specified by the arguments is true. +// +// Note: distro versions vary in format. +func NeedDistroVersionWithOp(version string, op Operator) Constraint { + return func(c *Constraints) { + c.DistroVersion = version + c.Operator = op + } +} + +// NeedDistroVersionEquals will skip the test unless the distro version is the +// same as the specified version. +// +// Note: distro versions vary in format. +func NeedDistroVersionEquals(version string) Constraint { + return NeedDistroVersionWithOp(version, eqOperator) +} + +// NeedDistroVersionNotEquals will skip the test unless the distro version is +// different to the specified version. +// +// Note: distro versions vary in format. +func NeedDistroVersionNotEquals(version string) Constraint { + return NeedDistroVersionWithOp(version, neOperator) +} + +// NeedDistroVersionLE will skip the test unless the distro version is older +// than or the same as the specified version. +// +// Note: distro versions vary in format. +func NeedDistroVersionLE(version string) Constraint { + return NeedDistroVersionWithOp(version, leOperator) +} + +// NeedDistroVersionLT will skip the test unless the distro version is older +// than the specified version. +// +// Note: distro versions vary in format. +func NeedDistroVersionLT(version string) Constraint { + return NeedDistroVersionWithOp(version, ltOperator) +} + +// NeedDistroVersionGE will skip the test unless the distro version is newer +// than or the same as the specified version. +// +// Note: distro versions vary in format. +func NeedDistroVersionGE(version string) Constraint { + return NeedDistroVersionWithOp(version, geOperator) +} + +// NeedDistroVersionGT will skip the test unless the distro version is newer +// than the specified version. +// +// Note: distro versions vary in format. +func NeedDistroVersionGT(version string) Constraint { + return NeedDistroVersionWithOp(version, gtOperator) +} + +// NeedDistroVersion will skip the test unless running on the specified +// (exact) version of some distro. +// +// Note: distro versions vary in format. +func NeedDistroVersion(version string) Constraint { + return NeedDistroVersionEquals(version) +} + +// NeedKernelVersionWithOp skips the test unless the distro version constraint +// specified by the arguments is true. +func NeedKernelVersionWithOp(version string, op Operator) Constraint { + return func(c *Constraints) { + c.KernelVersion = version + c.Operator = op + } +} + +// NeedKernelVersionLT will skip the test unless the distro version is older +// than the specified version. +func NeedKernelVersionEquals(version string) Constraint { + return NeedKernelVersionWithOp(version, eqOperator) +} + +// NeedKernelVersionNotEquals will skip the test unless the distro version is +// different to the specified version. +func NeedKernelVersionNotEquals(version string) Constraint { + return NeedKernelVersionWithOp(version, neOperator) +} + +// NeedKernelVersionLT will skip the test unless the distro version is older +// than the specified version. +// +// Note: distro versions vary in format. +func NeedKernelVersionLT(version string) Constraint { + return NeedKernelVersionWithOp(version, ltOperator) +} + +// NeedKernelVersionLE will skip the test unless the distro version is older +// than or the same as the specified version. +// +// Note: distro versions vary in format. +func NeedKernelVersionLE(version string) Constraint { + return NeedKernelVersionWithOp(version, leOperator) +} + +// NeedKernelVersionGT will skip the test unless the distro version is newer +// than the specified version. +// +// Note: distro versions vary in format. +func NeedKernelVersionGT(version string) Constraint { + return NeedKernelVersionWithOp(version, gtOperator) +} + +// NeedKernelVersionGE will skip the test unless the distro version is newer +// than or the same as the specified version. +// +// Note: distro versions vary in format. +func NeedKernelVersionGE(version string) Constraint { + return NeedKernelVersionWithOp(version, geOperator) +} + +// NeedKernelVersion will skip the test unless running on the specified +// (exact) version of some distro. +// +// Note: distro versions vary in format. +func NeedKernelVersion(version string) Constraint { + return NeedKernelVersionEquals(version) +} + +// WithIssue allows the specification of an issue URL. +// +// Note that the issue is not checked for validity. +func WithIssue(issue string) Constraint { + return func(c *Constraints) { + c.Issue = issue + } +} diff --git a/src/runtime/pkg/katatestutils/constraints_test.go b/src/runtime/pkg/katatestutils/constraints_test.go new file mode 100644 index 000000000..16bc63cf4 --- /dev/null +++ b/src/runtime/pkg/katatestutils/constraints_test.go @@ -0,0 +1,1832 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katatestutils + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "testing" + + "github.com/blang/semver" + "github.com/stretchr/testify/assert" +) + +const ( + testFileMode = os.FileMode(0640) + invalidOperator = 1234 + + skipUnknownDistroName = "skipping test as cannot determine distro name" +) + +type testDataUID struct { + uid int + op Operator + c Constraints +} + +type testDataDistro struct { + distro string + op Operator + c Constraints +} + +var distros = []string{ + "centos", + "clear-linux-os", + "debian", + "fedora", + "opensuse", + "rhel", + "sles", + "ubuntu", +} + +var thisUID = os.Getuid() +var rootUID = 0 + +// name and version of current distro and kernel version of system tests are +// running on +var distroName string +var distroVersion string +var kernelVersion string + +// error saved when attempting to determine distro name+version and kernel +// version. +var getDistroErr error +var getKernelErr error + +// true if running as root +var root = thisUID == rootUID + +var uidEqualsRootData = testDataUID{ + uid: rootUID, + op: eqOperator, + c: Constraints{ + Operator: eqOperator, + UID: rootUID, + UIDSet: true, + }, +} + +var uidNotEqualsRootData = testDataUID{ + uid: rootUID, + op: neOperator, + c: Constraints{ + Operator: neOperator, + UID: rootUID, + UIDSet: true, + }, +} + +var distroEqualsCurrentData testDataDistro +var distroNotEqualsCurrentData testDataDistro + +func init() { + distroName, distroVersion, getDistroErr = testGetDistro() + kernelVersion, getKernelErr = testGetKernelVersion() + + distroEqualsCurrentData = testDataDistro{ + distro: distroName, + op: eqOperator, + c: Constraints{ + DistroName: distroName, + Operator: eqOperator, + }, + } + + distroNotEqualsCurrentData = testDataDistro{ + distro: distroName, + op: neOperator, + c: Constraints{ + DistroName: distroName, + Operator: neOperator, + }, + } +} + +func fileExists(path string) bool { + if _, err := os.Stat(path); os.IsNotExist(err) { + return false + } + + return true +} + +// getAnotherDistro returns a distro name not equal to the one specified. +func getAnotherDistro(distro string) string { + for _, d := range distros { + if d != distro { + return d + } + } + + panic(fmt.Sprintf("failed to find a distro different to %s", distro)) +} + +func checkUIDConstraints(assert *assert.Assertions, a, b Constraints, desc string) { + msg := fmt.Sprintf("%s: a: %+v, b: %+v", desc, a, b) + + assert.Equal(a.UID, b.UID, msg) + assert.Equal(a.Operator, b.Operator, msg) + assert.Equal(a.UIDSet, b.UIDSet, msg) +} + +func checkDistroConstraints(assert *assert.Assertions, a, b Constraints, desc string) { + msg := fmt.Sprintf("%s: a: %+v, b: %+v", desc, a, b) + + assert.Equal(a.DistroName, b.DistroName, msg) + assert.Equal(a.Operator, b.Operator, msg) +} + +func checkKernelConstraint(assert *assert.Assertions, f Constraint, version string, op Operator, msg string) { + c := Constraints{} + + f(&c) + + assert.Equal(c.KernelVersion, version, msg) + assert.Equal(c.Operator, op, msg) +} + +// runCommand runs a command and returns its output +func runCommand(args ...string) ([]string, error) { + cmd := exec.Command(args[0], args[1:]...) + bytes, err := cmd.Output() + if err != nil { + return []string{}, err + } + + output := strings.Split(string(bytes), "\n") + + return output, nil +} + +// semverBumpVersion takes an existing semantic version and increments one or +// more parts of it, returning the new version number as a string. +func semverBumpVersion(ver semver.Version, bumpMajor, bumpMinor, bumpPatch bool) (string, error) { + if bumpMajor { + err := ver.IncrementMajor() + if err != nil { + return "", err + } + } + + if bumpMinor { + err := ver.IncrementMinor() + if err != nil { + return "", err + } + } + + if bumpPatch { + err := ver.IncrementPatch() + if err != nil { + return "", err + } + } + + return ver.String(), nil +} + +// changeVersion modifies the specified version and returns the +// string representation. If decrement is true the returned version is smaller +// than the specified version, else it is larger. +func changeVersion(version string, decrement bool) (string, error) { + operand := int64(1) + + if decrement { + operand = -1 + } + + // Is it an integer? + intResult, err := strconv.ParseUint(version, 10, 0) + if err == nil { + if intResult == 0 && decrement { + return "", fmt.Errorf("cannot decrement integer version with value zero") + } + + return fmt.Sprintf("%d", uint64(int64(intResult)+operand)), nil + } + + // Is it a float? + floatResult, err := strconv.ParseFloat(version, 32) + if err == nil { + if int(floatResult) == 0 && decrement { + return "", fmt.Errorf("cannot decrement integer part of floating point version with value zero: %v", version) + } + + return fmt.Sprintf("%f", floatResult+float64(operand)), nil + } + + // Not an int nor a float, so it must be a semantic version + ver, err := semver.Make(version) + if err != nil { + // but if not, bail as we've run out of options + return "", err + } + + if decrement { + // the semver package only provides increment operations, so + // handle decrement ourselves. + major := ver.Major + + if major == 0 { + return "", fmt.Errorf("cannot decrement semver with zero major version: %+v", version) + } + + major-- + + ver.Major = major + } else { + err = ver.IncrementMajor() + if err != nil { + return "", err + } + } + + return ver.String(), nil +} + +func incrementVersion(version string) (string, error) { + return changeVersion(version, false) +} + +func decrementVersion(version string) (string, error) { + return changeVersion(version, true) +} + +// testGetDistro is an alternative implementation of getDistroDetails() used +// for testing. +func testGetDistro() (name, version string, err error) { + files := []string{"/etc/os-release", "/usr/lib/os-release"} + + for _, file := range files { + if !fileExists(file) { + continue + } + + output, err := runCommand("grep", "^ID=", file) + if err != nil { + return "", "", err + } + + line := output[0] + fields := strings.Split(line, "=") + if name == "" { + name = strings.Trim(fields[1], `"`) + name = strings.ToLower(name) + } + + output, err = runCommand("grep", "^VERSION_ID=", file) + if err != nil { + return "", "", err + } + + line = output[0] + fields = strings.Split(line, "=") + if version == "" { + version = strings.Trim(fields[1], `"`) + version = strings.ToLower(version) + } + } + + if name != "" && version != "" { + return name, version, nil + } + + if name == "" { + return "", "", errUnknownDistroName + } + + if version == "" { + return "", "", errUnknownDistroVersion + } + + return "", "", errors.New("BUG: something bad happened") +} + +func testGetKernelVersion() (version string, err error) { + const file = "/proc/version" + + bytes, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + + line := string(bytes) + fields := strings.Fields(line) + + const minFields = 3 + + count := len(fields) + + if count < minFields { + return "", fmt.Errorf("expected atleast %d fields in file %q, got %d", + minFields, file, count) + } + + version = fixKernelVersion(fields[2]) + + return version, nil +} + +func TestOperatorString(t *testing.T) { + assert := assert.New(t) + + type testData struct { + op Operator + value string + } + + data := []testData{ + {eqOperator, "=="}, + {neOperator, "!="}, + } + + for i, d := range data { + value := d.op.String() + + assert.Equal(value, d.value, "test[%d]: %+v", i, d) + } +} + +func TestNewTestConstraint(t *testing.T) { + if getDistroErr != nil { + t.Skipf("skipping as unable to determine distro name/version: %v", + getDistroErr) + } + + if getKernelErr != nil { + t.Skipf("skipping as unable to determine kernel version: %v", + getKernelErr) + } + + assert := assert.New(t) + + for i, debug := range []bool{true, false} { + c := NewTestConstraint(debug) + + msg := fmt.Sprintf("test[%d]: debug: %v, constraint: %+v", i, debug, c) + + assert.Equal(debug, c.Debug, msg) + + assert.Equal(distroName, c.DistroName, msg) + assert.Equal(distroVersion, c.DistroVersion, msg) + assert.Equal(kernelVersion, c.KernelVersion, msg) + assert.Equal(thisUID, c.ActualEUID) + + toCheck := []string{ + distroName, + distroVersion, + kernelVersion, + c.DistroName, + c.DistroVersion, + c.KernelVersion, + } + + for _, str := range toCheck { + assert.NotNil(str, msg) + } + } +} + +func TestGetFileContents(t *testing.T) { + assert := assert.New(t) + + type testData struct { + contents string + } + + data := []testData{ + {""}, + {" "}, + {"\n"}, + {"\n\n"}, + {"\n\n\n"}, + {"foo"}, + {"foo\nbar"}, + } + + dir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(dir) + + file := filepath.Join(dir, "foo") + + // file doesn't exist + _, err = getFileContents(file) + assert.Error(err) + + for _, d := range data { + // create the file + err = ioutil.WriteFile(file, []byte(d.contents), testFileMode) + assert.NoError(err) + defer os.Remove(file) + + contents, err := getFileContents(file) + assert.NoError(err) + assert.Equal(contents, d.contents) + } +} + +func TestGetDistroDetails(t *testing.T) { + assert := assert.New(t) + + if getDistroErr == errUnknownDistroName { + t.Skip(skipUnknownDistroName) + } + + assert.NoError(getDistroErr) + assert.NotNil(distroName) + assert.NotNil(distroVersion) + + name, version, err := getDistroDetails() + assert.NoError(err) + assert.NotNil(name) + assert.NotNil(version) + + assert.Equal(name, distroName) + assert.Equal(version, distroVersion) +} + +func TestGetKernelVersion(t *testing.T) { + assert := assert.New(t) + + assert.NoError(getKernelErr) + assert.NotNil(kernelVersion) + + version, err := getKernelVersion() + assert.NoError(err) + assert.NotNil(version) + + assert.Equal(version, kernelVersion) +} + +func TestConstraintHandleDistroName(t *testing.T) { + assert := assert.New(t) + + type testData struct { + distro string + op Operator + result Result + expectError bool + } + + distroName, _, err := testGetDistro() + if err != nil && err == errUnknownDistroName { + t.Skip(skipUnknownDistroName) + } + + // Look for the first distro that is not the same as the distro this + // test is currently running on. + differentDistro := getAnotherDistro(distroName) + + data := []testData{ + {"", eqOperator, Result{}, true}, + {"", neOperator, Result{}, true}, + {"", invalidOperator, Result{}, true}, + {distroName, invalidOperator, Result{}, true}, + {distroName, invalidOperator, Result{}, true}, + + { + distroName, + eqOperator, + Result{ + Description: distroName, + Success: true, + }, + false, + }, + { + distroName, + neOperator, + Result{ + Description: distroName, + Success: false, + }, + false, + }, + { + differentDistro, + eqOperator, + Result{ + Description: differentDistro, + Success: false, + }, + false, + }, + + { + differentDistro, + neOperator, + Result{ + Description: differentDistro, + Success: true, + }, + false, + }, + } + + for _, debug := range []bool{true, false} { + tc := NewTestConstraint(debug) + + for i, d := range data { + result, err := tc.handleDistroName(d.distro, d.op) + + msg := fmt.Sprintf("test[%d]: %+v, result: %+v", i, d, result) + + if d.expectError { + assert.Error(err, msg) + continue + + } + + assert.NoError(err, msg) + assert.Equal(result.Success, d.result.Success, msg) + assert.NotNil(result.Description, msg) + } + } +} + +func TestConstraintHandleDistroVersion(t *testing.T) { + assert := assert.New(t) + + assert.NotNil(distroVersion) + + // Generate a new distro version for testing purposes. Since we don't + // know the format of this particular distros versioning scheme, we + // need to calculate it. + higherVersion, err := incrementVersion(distroVersion) + assert.NoError(err) + assert.NotEqual(distroVersion, higherVersion) + + type testData struct { + version string + op Operator + result Result + expectError bool + } + + data := []testData{ + {"", eqOperator, Result{}, true}, + {"", geOperator, Result{}, true}, + {"", gtOperator, Result{}, true}, + {"", leOperator, Result{}, true}, + {"", ltOperator, Result{}, true}, + {"", neOperator, Result{}, true}, + + {distroVersion, eqOperator, Result{Success: true}, false}, + {higherVersion, eqOperator, Result{Success: false}, false}, + + {distroVersion, gtOperator, Result{Success: false}, false}, + {higherVersion, gtOperator, Result{Success: false}, false}, + + {distroVersion, geOperator, Result{Success: true}, false}, + {higherVersion, geOperator, Result{Success: false}, false}, + + {distroVersion, ltOperator, Result{Success: false}, false}, + {higherVersion, ltOperator, Result{Success: true}, false}, + + {distroVersion, leOperator, Result{Success: true}, false}, + {higherVersion, leOperator, Result{Success: true}, false}, + + {distroVersion, neOperator, Result{Success: false}, false}, + {higherVersion, neOperator, Result{Success: true}, false}, + } + + for _, debug := range []bool{true, false} { + tc := NewTestConstraint(debug) + + for i, d := range data { + result, err := tc.handleDistroVersion(d.version, d.op) + + msg := fmt.Sprintf("test[%d]: %+v, result: %+v", i, d, result) + + if d.expectError { + assert.Error(err, msg) + continue + } + + assert.Equal(d.result.Success, result.Success, msg) + } + } +} + +func TestConstraintHandleVersionType(t *testing.T) { + assert := assert.New(t) + + type testData struct { + versionName string + currentVersion string + op Operator + newVersion string + result Result + expectError bool + } + + data := []testData{ + //---------- + + {"", "", eqOperator, "", Result{}, true}, + + {"name", "foo", eqOperator, "", Result{}, true}, + {"name", "", eqOperator, "foo", Result{}, true}, + {"name", "1", eqOperator, "", Result{}, true}, + {"name", "", eqOperator, "1", Result{}, true}, + + {"name", "1", eqOperator, "1", Result{Success: true}, false}, + {"name", "1", eqOperator, "2", Result{Success: false}, false}, + {"name", "2", eqOperator, "1", Result{Success: false}, false}, + + {"name", "3.141", eqOperator, "3.141", Result{Success: true}, false}, + {"name", "4.141", eqOperator, "3.141", Result{Success: false}, false}, + {"name", "3.141", eqOperator, "4.141", Result{Success: false}, false}, + + {"name", "3.1.4-1", eqOperator, "3.1.4-1", Result{Success: true}, false}, + {"name", "3.1.4-1", eqOperator, "4.1.4-1", Result{Success: false}, false}, + {"name", "4.1.4-1", eqOperator, "3.1.4-1", Result{Success: false}, false}, + + //---------- + + {"", "", ltOperator, "", Result{}, true}, + + {"name", "foo", ltOperator, "", Result{}, true}, + {"name", "", ltOperator, "foo", Result{}, true}, + {"name", "1", ltOperator, "", Result{}, true}, + {"name", "", ltOperator, "1", Result{}, true}, + + {"name", "1", ltOperator, "2", Result{Success: true}, false}, + {"name", "2", ltOperator, "1", Result{Success: false}, false}, + {"name", "1", ltOperator, "1", Result{Success: false}, false}, + + {"name", "1.3", ltOperator, "2.3", Result{Success: true}, false}, + {"name", "2.3", ltOperator, "1.3", Result{Success: false}, false}, + {"name", "1.3", ltOperator, "1.3", Result{Success: false}, false}, + + {"name", "3.1.4", ltOperator, "3.1.5", Result{Success: true}, false}, + {"name", "3.1.5", ltOperator, "3.1.4", Result{Success: false}, false}, + {"name", "3.1.4", ltOperator, "3.1.4", Result{Success: false}, false}, + + //---------- + + {"", "", leOperator, "", Result{}, true}, + + {"name", "foo", leOperator, "", Result{}, true}, + {"name", "", leOperator, "foo", Result{}, true}, + {"name", "1", leOperator, "", Result{}, true}, + {"name", "", leOperator, "1", Result{}, true}, + + {"name", "1", leOperator, "2", Result{Success: true}, false}, + {"name", "2", leOperator, "1", Result{Success: false}, false}, + {"name", "1", leOperator, "1", Result{Success: true}, false}, + + {"name", "1.3", leOperator, "2.3", Result{Success: true}, false}, + {"name", "2.3", leOperator, "1.3", Result{Success: false}, false}, + {"name", "1.3", leOperator, "1.3", Result{Success: true}, false}, + + {"name", "3.1.4", leOperator, "3.1.5", Result{Success: true}, false}, + {"name", "3.1.5", leOperator, "3.1.4", Result{Success: false}, false}, + {"name", "3.1.4", leOperator, "3.1.4", Result{Success: true}, false}, + + //---------- + + {"", "", gtOperator, "", Result{}, true}, + + {"name", "foo", gtOperator, "", Result{}, true}, + {"name", "", gtOperator, "foo", Result{}, true}, + {"name", "1", gtOperator, "", Result{}, true}, + {"name", "", gtOperator, "1", Result{}, true}, + + {"name", "1", gtOperator, "2", Result{Success: false}, false}, + {"name", "2", gtOperator, "1", Result{Success: true}, false}, + {"name", "1", gtOperator, "1", Result{Success: false}, false}, + + {"name", "1.3", gtOperator, "2.3", Result{Success: false}, false}, + {"name", "2.3", gtOperator, "1.3", Result{Success: true}, false}, + {"name", "1.3", gtOperator, "1.3", Result{Success: false}, false}, + + {"name", "3.1.4", gtOperator, "3.1.5", Result{Success: false}, false}, + {"name", "3.1.5", gtOperator, "3.1.4", Result{Success: true}, false}, + {"name", "3.1.4", gtOperator, "3.1.4", Result{Success: false}, false}, + + //---------- + + {"", "", geOperator, "", Result{}, true}, + + {"name", "foo", geOperator, "", Result{}, true}, + {"name", "", geOperator, "foo", Result{}, true}, + {"name", "1", geOperator, "", Result{}, true}, + {"name", "", geOperator, "1", Result{}, true}, + + {"name", "1", geOperator, "2", Result{Success: false}, false}, + {"name", "2", geOperator, "1", Result{Success: true}, false}, + {"name", "1", geOperator, "1", Result{Success: true}, false}, + + {"name", "1.3", geOperator, "2.3", Result{Success: false}, false}, + {"name", "2.3", geOperator, "1.3", Result{Success: true}, false}, + {"name", "1.3", geOperator, "1.3", Result{Success: true}, false}, + + {"name", "3.1.4", geOperator, "3.1.5", Result{Success: false}, false}, + {"name", "3.1.5", geOperator, "3.1.4", Result{Success: true}, false}, + {"name", "3.1.4", geOperator, "3.1.4", Result{Success: true}, false}, + + //---------- + + {"", "", neOperator, "", Result{}, true}, + + {"name", "foo", neOperator, "", Result{}, true}, + {"name", "", neOperator, "foo", Result{}, true}, + {"name", "1", neOperator, "", Result{}, true}, + {"name", "", neOperator, "1", Result{}, true}, + + {"name", "1", neOperator, "2", Result{Success: true}, false}, + {"name", "2", neOperator, "1", Result{Success: true}, false}, + {"name", "1", neOperator, "1", Result{Success: false}, false}, + + {"name", "1.3", neOperator, "2.3", Result{Success: true}, false}, + {"name", "2.3", neOperator, "1.3", Result{Success: true}, false}, + {"name", "1.3", neOperator, "1.3", Result{Success: false}, false}, + + {"name", "3.1.4", neOperator, "3.1.5", Result{Success: true}, false}, + {"name", "3.1.5", neOperator, "3.1.4", Result{Success: true}, false}, + {"name", "3.1.4", neOperator, "3.1.4", Result{Success: false}, false}, + + //---------- + } + + for i, d := range data { + result, err := handleVersionType(d.versionName, d.currentVersion, d.op, d.newVersion) + + msg := fmt.Sprintf("test[%d]: %+v, result: %+v", i, d, result) + + if d.expectError { + assert.Error(err, msg) + continue + } + + assert.Equal(d.result.Success, result.Success, msg) + } +} + +func TestConstraintHandleKernelVersion(t *testing.T) { + assert := assert.New(t) + + ver, err := semver.Make(kernelVersion) + assert.NoError(err) + + newerMajor, err := semverBumpVersion(ver, true, false, false) + assert.NoError(err) + + newerMinor, err := semverBumpVersion(ver, false, true, false) + assert.NoError(err) + + newerPatch, err := semverBumpVersion(ver, false, false, true) + assert.NoError(err) + + type testData struct { + version string + op Operator + result Result + expectError bool + } + + data := []testData{ + {"", eqOperator, Result{}, true}, + {"", geOperator, Result{}, true}, + {"", gtOperator, Result{}, true}, + {"", leOperator, Result{}, true}, + {"", ltOperator, Result{}, true}, + {"", neOperator, Result{}, true}, + + {kernelVersion, eqOperator, Result{Success: true}, false}, + {kernelVersion, neOperator, Result{Success: false}, false}, + + {newerMajor, eqOperator, Result{Success: false}, false}, + {newerMajor, geOperator, Result{Success: false}, false}, + {newerMajor, gtOperator, Result{Success: false}, false}, + {newerMajor, ltOperator, Result{Success: true}, false}, + {newerMajor, leOperator, Result{Success: true}, false}, + {newerMajor, neOperator, Result{Success: true}, false}, + + {newerMinor, eqOperator, Result{Success: false}, false}, + {newerMinor, geOperator, Result{Success: false}, false}, + {newerMinor, gtOperator, Result{Success: false}, false}, + {newerMinor, ltOperator, Result{Success: true}, false}, + {newerMinor, leOperator, Result{Success: true}, false}, + {newerMinor, neOperator, Result{Success: true}, false}, + + {newerPatch, eqOperator, Result{Success: false}, false}, + {newerPatch, geOperator, Result{Success: false}, false}, + {newerPatch, gtOperator, Result{Success: false}, false}, + {newerPatch, ltOperator, Result{Success: true}, false}, + {newerPatch, leOperator, Result{Success: true}, false}, + {newerPatch, neOperator, Result{Success: true}, false}, + } + + for _, debug := range []bool{true, false} { + tc := NewTestConstraint(debug) + + for i, d := range data { + result, err := tc.handleKernelVersion(d.version, d.op) + + msg := fmt.Sprintf("test[%d]: %+v, result: %+v", i, d, result) + + if d.expectError { + assert.Error(err, msg) + continue + } + + assert.Equal(d.result.Success, result.Success, msg) + } + } +} + +func TestConstraintHandleUID(t *testing.T) { + assert := assert.New(t) + + type testData struct { + uid int + op Operator + result Result + expectError bool + } + + data := []testData{ + {-1, eqOperator, Result{}, true}, + {-1, neOperator, Result{}, true}, + {-2, eqOperator, Result{}, true}, + {-2, neOperator, Result{}, true}, + {rootUID, invalidOperator, Result{}, true}, + {thisUID, invalidOperator, Result{}, true}, + + {rootUID, eqOperator, Result{Success: root}, false}, + {rootUID, neOperator, Result{Success: !root}, false}, + + {thisUID, eqOperator, Result{Success: true}, false}, + {thisUID, neOperator, Result{Success: false}, false}, + } + + for _, debug := range []bool{true, false} { + tc := NewTestConstraint(debug) + + for i, d := range data { + result, err := tc.handleUID(d.uid, d.op) + + msg := fmt.Sprintf("test[%d]: %+v, result: %+v", i, d, result) + + if d.expectError { + assert.Error(err, msg) + continue + } + + assert.NoError(err, msg) + assert.Equal(result.Success, d.result.Success, msg) + assert.NotNil(result.Description, msg) + } + } +} + +func TestConstraintHandleResults(t *testing.T) { + assert := assert.New(t) + + type testData struct { + result Result + err error + } + + data := []testData{ + {Result{}, errors.New("foo")}, + + {Result{Success: true}, nil}, + {Result{Success: false}, nil}, + } + + for _, debug := range []bool{true, false} { + tc := NewTestConstraint(debug) + + for i, d := range data { + tc.Passed = nil + tc.Failed = nil + + msg := fmt.Sprintf("test[%d]: %+v", i, d) + + if d.err != nil { + assert.Panics(func() { + tc.handleResults(d.result, d.err) + }, msg) + continue + } + + tc.handleResults(d.result, d.err) + + passedLen := len(tc.Passed) + failedLen := len(tc.Failed) + + var expectedPassedLen int + var expectedFailedLen int + + if d.result.Success { + expectedPassedLen = 1 + expectedFailedLen = 0 + } else { + expectedPassedLen = 0 + expectedFailedLen = 1 + } + + assert.Equal(passedLen, expectedPassedLen, msg) + assert.Equal(failedLen, expectedFailedLen, msg) + } + } +} + +func TestNeedUID(t *testing.T) { + assert := assert.New(t) + + data := []testDataUID{ + uidEqualsRootData, + uidNotEqualsRootData, + {thisUID, eqOperator, Constraints{ + Operator: eqOperator, + UID: thisUID, + UIDSet: true}, + }, + } + + for i, d := range data { + c := Constraints{} + + f := NeedUID(d.uid, d.op) + f(&c) + + desc := fmt.Sprintf("test[%d]: %+v", i, d) + checkUIDConstraints(assert, c, d.c, desc) + } +} + +func TestNeedRoot(t *testing.T) { + assert := assert.New(t) + + c := Constraints{} + + f := NeedRoot() + f(&c) + + checkUIDConstraints(assert, c, uidEqualsRootData.c, "TestNeedRoot") +} + +func TestNeedNonRoot(t *testing.T) { + assert := assert.New(t) + + c := Constraints{} + + f := NeedNonRoot() + f(&c) + + checkUIDConstraints(assert, c, uidNotEqualsRootData.c, "TestNeedNonRoot") +} + +func TestNeedDistroWithOp(t *testing.T) { + assert := assert.New(t) + + if getDistroErr == errUnknownDistroName { + t.Skip(skipUnknownDistroName) + } + + data := []testDataDistro{ + distroEqualsCurrentData, + distroNotEqualsCurrentData, + + // check name provided is lower-cased + { + strings.ToUpper(distroName), + eqOperator, + Constraints{ + DistroName: distroName, + Operator: eqOperator, + }, + }, + } + + for i, d := range data { + + c := Constraints{} + + f := NeedDistroWithOp(d.distro, d.op) + f(&c) + + desc := fmt.Sprintf("test[%d]: %+v, constraints: %+v", i, d, c) + checkDistroConstraints(assert, d.c, c, desc) + } +} + +func TestNeedDistroEquals(t *testing.T) { + assert := assert.New(t) + + c := Constraints{} + + f := NeedDistroEquals(distroName) + f(&c) + + checkDistroConstraints(assert, c, distroEqualsCurrentData.c, "TestNeedDistroEquals") +} + +func TestNeedDistroNotEquals(t *testing.T) { + assert := assert.New(t) + + c := Constraints{} + + f := NeedDistroNotEquals(distroName) + f(&c) + + checkDistroConstraints(assert, c, distroNotEqualsCurrentData.c, "TestNeedDistroNotEquals") +} + +func TestWithIssue(t *testing.T) { + assert := assert.New(t) + + c := Constraints{} + + issue := "issue" + + f := WithIssue(issue) + f(&c) + + assert.Equal(c.Issue, issue) +} + +func TestNeedKernelVersionWithOp(t *testing.T) { + assert := assert.New(t) + + type testData struct { + version string + op Operator + } + + version := "version" + + data := []testData{ + {version, eqOperator}, + {version, geOperator}, + {version, gtOperator}, + {version, leOperator}, + {version, ltOperator}, + {version, neOperator}, + } + + for i, d := range data { + msg := fmt.Sprintf("test[%d]: %+v", i, d) + + c := NeedKernelVersionWithOp(d.version, d.op) + + checkKernelConstraint(assert, c, d.version, d.op, msg) + } +} + +func TestNeedKernelVersion(t *testing.T) { + assert := assert.New(t) + + version := "version" + f := NeedKernelVersion(version) + checkKernelConstraint(assert, f, version, eqOperator, "TestNeedKernelVersion") +} + +func TestNeedKernelVersionEquals(t *testing.T) { + assert := assert.New(t) + + version := "version" + f := NeedKernelVersionEquals(version) + checkKernelConstraint(assert, f, version, eqOperator, "TestNeedKernelVersionEquals") +} + +func TestNeedKernelVersionLE(t *testing.T) { + assert := assert.New(t) + + version := "version" + f := NeedKernelVersionLE(version) + checkKernelConstraint(assert, f, version, leOperator, "TestNeedKernelVersionLE") +} + +func TestNeedKernelVersionLT(t *testing.T) { + assert := assert.New(t) + + version := "version" + f := NeedKernelVersionLT(version) + checkKernelConstraint(assert, f, version, ltOperator, "TestNeedKernelVersionLT") +} + +func TestNeedKernelVersionGE(t *testing.T) { + assert := assert.New(t) + + version := "version" + f := NeedKernelVersionGE(version) + checkKernelConstraint(assert, f, version, geOperator, "TestNeedKernelVersionGE") +} + +func TestNeedKernelVersionGT(t *testing.T) { + assert := assert.New(t) + + version := "version" + f := NeedKernelVersionGT(version) + checkKernelConstraint(assert, f, version, gtOperator, "TestNeedKernelVersionGT") +} + +func TestConstraintNotValid(t *testing.T) { + assert := assert.New(t) + + for _, debug := range []bool{true, false} { + tc := NewTestConstraint(debug) + + // Ensure no params is an error + assert.Panics(func() { + _ = tc.NotValid() + }) + + // Test specification of a single constraint + if root { + result := tc.NotValid(NeedRoot()) + assert.False(result) + + result = tc.NotValid(NeedNonRoot()) + assert.True(result) + } else { + result := tc.NotValid(NeedRoot()) + assert.True(result) + + result = tc.NotValid(NeedNonRoot()) + assert.False(result) + } + + // Now test specification of multiple constraints + if root { + result := tc.NotValid(NeedRoot(), NeedDistro(distroName)) + assert.False(result) + + result = tc.NotValid(NeedNonRoot(), NeedDistro(distroName)) + assert.True(result) + } else { + result := tc.NotValid(NeedRoot(), NeedDistro(distroName)) + assert.True(result) + + result = tc.NotValid(NeedNonRoot(), NeedDistro(distroName)) + assert.False(result) + } + } + +} + +func TestConstraintNotValidKernelVersion(t *testing.T) { + assert := assert.New(t) + + assert.NotNil(kernelVersion) + + // Generate new kernel versions for testing purposes based on the + // current kernel version. + higherVersion, err := incrementVersion(kernelVersion) + assert.NoError(err) + assert.NotEqual(kernelVersion, higherVersion) + + lowerVersion, err := decrementVersion(kernelVersion) + assert.NoError(err) + assert.NotEqual(kernelVersion, lowerVersion) + + // Antique kernel version numbers. + // + // Note: Not all are actually real kernel releases - we're just trying + // to do a thorough test. + lowKernelVersions := []string{ + "0.0.0", + "0.0.1", + "1.0.0", + "1.0.6-1.1.0", + "2.0.0", + "2.6.0", + lowerVersion, + } + + // Host kernel is expected to be newer than all the low kernel versions + for _, debug := range []bool{true, false} { + tc := NewTestConstraint(debug) + + for _, ver := range lowKernelVersions { + result := tc.NotValid(NeedKernelVersionEquals(ver)) + assert.True(result) + + result = tc.NotValid(NeedKernelVersionLE(ver)) + assert.True(result) + + result = tc.NotValid(NeedKernelVersionLT(ver)) + assert.True(result) + + result = tc.NotValid(NeedKernelVersionGT(ver)) + assert.False(result) + + result = tc.NotValid(NeedKernelVersionGE(ver)) + assert.False(result) + + result = tc.NotValid(NeedKernelVersionNotEquals(ver)) + assert.False(result) + } + } + + // Ridiculously high kernel version numbers. The host kernel is + // expected to never reach these values. + highKernelVersions := []string{ + higherVersion, + "999.0.0", + "999.0.999", + "999.999.999", + "1024.0.0", + } + + for _, debug := range []bool{true, false} { + tc := NewTestConstraint(debug) + + for _, ver := range highKernelVersions { + result := tc.NotValid(NeedKernelVersionEquals(ver)) + assert.True(result) + + result = tc.NotValid(NeedKernelVersionGE(ver)) + assert.True(result) + + result = tc.NotValid(NeedKernelVersionGT(ver)) + assert.True(result) + + result = tc.NotValid(NeedKernelVersionLE(ver)) + assert.False(result) + + result = tc.NotValid(NeedKernelVersionLT(ver)) + assert.False(result) + + result = tc.NotValid(NeedKernelVersionNotEquals(ver)) + assert.False(result) + } + } +} + +func TestConstraintNotValidDistroVersion(t *testing.T) { + assert := assert.New(t) + + assert.NotNil(distroVersion) + + // Generate new distro versions for testing purposes based on the + // current kernel version. + higherVersion, err := incrementVersion(distroVersion) + assert.NoError(err) + assert.NotEqual(distroVersion, higherVersion) + + lowerVersion, err := decrementVersion(distroVersion) + assert.NoError(err) + assert.NotEqual(distroVersion, lowerVersion) + + for _, debug := range []bool{true, false} { + tc := NewTestConstraint(debug) + + result := tc.NotValid(NeedDistroVersionEquals(higherVersion)) + assert.True(result) + + result = tc.NotValid(NeedDistroVersionEquals(distroVersion)) + assert.False(result) + + result = tc.NotValid(NeedDistroVersionLE(higherVersion)) + assert.False(result) + + result = tc.NotValid(NeedDistroVersionLE(distroVersion)) + assert.False(result) + + result = tc.NotValid(NeedDistroVersionLT(higherVersion)) + assert.False(result) + + result = tc.NotValid(NeedDistroVersionLT(distroVersion)) + assert.True(result) + + result = tc.NotValid(NeedDistroVersionGE(higherVersion)) + assert.True(result) + + result = tc.NotValid(NeedDistroVersionGE(distroVersion)) + assert.False(result) + + result = tc.NotValid(NeedDistroVersionGT(higherVersion)) + assert.True(result) + + result = tc.NotValid(NeedDistroVersionGT(distroVersion)) + assert.True(result) + + result = tc.NotValid(NeedDistroVersionNotEquals(higherVersion)) + assert.False(result) + + result = tc.NotValid(NeedDistroVersionNotEquals(distroVersion)) + assert.True(result) + } +} + +func TestConstraintConstraintValid(t *testing.T) { + assert := assert.New(t) + + type testData struct { + fn Constraint + valid bool + expected TestConstraint + } + + issue := "issue" + + data := []testData{ + { + WithIssue(issue), + true, + TestConstraint{Issue: issue}, + }, + + { + NeedDistroWithOp(distroName, eqOperator), + true, + TestConstraint{ + Passed: []Result{ + {Success: true}, + }, + }, + }, + { + NeedDistroWithOp(distroName, neOperator), + false, + TestConstraint{ + Failed: []Result{ + {Success: false}, + }, + }, + }, + { + NeedDistroWithOp(getAnotherDistro(distroName), eqOperator), + false, + TestConstraint{ + Failed: []Result{ + {Success: false}, + }, + }, + }, + { + NeedDistroWithOp(getAnotherDistro(distroName), neOperator), + true, + TestConstraint{ + Failed: []Result{ + {Success: true}, + }, + }, + }, + + { + NeedDistroEquals(distroName), + true, + TestConstraint{ + Passed: []Result{ + {Success: true}, + }, + }, + }, + { + NeedDistroEquals(getAnotherDistro(distroName)), + false, + TestConstraint{ + Failed: []Result{ + {Success: false}, + }, + }, + }, + + { + NeedDistroNotEquals(getAnotherDistro(distroName)), + true, + TestConstraint{ + Passed: []Result{ + {Success: true}, + }, + }, + }, + { + NeedDistroNotEquals(distroName), + false, + TestConstraint{ + Failed: []Result{ + {Success: false}, + }, + }, + }, + + { + NeedDistro(distroName), + true, + TestConstraint{ + Passed: []Result{ + {Success: true}, + }, + }, + }, + { + NeedDistro(getAnotherDistro(distroName)), + false, + TestConstraint{ + Failed: []Result{ + {Success: false}, + }, + }, + }, + } + + if root { + td := testData{ + fn: NeedRoot(), + valid: true, + expected: TestConstraint{ + Passed: []Result{ + {Success: true}, + }, + }, + } + + data = append(data, td) + + td = testData{ + fn: NeedNonRoot(), + valid: false, + expected: TestConstraint{ + Failed: []Result{ + {Success: false}, + }, + }, + } + + data = append(data, td) + } else { + td := testData{ + fn: NeedRoot(), + valid: false, + expected: TestConstraint{ + Failed: []Result{ + {Success: false}, + }, + }, + } + + data = append(data, td) + + td = testData{ + fn: NeedNonRoot(), + valid: true, + expected: TestConstraint{ + Passed: []Result{ + {Success: true}, + }, + }, + } + + data = append(data, td) + } + + for _, debug := range []bool{true, false} { + for i, d := range data { + tc := NewTestConstraint(debug) + + result := tc.constraintValid(d.fn) + + msg := fmt.Sprintf("test[%d]: %+v, result: %v", i, d, result) + + if d.expected.Issue != "" { + assert.Equal(tc.Issue, d.expected.Issue, msg) + } + + if d.valid { + assert.True(result, msg) + + if len(d.expected.Passed) != 0 { + assert.Equal(d.expected.Passed[0].Success, tc.Passed[0].Success, msg) + } + } else { + assert.False(result, msg) + + if len(d.expected.Failed) != 0 { + assert.Equal(d.expected.Failed[0].Success, tc.Failed[0].Success, msg) + } + } + } + } +} + +func TestEvalIntVersion(t *testing.T) { + assert := assert.New(t) + + type testData struct { + currentVer string + op Operator + newVer string + expectSuccess bool + expectError bool + } + + data := []testData{ + //---------- + + {"", eqOperator, "", false, true}, + {"", eqOperator, "1", false, true}, + {"1", eqOperator, "", false, true}, + + {"foo", eqOperator, "", false, true}, + {"", eqOperator, "foo", false, true}, + {"foo", eqOperator, "1", false, true}, + {"1", eqOperator, "foo", false, true}, + + {"1", eqOperator, "1", true, false}, + {"1", eqOperator, "2", false, false}, + + //---------- + + {"", geOperator, "", false, true}, + {"foo", geOperator, "", false, true}, + {"", geOperator, "foo", false, true}, + {"1", geOperator, "", false, true}, + {"", geOperator, "1", false, true}, + + {"1", geOperator, "2", false, false}, + {"2", geOperator, "1", true, false}, + {"2", geOperator, "2", true, false}, + + //---------- + + {"", gtOperator, "", false, true}, + {"foo", gtOperator, "", false, true}, + {"", gtOperator, "foo", false, true}, + {"1", gtOperator, "", false, true}, + {"", gtOperator, "1", false, true}, + + {"2", gtOperator, "1", true, false}, + {"1", gtOperator, "2", false, false}, + {"1", gtOperator, "1", false, false}, + + //---------- + + {"", leOperator, "", false, true}, + {"foo", leOperator, "", false, true}, + {"", leOperator, "foo", false, true}, + {"1", leOperator, "", false, true}, + {"", leOperator, "1", false, true}, + + {"2", leOperator, "1", false, false}, + {"1", leOperator, "2", true, false}, + {"1", leOperator, "1", true, false}, + + //---------- + + {"", ltOperator, "", false, true}, + {"foo", ltOperator, "", false, true}, + {"", ltOperator, "foo", false, true}, + {"1", ltOperator, "", false, true}, + {"", ltOperator, "1", false, true}, + + {"1", ltOperator, "2", true, false}, + {"2", ltOperator, "1", false, false}, + {"1", ltOperator, "1", false, false}, + + //---------- + + {"", neOperator, "", false, true}, + {"foo", neOperator, "", false, true}, + {"", neOperator, "foo", false, true}, + {"1", neOperator, "", false, true}, + {"", neOperator, "1", false, true}, + + {"2", neOperator, "2", false, false}, + {"1", neOperator, "2", true, false}, + {"2", neOperator, "1", true, false}, + } + + for i, d := range data { + success, err := evalIntVersion(d.currentVer, d.op, d.newVer) + + msg := fmt.Sprintf("test[%d]: %+v, success: %v", i, d, success) + + if d.expectError { + assert.Error(err, msg) + continue + } + + if d.expectSuccess { + assert.True(success, msg) + } else { + assert.False(success, msg) + } + } +} + +func TestEvalFloatVersion(t *testing.T) { + assert := assert.New(t) + + type testData struct { + currentVer string + op Operator + newVer string + expectSuccess bool + expectError bool + } + + data := []testData{ + //---------- + + {"", eqOperator, "", false, true}, + {"foo", eqOperator, "", false, true}, + {"", eqOperator, "foo", false, true}, + {"foo", eqOperator, "1", false, true}, + {"1", eqOperator, "foo", false, true}, + + {"1", eqOperator, "1", true, false}, + {"1", eqOperator, "2", false, false}, + + {"1.1", eqOperator, "1.1", true, false}, + {"1.1", eqOperator, "2.1", false, false}, + + //---------- + + {"", geOperator, "", false, true}, + {"foo", geOperator, "", false, true}, + {"", geOperator, "foo", false, true}, + + {"1", geOperator, "2", false, false}, + {"2", geOperator, "1", true, false}, + {"2", geOperator, "2", true, false}, + + {"1.1", geOperator, "2.1", false, false}, + {"2.1", geOperator, "1.1", true, false}, + {"2.1", geOperator, "2.1", true, false}, + + //---------- + + {"", gtOperator, "", false, true}, + {"foo", gtOperator, "", false, true}, + {"", gtOperator, "foo", false, true}, + + {"2", gtOperator, "1", true, false}, + {"1", gtOperator, "2", false, false}, + {"1", gtOperator, "1", false, false}, + + {"2.1", gtOperator, "1.1", true, false}, + {"1.1", gtOperator, "2.1", false, false}, + {"1.1", gtOperator, "1.1", false, false}, + + //---------- + + {"", leOperator, "", false, true}, + {"foo", leOperator, "", false, true}, + {"", leOperator, "foo", false, true}, + + {"2", leOperator, "1", false, false}, + {"1", leOperator, "2", true, false}, + {"1", leOperator, "1", true, false}, + + {"2.1", leOperator, "1.1", false, false}, + {"1.1", leOperator, "2.1", true, false}, + {"1.1", leOperator, "1.1", true, false}, + + //---------- + + {"", ltOperator, "", false, true}, + {"foo", ltOperator, "", false, true}, + {"", ltOperator, "foo", false, true}, + + {"1", ltOperator, "2", true, false}, + {"2", ltOperator, "1", false, false}, + {"1", ltOperator, "1", false, false}, + + {"1.1", ltOperator, "2.1", true, false}, + {"2.1", ltOperator, "1.1", false, false}, + {"1.1", ltOperator, "1.1", false, false}, + + //---------- + + {"", neOperator, "", false, true}, + {"foo", neOperator, "", false, true}, + {"", neOperator, "foo", false, true}, + + {"2", neOperator, "2", false, false}, + {"1", neOperator, "2", true, false}, + {"2", neOperator, "1", true, false}, + + {"2.1", neOperator, "2.1", false, false}, + {"1.1", neOperator, "2.1", true, false}, + {"2.1", neOperator, "1.1", true, false}, + } + + for i, d := range data { + success, err := evalFloatVersion(d.currentVer, d.op, d.newVer) + + msg := fmt.Sprintf("test[%d]: %+v, success: %v", i, d, success) + + if d.expectError { + assert.Error(err, msg) + continue + } + + if d.expectSuccess { + assert.True(success, msg) + } else { + assert.False(success, msg) + } + } +} + +func TestEvalSemverVersion(t *testing.T) { + assert := assert.New(t) + + type testData struct { + currentVer string + op Operator + newVer string + expectSuccess bool + expectError bool + } + + data := []testData{ + //---------- + + {"", eqOperator, "", false, true}, + {"foo", eqOperator, "", false, true}, + {"", eqOperator, "foo", false, true}, + {"foo", eqOperator, "1", false, true}, + {"1", eqOperator, "foo", false, true}, + + {"1.1.1", eqOperator, "1.1.1", true, false}, + {"1.1.1", eqOperator, "2.2.2", false, false}, + + //---------- + + {"", geOperator, "", false, true}, + {"foo", geOperator, "", false, true}, + {"", geOperator, "foo", false, true}, + + {"1.1.1", geOperator, "2.2.2", false, false}, + {"2.2.2", geOperator, "1.1.1", true, false}, + {"2.2.2", geOperator, "2.2.2", true, false}, + + //---------- + + {"", gtOperator, "", false, true}, + {"foo", gtOperator, "", false, true}, + {"", gtOperator, "foo", false, true}, + + {"2.2.2", gtOperator, "1.1.1", true, false}, + {"1.1.1", gtOperator, "2.2.2", false, false}, + {"1.1.1", gtOperator, "1.1.1", false, false}, + + //---------- + + {"", leOperator, "", false, true}, + {"foo", leOperator, "", false, true}, + {"", leOperator, "foo", false, true}, + + {"2.2.2", leOperator, "1.1.1", false, false}, + {"1.1.1", leOperator, "2.2.2", true, false}, + {"1.1.1", leOperator, "1.1.1", true, false}, + + //---------- + + {"", ltOperator, "", false, true}, + {"foo", ltOperator, "", false, true}, + {"", ltOperator, "foo", false, true}, + + {"1.1.1", ltOperator, "2.2.2", true, false}, + {"2.2.2", ltOperator, "1.1.1", false, false}, + {"1.1.1", ltOperator, "1.1.1", false, false}, + + //---------- + + {"", neOperator, "", false, true}, + {"foo", neOperator, "", false, true}, + {"", neOperator, "foo", false, true}, + + {"2.2.2", neOperator, "2.2.2", false, false}, + {"1.1.1", neOperator, "2.2.2", true, false}, + {"2.2.2", neOperator, "1.1.1", true, false}, + } + + for i, d := range data { + success, err := evalSemverVersion(d.currentVer, d.op, d.newVer) + + msg := fmt.Sprintf("test[%d]: %+v, success: %v", i, d, success) + + if d.expectError { + assert.Error(err, msg) + continue + } + + if d.expectSuccess { + assert.True(success, msg) + } else { + assert.False(success, msg) + } + } +} diff --git a/src/runtime/pkg/katatestutils/utils.go b/src/runtime/pkg/katatestutils/utils.go new file mode 100644 index 000000000..bb71d616f --- /dev/null +++ b/src/runtime/pkg/katatestutils/utils.go @@ -0,0 +1,92 @@ +// Copyright (c) 2018-2019 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katatestutils + +import "strconv" + +type RuntimeConfigOptions struct { + Hypervisor string + HypervisorPath string + DefaultVCPUCount uint32 + DefaultMaxVCPUCount uint32 + DefaultMemSize uint32 + DefaultMsize9p uint32 + DefaultGuestHookPath string + KernelPath string + ImagePath string + KernelParams string + MachineType string + ShimPath string + ProxyPath string + NetmonPath string + LogPath string + BlockDeviceDriver string + AgentTraceMode string + AgentTraceType string + SharedFS string + PCIeRootPort uint32 + DisableBlock bool + EnableIOThreads bool + HotplugVFIOOnRootBus bool + DisableNewNetNs bool + HypervisorDebug bool + RuntimeDebug bool + RuntimeTrace bool + ProxyDebug bool + ShimDebug bool + NetmonDebug bool + AgentDebug bool + AgentTrace bool +} + +func MakeRuntimeConfigFileData(config RuntimeConfigOptions) string { + return ` + # Runtime configuration file + + [hypervisor.` + config.Hypervisor + `] + path = "` + config.HypervisorPath + `" + kernel = "` + config.KernelPath + `" + block_device_driver = "` + config.BlockDeviceDriver + `" + kernel_params = "` + config.KernelParams + `" + image = "` + config.ImagePath + `" + machine_type = "` + config.MachineType + `" + default_vcpus = ` + strconv.FormatUint(uint64(config.DefaultVCPUCount), 10) + ` + default_maxvcpus = ` + strconv.FormatUint(uint64(config.DefaultMaxVCPUCount), 10) + ` + default_memory = ` + strconv.FormatUint(uint64(config.DefaultMemSize), 10) + ` + disable_block_device_use = ` + strconv.FormatBool(config.DisableBlock) + ` + enable_iothreads = ` + strconv.FormatBool(config.EnableIOThreads) + ` + hotplug_vfio_on_root_bus = ` + strconv.FormatBool(config.HotplugVFIOOnRootBus) + ` + pcie_root_port = ` + strconv.FormatUint(uint64(config.PCIeRootPort), 10) + ` + msize_9p = ` + strconv.FormatUint(uint64(config.DefaultMsize9p), 10) + ` + enable_debug = ` + strconv.FormatBool(config.HypervisorDebug) + ` + guest_hook_path = "` + config.DefaultGuestHookPath + `" + shared_fs = "` + config.SharedFS + `" + virtio_fs_daemon = "/path/to/virtiofsd" + + [proxy.kata] + enable_debug = ` + strconv.FormatBool(config.ProxyDebug) + ` + path = "` + config.ProxyPath + `" + + [shim.kata] + path = "` + config.ShimPath + `" + enable_debug = ` + strconv.FormatBool(config.ShimDebug) + ` + + [agent.kata] + enable_debug = ` + strconv.FormatBool(config.AgentDebug) + ` + enable_tracing = ` + strconv.FormatBool(config.AgentTrace) + ` + trace_mode = "` + config.AgentTraceMode + `"` + ` + trace_type = "` + config.AgentTraceType + `"` + ` + + [netmon] + path = "` + config.NetmonPath + `" + enable_debug = ` + strconv.FormatBool(config.NetmonDebug) + ` + + [runtime] + enable_debug = ` + strconv.FormatBool(config.RuntimeDebug) + ` + enable_tracing = ` + strconv.FormatBool(config.RuntimeTrace) + ` + disable_new_netns= ` + strconv.FormatBool(config.DisableNewNetNs) +} diff --git a/src/runtime/pkg/katautils/README.md b/src/runtime/pkg/katautils/README.md new file mode 100644 index 000000000..36481cdba --- /dev/null +++ b/src/runtime/pkg/katautils/README.md @@ -0,0 +1,4 @@ +# Kata utilities packages + +The `katautils` package contains useful functions that are shared by various +parts of the codebase, including the runtime and the container v2 shim. diff --git a/src/runtime/pkg/katautils/config-settings.go.in b/src/runtime/pkg/katautils/config-settings.go.in new file mode 100644 index 000000000..aaf78cc3f --- /dev/null +++ b/src/runtime/pkg/katautils/config-settings.go.in @@ -0,0 +1,66 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Note that some variables are "var" to allow them to be modified +// by the tests. + +package katautils + +var defaultHypervisorPath = "/usr/bin/qemu-lite-system-x86_64" +var defaultHypervisorCtlPath = "/usr/bin/acrnctl" +var defaultJailerPath = "/usr/bin/jailer" +var defaultImagePath = "/usr/share/kata-containers/kata-containers.img" +var defaultKernelPath = "/usr/share/kata-containers/vmlinuz.container" +var defaultInitrdPath = "/usr/share/kata-containers/kata-containers-initrd.img" +var defaultFirmwarePath = "" +var defaultMachineAccelerators = "" +var defaultShimPath = "/usr/libexec/kata-containers/kata-shim" +var systemdUnitName = "kata-containers.target" + +const defaultKernelParams = "" +const defaultMachineType = "pc" + +const defaultVCPUCount uint32 = 1 +const defaultMaxVCPUCount uint32 = 0 +const defaultMemSize uint32 = 2048 // MiB +const defaultMemSlots uint32 = 10 +const defaultMemOffset uint32 = 0 // MiB +const defaultVirtioMem bool = false +const defaultBridgesCount uint32 = 1 +const defaultInterNetworkingModel = "tcfilter" +const defaultDisableBlockDeviceUse bool = false +const defaultBlockDeviceDriver = "virtio-scsi" +const defaultBlockDeviceCacheSet bool = false +const defaultBlockDeviceCacheDirect bool = false +const defaultBlockDeviceCacheNoflush bool = false +const defaultEnableIOThreads bool = false +const defaultEnableMemPrealloc bool = false +const defaultEnableHugePages bool = false +const defaultFileBackedMemRootDir string = "" +const defaultEnableSwap bool = false +const defaultEnableDebug bool = false +const defaultDisableNestingChecks bool = false +const defaultMsize9p uint32 = 8192 +const defaultHotplugVFIOOnRootBus bool = false +const defaultPCIeRootPort = 0 +const defaultEntropySource = "/dev/urandom" +const defaultGuestHookPath string = "" +const defaultVirtioFSCacheMode = "none" +const defaultDisableImageNvdimm = false +const defaultVhostUserStorePath string = "/var/run/kata-containers/vhost-user/" + +const defaultTemplatePath string = "/run/vc/vm/template" +const defaultVMCacheEndpoint string = "/var/run/kata-containers/cache.sock" + +// Default config file used by stateless systems. +var defaultRuntimeConfiguration = "@CONFIG_PATH@" + +// Alternate config file that takes precedence over +// defaultRuntimeConfiguration. +var defaultSysConfRuntimeConfiguration = "@SYSCONFIG@" + +var name = "kata" +var defaultProxyPath = "/usr/libexec/kata-containers/kata-proxy" +var defaultNetmonPath = "/usr/libexec/kata-containers/kata-netmon" diff --git a/src/runtime/pkg/katautils/config.go b/src/runtime/pkg/katautils/config.go new file mode 100644 index 000000000..14794a24f --- /dev/null +++ b/src/runtime/pkg/katautils/config.go @@ -0,0 +1,1405 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "errors" + "fmt" + "io/ioutil" + goruntime "runtime" + "strings" + + "github.com/BurntSushi/toml" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/device/config" + exp "github.com/kata-containers/runtime/virtcontainers/experimental" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/kata-containers/runtime/virtcontainers/utils" + "github.com/sirupsen/logrus" +) + +const ( + defaultHypervisor = vc.QemuHypervisor + defaultAgent = vc.KataContainersAgent +) + +var ( + defaultProxy = vc.KataProxyType + defaultShim = vc.KataShimType + + // if true, enable opentracing support. + tracing = false +) + +// The TOML configuration file contains a number of sections (or +// tables). The names of these tables are in dotted ("nested table") +// form: +// +// [.] +// +// The components are hypervisor, proxy, shim and agent. For example, +// +// [proxy.kata] +// +// The currently supported types are listed below: +const ( + // supported hypervisor component types + firecrackerHypervisorTableType = "firecracker" + clhHypervisorTableType = "clh" + qemuHypervisorTableType = "qemu" + acrnHypervisorTableType = "acrn" + + // supported proxy component types + kataProxyTableType = "kata" + + // supported shim component types + kataShimTableType = "kata" + + // supported agent component types + kataAgentTableType = "kata" + + // the maximum amount of PCI bridges that can be cold plugged in a VM + maxPCIBridges uint32 = 5 +) + +type tomlConfig struct { + Hypervisor map[string]hypervisor + Proxy map[string]proxy + Shim map[string]shim + Agent map[string]agent + Runtime runtime + Factory factory + Netmon netmon +} + +type factory struct { + Template bool `toml:"enable_template"` + TemplatePath string `toml:"template_path"` + VMCacheNumber uint `toml:"vm_cache_number"` + VMCacheEndpoint string `toml:"vm_cache_endpoint"` +} + +type hypervisor struct { + Path string `toml:"path"` + JailerPath string `toml:"jailer_path"` + Kernel string `toml:"kernel"` + CtlPath string `toml:"ctlpath"` + Initrd string `toml:"initrd"` + Image string `toml:"image"` + Firmware string `toml:"firmware"` + MachineAccelerators string `toml:"machine_accelerators"` + KernelParams string `toml:"kernel_params"` + MachineType string `toml:"machine_type"` + BlockDeviceDriver string `toml:"block_device_driver"` + EntropySource string `toml:"entropy_source"` + SharedFS string `toml:"shared_fs"` + VirtioFSDaemon string `toml:"virtio_fs_daemon"` + VirtioFSCache string `toml:"virtio_fs_cache"` + VirtioFSExtraArgs []string `toml:"virtio_fs_extra_args"` + VirtioFSCacheSize uint32 `toml:"virtio_fs_cache_size"` + BlockDeviceCacheSet bool `toml:"block_device_cache_set"` + BlockDeviceCacheDirect bool `toml:"block_device_cache_direct"` + BlockDeviceCacheNoflush bool `toml:"block_device_cache_noflush"` + EnableVhostUserStore bool `toml:"enable_vhost_user_store"` + VhostUserStorePath string `toml:"vhost_user_store_path"` + NumVCPUs int32 `toml:"default_vcpus"` + DefaultMaxVCPUs uint32 `toml:"default_maxvcpus"` + MemorySize uint32 `toml:"default_memory"` + MemSlots uint32 `toml:"memory_slots"` + MemOffset uint32 `toml:"memory_offset"` + DefaultBridges uint32 `toml:"default_bridges"` + Msize9p uint32 `toml:"msize_9p"` + PCIeRootPort uint32 `toml:"pcie_root_port"` + DisableBlockDeviceUse bool `toml:"disable_block_device_use"` + MemPrealloc bool `toml:"enable_mem_prealloc"` + HugePages bool `toml:"enable_hugepages"` + VirtioMem bool `toml:"enable_virtio_mem"` + FileBackedMemRootDir string `toml:"file_mem_backend"` + Swap bool `toml:"enable_swap"` + Debug bool `toml:"enable_debug"` + DisableNestingChecks bool `toml:"disable_nesting_checks"` + EnableIOThreads bool `toml:"enable_iothreads"` + UseVSock bool `toml:"use_vsock"` + DisableImageNvdimm bool `toml:"disable_image_nvdimm"` + HotplugVFIOOnRootBus bool `toml:"hotplug_vfio_on_root_bus"` + DisableVhostNet bool `toml:"disable_vhost_net"` + GuestHookPath string `toml:"guest_hook_path"` +} + +type proxy struct { + Path string `toml:"path"` + Debug bool `toml:"enable_debug"` +} + +type runtime struct { + Debug bool `toml:"enable_debug"` + Tracing bool `toml:"enable_tracing"` + DisableNewNetNs bool `toml:"disable_new_netns"` + DisableGuestSeccomp bool `toml:"disable_guest_seccomp"` + SandboxCgroupOnly bool `toml:"sandbox_cgroup_only"` + Experimental []string `toml:"experimental"` + InterNetworkModel string `toml:"internetworking_model"` +} + +type shim struct { + Path string `toml:"path"` + Debug bool `toml:"enable_debug"` + Tracing bool `toml:"enable_tracing"` +} + +type agent struct { + Debug bool `toml:"enable_debug"` + Tracing bool `toml:"enable_tracing"` + TraceMode string `toml:"trace_mode"` + TraceType string `toml:"trace_type"` + KernelModules []string `toml:"kernel_modules"` +} + +type netmon struct { + Path string `toml:"path"` + Debug bool `toml:"enable_debug"` + Enable bool `toml:"enable_netmon"` +} + +func (h hypervisor) path() (string, error) { + p := h.Path + + if h.Path == "" { + p = defaultHypervisorPath + } + + return ResolvePath(p) +} + +func (h hypervisor) ctlpath() (string, error) { + p := h.CtlPath + + if h.CtlPath == "" { + p = defaultHypervisorCtlPath + } + + return ResolvePath(p) +} + +func (h hypervisor) jailerPath() (string, error) { + p := h.JailerPath + + if h.JailerPath == "" { + return "", nil + } + + return ResolvePath(p) +} + +func (h hypervisor) kernel() (string, error) { + p := h.Kernel + + if p == "" { + p = defaultKernelPath + } + + return ResolvePath(p) +} + +func (h hypervisor) initrd() (string, error) { + p := h.Initrd + + if p == "" { + return "", errors.New("initrd is not set") + } + + return ResolvePath(p) +} + +func (h hypervisor) image() (string, error) { + p := h.Image + + if p == "" { + return "", errors.New("image is not set") + } + + return ResolvePath(p) +} + +func (h hypervisor) firmware() (string, error) { + p := h.Firmware + + if p == "" { + if defaultFirmwarePath == "" { + return "", nil + } + p = defaultFirmwarePath + } + + return ResolvePath(p) +} + +func (h hypervisor) machineAccelerators() string { + var machineAccelerators string + accelerators := strings.Split(h.MachineAccelerators, ",") + acceleratorsLen := len(accelerators) + for i := 0; i < acceleratorsLen; i++ { + if accelerators[i] != "" { + machineAccelerators += strings.Trim(accelerators[i], "\r\t\n ") + "," + } + } + + machineAccelerators = strings.Trim(machineAccelerators, ",") + + return machineAccelerators +} + +func (h hypervisor) kernelParams() string { + if h.KernelParams == "" { + return defaultKernelParams + } + + return h.KernelParams +} + +func (h hypervisor) machineType() string { + if h.MachineType == "" { + return defaultMachineType + } + + return h.MachineType +} + +func (h hypervisor) GetEntropySource() string { + if h.EntropySource == "" { + return defaultEntropySource + } + + return h.EntropySource +} + +func (h hypervisor) defaultVCPUs() uint32 { + numCPUs := goruntime.NumCPU() + + if h.NumVCPUs < 0 || h.NumVCPUs > int32(numCPUs) { + return uint32(numCPUs) + } + if h.NumVCPUs == 0 { // or unspecified + return defaultVCPUCount + } + + return uint32(h.NumVCPUs) +} + +func (h hypervisor) defaultMaxVCPUs() uint32 { + numcpus := uint32(goruntime.NumCPU()) + maxvcpus := vc.MaxQemuVCPUs() + reqVCPUs := h.DefaultMaxVCPUs + + //don't exceed the number of physical CPUs. If a default is not provided, use the + // numbers of physical CPUs + if reqVCPUs >= numcpus || reqVCPUs == 0 { + reqVCPUs = numcpus + } + + // Don't exceed the maximum number of vCPUs supported by hypervisor + if reqVCPUs > maxvcpus { + return maxvcpus + } + + return reqVCPUs +} + +func (h hypervisor) defaultMemSz() uint32 { + if h.MemorySize < vc.MinHypervisorMemory { + return defaultMemSize // MiB + } + + return h.MemorySize +} + +func (h hypervisor) defaultMemSlots() uint32 { + slots := h.MemSlots + if slots == 0 { + slots = defaultMemSlots + } + + return slots +} + +func (h hypervisor) defaultMemOffset() uint32 { + offset := h.MemOffset + if offset == 0 { + offset = defaultMemOffset + } + + return offset +} + +func (h hypervisor) defaultBridges() uint32 { + if h.DefaultBridges == 0 { + return defaultBridgesCount + } + + if h.DefaultBridges > maxPCIBridges { + return maxPCIBridges + } + + return h.DefaultBridges +} + +func (h hypervisor) defaultVirtioFSCache() string { + if h.VirtioFSCache == "" { + return defaultVirtioFSCacheMode + } + + return h.VirtioFSCache +} + +func (h hypervisor) blockDeviceDriver() (string, error) { + supportedBlockDrivers := []string{config.VirtioSCSI, config.VirtioBlock, config.VirtioMmio, config.Nvdimm, config.VirtioBlockCCW} + + if h.BlockDeviceDriver == "" { + return defaultBlockDeviceDriver, nil + } + + for _, b := range supportedBlockDrivers { + if b == h.BlockDeviceDriver { + return h.BlockDeviceDriver, nil + } + } + + return "", fmt.Errorf("Invalid hypervisor block storage driver %v specified (supported drivers: %v)", h.BlockDeviceDriver, supportedBlockDrivers) +} + +func (h hypervisor) sharedFS() (string, error) { + supportedSharedFS := []string{config.Virtio9P, config.VirtioFS} + + if h.SharedFS == "" { + return config.Virtio9P, nil + } + + for _, fs := range supportedSharedFS { + if fs == h.SharedFS { + return h.SharedFS, nil + } + } + + return "", fmt.Errorf("Invalid hypervisor shared file system %v specified (supported file systems: %v)", h.SharedFS, supportedSharedFS) +} + +func (h hypervisor) msize9p() uint32 { + if h.Msize9p == 0 { + return defaultMsize9p + } + + return h.Msize9p +} + +func (h hypervisor) useVSock() bool { + return h.UseVSock +} + +func (h hypervisor) guestHookPath() string { + if h.GuestHookPath == "" { + return defaultGuestHookPath + } + return h.GuestHookPath +} + +func (h hypervisor) vhostUserStorePath() string { + if h.VhostUserStorePath == "" { + return defaultVhostUserStorePath + } + return h.VhostUserStorePath +} + +func (h hypervisor) getInitrdAndImage() (initrd string, image string, err error) { + initrd, errInitrd := h.initrd() + + image, errImage := h.image() + + if image != "" && initrd != "" { + return "", "", errors.New("having both an image and an initrd defined in the configuration file is not supported") + } + + if errInitrd != nil && errImage != nil { + return "", "", fmt.Errorf("Either initrd or image must be set to a valid path (initrd: %v) (image: %v)", errInitrd, errImage) + } + + return +} + +func (p proxy) path() (string, error) { + path := p.Path + if path == "" { + path = defaultProxyPath + } + + return ResolvePath(path) +} + +func (p proxy) debug() bool { + return p.Debug +} + +func (s shim) path() (string, error) { + p := s.Path + + if p == "" { + p = defaultShimPath + } + + return ResolvePath(p) +} + +func (s shim) debug() bool { + return s.Debug +} + +func (s shim) trace() bool { + return s.Tracing +} + +func (a agent) debug() bool { + return a.Debug +} + +func (a agent) trace() bool { + return a.Tracing +} + +func (a agent) traceMode() string { + return a.TraceMode +} + +func (a agent) traceType() string { + return a.TraceType +} + +func (a agent) kernelModules() []string { + return a.KernelModules +} + +func (n netmon) enable() bool { + return n.Enable +} + +func (n netmon) path() string { + if n.Path == "" { + return defaultNetmonPath + } + + return n.Path +} + +func (n netmon) debug() bool { + return n.Debug +} + +func newFirecrackerHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) { + hypervisor, err := h.path() + if err != nil { + return vc.HypervisorConfig{}, err + } + + jailer, err := h.jailerPath() + if err != nil { + return vc.HypervisorConfig{}, err + } + + kernel, err := h.kernel() + if err != nil { + return vc.HypervisorConfig{}, err + } + + initrd, image, err := h.getInitrdAndImage() + if err != nil { + return vc.HypervisorConfig{}, err + } + + firmware, err := h.firmware() + if err != nil { + return vc.HypervisorConfig{}, err + } + + kernelParams := h.kernelParams() + + blockDriver, err := h.blockDeviceDriver() + if err != nil { + return vc.HypervisorConfig{}, err + } + + if !utils.SupportsVsocks() { + return vc.HypervisorConfig{}, errors.New("No vsock support, firecracker cannot be used") + } + + return vc.HypervisorConfig{ + HypervisorPath: hypervisor, + JailerPath: jailer, + KernelPath: kernel, + InitrdPath: initrd, + ImagePath: image, + FirmwarePath: firmware, + KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)), + NumVCPUs: h.defaultVCPUs(), + DefaultMaxVCPUs: h.defaultMaxVCPUs(), + MemorySize: h.defaultMemSz(), + MemSlots: h.defaultMemSlots(), + EntropySource: h.GetEntropySource(), + DefaultBridges: h.defaultBridges(), + DisableBlockDeviceUse: h.DisableBlockDeviceUse, + HugePages: h.HugePages, + Mlock: !h.Swap, + Debug: h.Debug, + DisableNestingChecks: h.DisableNestingChecks, + BlockDeviceDriver: blockDriver, + EnableIOThreads: h.EnableIOThreads, + DisableVhostNet: true, // vhost-net backend is not supported in Firecracker + UseVSock: true, + GuestHookPath: h.guestHookPath(), + }, nil +} + +func newQemuHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) { + hypervisor, err := h.path() + if err != nil { + return vc.HypervisorConfig{}, err + } + + kernel, err := h.kernel() + if err != nil { + return vc.HypervisorConfig{}, err + } + + initrd, image, err := h.getInitrdAndImage() + if err != nil { + return vc.HypervisorConfig{}, err + } + + if image != "" && initrd != "" { + return vc.HypervisorConfig{}, + errors.New("having both an image and an initrd defined in the configuration file is not supported") + } + + if image == "" && initrd == "" { + return vc.HypervisorConfig{}, + errors.New("either image or initrd must be defined in the configuration file") + } + + firmware, err := h.firmware() + if err != nil { + return vc.HypervisorConfig{}, err + } + + machineAccelerators := h.machineAccelerators() + kernelParams := h.kernelParams() + machineType := h.machineType() + + blockDriver, err := h.blockDeviceDriver() + if err != nil { + return vc.HypervisorConfig{}, err + } + + sharedFS, err := h.sharedFS() + if err != nil { + return vc.HypervisorConfig{}, err + } + + if sharedFS == config.VirtioFS && h.VirtioFSDaemon == "" { + return vc.HypervisorConfig{}, + errors.New("cannot enable virtio-fs without daemon path in configuration file") + } + + useVSock := false + if h.useVSock() { + if utils.SupportsVsocks() { + kataUtilsLogger.Info("vsock supported") + useVSock = true + } else { + kataUtilsLogger.Warn("No vsock support, falling back to legacy serial port") + } + } + + return vc.HypervisorConfig{ + HypervisorPath: hypervisor, + KernelPath: kernel, + InitrdPath: initrd, + ImagePath: image, + FirmwarePath: firmware, + MachineAccelerators: machineAccelerators, + KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)), + HypervisorMachineType: machineType, + NumVCPUs: h.defaultVCPUs(), + DefaultMaxVCPUs: h.defaultMaxVCPUs(), + MemorySize: h.defaultMemSz(), + MemSlots: h.defaultMemSlots(), + MemOffset: h.defaultMemOffset(), + VirtioMem: h.VirtioMem, + EntropySource: h.GetEntropySource(), + DefaultBridges: h.defaultBridges(), + DisableBlockDeviceUse: h.DisableBlockDeviceUse, + SharedFS: sharedFS, + VirtioFSDaemon: h.VirtioFSDaemon, + VirtioFSCacheSize: h.VirtioFSCacheSize, + VirtioFSCache: h.defaultVirtioFSCache(), + VirtioFSExtraArgs: h.VirtioFSExtraArgs, + MemPrealloc: h.MemPrealloc, + HugePages: h.HugePages, + FileBackedMemRootDir: h.FileBackedMemRootDir, + Mlock: !h.Swap, + Debug: h.Debug, + DisableNestingChecks: h.DisableNestingChecks, + BlockDeviceDriver: blockDriver, + BlockDeviceCacheSet: h.BlockDeviceCacheSet, + BlockDeviceCacheDirect: h.BlockDeviceCacheDirect, + BlockDeviceCacheNoflush: h.BlockDeviceCacheNoflush, + EnableIOThreads: h.EnableIOThreads, + Msize9p: h.msize9p(), + UseVSock: useVSock, + DisableImageNvdimm: h.DisableImageNvdimm, + HotplugVFIOOnRootBus: h.HotplugVFIOOnRootBus, + PCIeRootPort: h.PCIeRootPort, + DisableVhostNet: h.DisableVhostNet, + EnableVhostUserStore: h.EnableVhostUserStore, + VhostUserStorePath: h.vhostUserStorePath(), + GuestHookPath: h.guestHookPath(), + }, nil +} + +func newAcrnHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) { + hypervisor, err := h.path() + if err != nil { + return vc.HypervisorConfig{}, err + } + + hypervisorctl, err := h.ctlpath() + if err != nil { + return vc.HypervisorConfig{}, err + } + + kernel, err := h.kernel() + if err != nil { + return vc.HypervisorConfig{}, err + } + + image, err := h.image() + if err != nil { + return vc.HypervisorConfig{}, err + } + + if image == "" { + return vc.HypervisorConfig{}, + errors.New("image must be defined in the configuration file") + } + + firmware, err := h.firmware() + if err != nil { + return vc.HypervisorConfig{}, err + } + + kernelParams := h.kernelParams() + + blockDriver, err := h.blockDeviceDriver() + if err != nil { + return vc.HypervisorConfig{}, err + } + + return vc.HypervisorConfig{ + HypervisorPath: hypervisor, + KernelPath: kernel, + ImagePath: image, + HypervisorCtlPath: hypervisorctl, + FirmwarePath: firmware, + KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)), + NumVCPUs: h.defaultVCPUs(), + DefaultMaxVCPUs: h.defaultMaxVCPUs(), + MemorySize: h.defaultMemSz(), + MemSlots: h.defaultMemSlots(), + EntropySource: h.GetEntropySource(), + DefaultBridges: h.defaultBridges(), + HugePages: h.HugePages, + Mlock: !h.Swap, + Debug: h.Debug, + DisableNestingChecks: h.DisableNestingChecks, + BlockDeviceDriver: blockDriver, + DisableVhostNet: h.DisableVhostNet, + GuestHookPath: h.guestHookPath(), + }, nil +} + +func newClhHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) { + hypervisor, err := h.path() + if err != nil { + return vc.HypervisorConfig{}, err + } + + kernel, err := h.kernel() + if err != nil { + return vc.HypervisorConfig{}, err + } + + initrd, image, err := h.getInitrdAndImage() + if err != nil { + return vc.HypervisorConfig{}, err + } + + if initrd != "" { + return vc.HypervisorConfig{}, + errors.New("having an initrd defined in the configuration file is not supported") + } + + if image == "" { + return vc.HypervisorConfig{}, + errors.New("image must be defined in the configuration file") + } + + firmware, err := h.firmware() + if err != nil { + return vc.HypervisorConfig{}, err + } + + machineAccelerators := h.machineAccelerators() + kernelParams := h.kernelParams() + machineType := h.machineType() + + blockDriver, err := h.blockDeviceDriver() + if err != nil { + return vc.HypervisorConfig{}, err + } + + sharedFS := config.VirtioFS + + if h.VirtioFSDaemon == "" { + return vc.HypervisorConfig{}, + errors.New("virtio-fs daemon path is missing in configuration file") + } + + return vc.HypervisorConfig{ + HypervisorPath: hypervisor, + KernelPath: kernel, + InitrdPath: initrd, + ImagePath: image, + FirmwarePath: firmware, + MachineAccelerators: machineAccelerators, + KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)), + HypervisorMachineType: machineType, + NumVCPUs: h.defaultVCPUs(), + DefaultMaxVCPUs: h.defaultMaxVCPUs(), + MemorySize: h.defaultMemSz(), + MemSlots: h.defaultMemSlots(), + MemOffset: h.defaultMemOffset(), + VirtioMem: h.VirtioMem, + EntropySource: h.GetEntropySource(), + DefaultBridges: h.defaultBridges(), + DisableBlockDeviceUse: h.DisableBlockDeviceUse, + SharedFS: sharedFS, + VirtioFSDaemon: h.VirtioFSDaemon, + VirtioFSCacheSize: h.VirtioFSCacheSize, + VirtioFSCache: h.VirtioFSCache, + MemPrealloc: h.MemPrealloc, + HugePages: h.HugePages, + FileBackedMemRootDir: h.FileBackedMemRootDir, + Mlock: !h.Swap, + Debug: h.Debug, + DisableNestingChecks: h.DisableNestingChecks, + BlockDeviceDriver: blockDriver, + BlockDeviceCacheSet: h.BlockDeviceCacheSet, + BlockDeviceCacheDirect: h.BlockDeviceCacheDirect, + BlockDeviceCacheNoflush: h.BlockDeviceCacheNoflush, + EnableIOThreads: h.EnableIOThreads, + Msize9p: h.msize9p(), + HotplugVFIOOnRootBus: h.HotplugVFIOOnRootBus, + PCIeRootPort: h.PCIeRootPort, + DisableVhostNet: true, + UseVSock: true, + }, nil +} + +func newFactoryConfig(f factory) (oci.FactoryConfig, error) { + if f.TemplatePath == "" { + f.TemplatePath = defaultTemplatePath + } + if f.VMCacheEndpoint == "" { + f.VMCacheEndpoint = defaultVMCacheEndpoint + } + return oci.FactoryConfig{ + Template: f.Template, + TemplatePath: f.TemplatePath, + VMCacheNumber: f.VMCacheNumber, + VMCacheEndpoint: f.VMCacheEndpoint, + }, nil +} + +func newShimConfig(s shim) (vc.ShimConfig, error) { + path, err := s.path() + if err != nil { + return vc.ShimConfig{}, err + } + + return vc.ShimConfig{ + Path: path, + Debug: s.debug(), + Trace: s.trace(), + }, nil +} + +func updateRuntimeConfigHypervisor(configPath string, tomlConf tomlConfig, config *oci.RuntimeConfig) error { + for k, hypervisor := range tomlConf.Hypervisor { + var err error + var hConfig vc.HypervisorConfig + + switch k { + case firecrackerHypervisorTableType: + config.HypervisorType = vc.FirecrackerHypervisor + hConfig, err = newFirecrackerHypervisorConfig(hypervisor) + case qemuHypervisorTableType: + config.HypervisorType = vc.QemuHypervisor + hConfig, err = newQemuHypervisorConfig(hypervisor) + case acrnHypervisorTableType: + config.HypervisorType = vc.AcrnHypervisor + hConfig, err = newAcrnHypervisorConfig(hypervisor) + case clhHypervisorTableType: + config.HypervisorType = vc.ClhHypervisor + hConfig, err = newClhHypervisorConfig(hypervisor) + } + + if err != nil { + return fmt.Errorf("%v: %v", configPath, err) + } + + config.HypervisorConfig = hConfig + } + + return nil +} + +func updateRuntimeConfigProxy(configPath string, tomlConf tomlConfig, config *oci.RuntimeConfig, builtIn bool) error { + if builtIn { + config.ProxyType = vc.KataBuiltInProxyType + config.ProxyConfig = vc.ProxyConfig{ + Debug: config.Debug, + } + return nil + } + + for k, proxy := range tomlConf.Proxy { + switch k { + case kataProxyTableType: + config.ProxyType = vc.KataProxyType + default: + return fmt.Errorf("%s proxy type not supported", k) + } + + path, err := proxy.path() + if err != nil { + return err + } + + config.ProxyConfig = vc.ProxyConfig{ + Path: path, + Debug: proxy.debug(), + } + } + + return nil +} + +func updateRuntimeConfigAgent(configPath string, tomlConf tomlConfig, config *oci.RuntimeConfig, builtIn bool) error { + if builtIn { + var agentConfig vc.KataAgentConfig + + // If the agent config section isn't a Kata one, just default + // to everything being disabled. + agentConfig, _ = config.AgentConfig.(vc.KataAgentConfig) + + config.AgentType = vc.KataContainersAgent + config.AgentConfig = vc.KataAgentConfig{ + LongLiveConn: true, + UseVSock: config.HypervisorConfig.UseVSock, + Debug: agentConfig.Debug, + KernelModules: agentConfig.KernelModules, + } + + return nil + } + + for k, agent := range tomlConf.Agent { + switch k { + case kataAgentTableType: + config.AgentType = vc.KataContainersAgent + config.AgentConfig = vc.KataAgentConfig{ + UseVSock: config.HypervisorConfig.UseVSock, + Debug: agent.debug(), + Trace: agent.trace(), + TraceMode: agent.traceMode(), + TraceType: agent.traceType(), + KernelModules: agent.kernelModules(), + } + default: + return fmt.Errorf("%s agent type is not supported", k) + } + } + + return nil +} + +func updateRuntimeConfigShim(configPath string, tomlConf tomlConfig, config *oci.RuntimeConfig, builtIn bool) error { + if builtIn { + config.ShimType = vc.KataBuiltInShimType + config.ShimConfig = vc.ShimConfig{} + return nil + } + + for k, shim := range tomlConf.Shim { + switch k { + case kataShimTableType: + config.ShimType = vc.KataShimType + default: + return fmt.Errorf("%s shim is not supported", k) + } + + shConfig, err := newShimConfig(shim) + if err != nil { + return fmt.Errorf("%v: %v", configPath, err) + } + + config.ShimConfig = shConfig + } + + return nil +} + +// SetKernelParams adds the user-specified kernel parameters (from the +// configuration file) to the defaults so that the former take priority. +func SetKernelParams(runtimeConfig *oci.RuntimeConfig) error { + defaultKernelParams := GetKernelParamsFunc(needSystemd(runtimeConfig.HypervisorConfig), runtimeConfig.Trace) + + if runtimeConfig.HypervisorConfig.Debug { + strParams := vc.SerializeParams(defaultKernelParams, "=") + formatted := strings.Join(strParams, " ") + kataUtilsLogger.WithField("default-kernel-parameters", formatted).Debug() + } + + // retrieve the parameters specified in the config file + userKernelParams := runtimeConfig.HypervisorConfig.KernelParams + + // reset + runtimeConfig.HypervisorConfig.KernelParams = []vc.Param{} + + // first, add default values + for _, p := range defaultKernelParams { + if err := runtimeConfig.AddKernelParam(p); err != nil { + return err + } + } + + // set the scsi scan mode to none for virtio-scsi + if runtimeConfig.HypervisorConfig.BlockDeviceDriver == config.VirtioSCSI { + p := vc.Param{ + Key: "scsi_mod.scan", + Value: "none", + } + if err := runtimeConfig.AddKernelParam(p); err != nil { + return err + } + } + + // next, check for agent specific kernel params + if agentConfig, ok := runtimeConfig.AgentConfig.(vc.KataAgentConfig); ok { + err := vc.KataAgentSetDefaultTraceConfigOptions(&agentConfig) + if err != nil { + return err + } + + params := vc.KataAgentKernelParams(agentConfig) + + for _, p := range params { + if err := runtimeConfig.AddKernelParam(p); err != nil { + return err + } + } + } + + // now re-add the user-specified values so that they take priority. + for _, p := range userKernelParams { + if err := runtimeConfig.AddKernelParam(p); err != nil { + return err + } + } + + return nil +} + +func updateRuntimeConfig(configPath string, tomlConf tomlConfig, config *oci.RuntimeConfig, builtIn bool) error { + if err := updateRuntimeConfigHypervisor(configPath, tomlConf, config); err != nil { + return err + } + + if err := updateRuntimeConfigProxy(configPath, tomlConf, config, builtIn); err != nil { + return err + } + + if err := updateRuntimeConfigAgent(configPath, tomlConf, config, builtIn); err != nil { + return err + } + + if err := updateRuntimeConfigShim(configPath, tomlConf, config, builtIn); err != nil { + return err + } + + fConfig, err := newFactoryConfig(tomlConf.Factory) + if err != nil { + return fmt.Errorf("%v: %v", configPath, err) + } + config.FactoryConfig = fConfig + + config.NetmonConfig = vc.NetmonConfig{ + Path: tomlConf.Netmon.path(), + Debug: tomlConf.Netmon.debug(), + Enable: tomlConf.Netmon.enable(), + } + + err = SetKernelParams(config) + if err != nil { + return err + } + + return nil +} + +func GetDefaultHypervisorConfig() vc.HypervisorConfig { + return vc.HypervisorConfig{ + HypervisorPath: defaultHypervisorPath, + JailerPath: defaultJailerPath, + KernelPath: defaultKernelPath, + ImagePath: defaultImagePath, + InitrdPath: defaultInitrdPath, + FirmwarePath: defaultFirmwarePath, + MachineAccelerators: defaultMachineAccelerators, + HypervisorMachineType: defaultMachineType, + NumVCPUs: defaultVCPUCount, + DefaultMaxVCPUs: defaultMaxVCPUCount, + MemorySize: defaultMemSize, + MemOffset: defaultMemOffset, + VirtioMem: defaultVirtioMem, + DisableBlockDeviceUse: defaultDisableBlockDeviceUse, + DefaultBridges: defaultBridgesCount, + MemPrealloc: defaultEnableMemPrealloc, + HugePages: defaultEnableHugePages, + FileBackedMemRootDir: defaultFileBackedMemRootDir, + Mlock: !defaultEnableSwap, + Debug: defaultEnableDebug, + DisableNestingChecks: defaultDisableNestingChecks, + BlockDeviceDriver: defaultBlockDeviceDriver, + BlockDeviceCacheSet: defaultBlockDeviceCacheSet, + BlockDeviceCacheDirect: defaultBlockDeviceCacheDirect, + BlockDeviceCacheNoflush: defaultBlockDeviceCacheNoflush, + EnableIOThreads: defaultEnableIOThreads, + Msize9p: defaultMsize9p, + HotplugVFIOOnRootBus: defaultHotplugVFIOOnRootBus, + PCIeRootPort: defaultPCIeRootPort, + GuestHookPath: defaultGuestHookPath, + VhostUserStorePath: defaultVhostUserStorePath, + VirtioFSCache: defaultVirtioFSCacheMode, + DisableImageNvdimm: defaultDisableImageNvdimm, + } +} + +func initConfig() (config oci.RuntimeConfig, err error) { + var defaultAgentConfig interface{} + + err = config.InterNetworkModel.SetModel(defaultInterNetworkingModel) + if err != nil { + return oci.RuntimeConfig{}, err + } + + defaultAgentConfig = vc.KataAgentConfig{} + + config = oci.RuntimeConfig{ + HypervisorType: defaultHypervisor, + HypervisorConfig: GetDefaultHypervisorConfig(), + AgentType: defaultAgent, + AgentConfig: defaultAgentConfig, + ProxyType: defaultProxy, + ShimType: defaultShim, + } + + return config, nil +} + +// LoadConfiguration loads the configuration file and converts it into a +// runtime configuration. +// +// If ignoreLogging is true, the system logger will not be initialised nor +// will this function make any log calls. +// +// All paths are resolved fully meaning if this function does not return an +// error, all paths are valid at the time of the call. +func LoadConfiguration(configPath string, ignoreLogging, builtIn bool) (resolvedConfigPath string, config oci.RuntimeConfig, err error) { + + config, err = initConfig() + if err != nil { + return "", oci.RuntimeConfig{}, err + } + + tomlConf, resolved, err := decodeConfig(configPath) + if err != nil { + return "", oci.RuntimeConfig{}, err + } + + config.Debug = tomlConf.Runtime.Debug + if !tomlConf.Runtime.Debug { + // If debug is not required, switch back to the original + // default log priority, otherwise continue in debug mode. + kataUtilsLogger.Logger.Level = originalLoggerLevel + } + + config.Trace = tomlConf.Runtime.Tracing + tracing = config.Trace + + if tomlConf.Runtime.InterNetworkModel != "" { + err = config.InterNetworkModel.SetModel(tomlConf.Runtime.InterNetworkModel) + if err != nil { + return "", config, err + } + } + + if !ignoreLogging { + err := handleSystemLog("", "") + if err != nil { + return "", config, err + } + + kataUtilsLogger.WithFields( + logrus.Fields{ + "format": "TOML", + "file": resolved, + }).Info("loaded configuration") + } + + if err := updateRuntimeConfig(resolved, tomlConf, &config, builtIn); err != nil { + return "", config, err + } + + config.DisableGuestSeccomp = tomlConf.Runtime.DisableGuestSeccomp + + // use no proxy if HypervisorConfig.UseVSock is true + if config.HypervisorConfig.UseVSock { + kataUtilsLogger.Info("VSOCK supported, configure to not use proxy") + config.ProxyType = vc.NoProxyType + config.ProxyConfig = vc.ProxyConfig{Debug: config.Debug} + } + + config.SandboxCgroupOnly = tomlConf.Runtime.SandboxCgroupOnly + config.DisableNewNetNs = tomlConf.Runtime.DisableNewNetNs + for _, f := range tomlConf.Runtime.Experimental { + feature := exp.Get(f) + if feature == nil { + return "", config, fmt.Errorf("Unsupported experimental feature %q", f) + } + config.Experimental = append(config.Experimental, *feature) + } + + if err := checkConfig(config); err != nil { + return "", config, err + } + + return resolved, config, nil +} + +func decodeConfig(configPath string) (tomlConfig, string, error) { + var ( + resolved string + tomlConf tomlConfig + err error + ) + + if configPath == "" { + resolved, err = getDefaultConfigFile() + } else { + resolved, err = ResolvePath(configPath) + } + + if err != nil { + return tomlConf, "", fmt.Errorf("Cannot find usable config file (%v)", err) + } + + configData, err := ioutil.ReadFile(resolved) + if err != nil { + return tomlConf, resolved, err + } + + _, err = toml.Decode(string(configData), &tomlConf) + if err != nil { + return tomlConf, resolved, err + } + + return tomlConf, resolved, nil +} + +// checkConfig checks the validity of the specified config. +func checkConfig(config oci.RuntimeConfig) error { + if err := checkNetNsConfig(config); err != nil { + return err + } + + if err := checkHypervisorConfig(config.HypervisorConfig); err != nil { + return err + } + + if err := checkFactoryConfig(config); err != nil { + return err + } + + return nil +} + +// checkNetNsConfig performs sanity checks on disable_new_netns config. +// Because it is an expert option and conflicts with some other common configs. +func checkNetNsConfig(config oci.RuntimeConfig) error { + if config.DisableNewNetNs { + if config.NetmonConfig.Enable { + return fmt.Errorf("config disable_new_netns conflicts with enable_netmon") + } + if config.InterNetworkModel != vc.NetXConnectNoneModel { + return fmt.Errorf("config disable_new_netns only works with 'none' internetworking_model") + } + } else if shim, ok := config.ShimConfig.(vc.ShimConfig); ok && shim.Trace { + // Normally, the shim runs in a separate network namespace. + // But when tracing, the shim process needs to be able to talk + // to the Jaeger agent running in the host network namespace. + return errors.New("Shim tracing requires disable_new_netns for Jaeger agent communication") + } + + return nil +} + +// checkFactoryConfig ensures the VM factory configuration is valid. +func checkFactoryConfig(config oci.RuntimeConfig) error { + if config.FactoryConfig.Template { + if config.HypervisorConfig.InitrdPath == "" { + return errors.New("Factory option enable_template requires an initrd image") + } + } + + if config.FactoryConfig.VMCacheNumber > 0 { + if config.HypervisorType != vc.QemuHypervisor { + return errors.New("VM cache just support qemu") + } + if config.AgentType != vc.KataContainersAgent { + return errors.New("VM cache just support kata agent") + } + } + + return nil +} + +// checkHypervisorConfig performs basic "sanity checks" on the hypervisor +// config. +func checkHypervisorConfig(config vc.HypervisorConfig) error { + type image struct { + path string + initrd bool + } + + images := []image{ + { + path: config.ImagePath, + initrd: false, + }, + { + path: config.InitrdPath, + initrd: true, + }, + } + + memSizeMB := int64(config.MemorySize) + + if memSizeMB == 0 { + return errors.New("VM memory cannot be zero") + } + + mb := int64(1024 * 1024) + + for _, image := range images { + if image.path == "" { + continue + } + + imageSizeBytes, err := fileSize(image.path) + if err != nil { + return err + } + + if imageSizeBytes == 0 { + return fmt.Errorf("image %q is empty", image.path) + } + + if imageSizeBytes > mb { + imageSizeMB := imageSizeBytes / mb + + msg := fmt.Sprintf("VM memory (%dMB) smaller than image %q size (%dMB)", + memSizeMB, image.path, imageSizeMB) + if imageSizeMB >= memSizeMB { + if image.initrd { + // Initrd's need to be fully read into memory + return errors.New(msg) + } + + // Images do not need to be fully read + // into memory, but it would be highly + // unusual to have an image larger + // than the amount of memory assigned + // to the VM. + kataUtilsLogger.Warn(msg) + } + } + } + + return nil +} + +// GetDefaultConfigFilePaths returns a list of paths that will be +// considered as configuration files in priority order. +func GetDefaultConfigFilePaths() []string { + return []string{ + // normally below "/etc" + defaultSysConfRuntimeConfiguration, + + // normally below "/usr/share" + defaultRuntimeConfiguration, + } +} + +// getDefaultConfigFile looks in multiple default locations for a +// configuration file and returns the resolved path for the first file +// found, or an error if no config files can be found. +func getDefaultConfigFile() (string, error) { + var errs []string + + for _, file := range GetDefaultConfigFilePaths() { + resolved, err := ResolvePath(file) + if err == nil { + return resolved, nil + } + s := fmt.Sprintf("config file %q unresolvable: %v", file, err) + errs = append(errs, s) + } + + return "", errors.New(strings.Join(errs, ", ")) +} + +// SetConfigOptions will override some of the defaults settings. +func SetConfigOptions(n, runtimeConfig, sysRuntimeConfig string) { + if n != "" { + name = n + } + + if runtimeConfig != "" { + defaultRuntimeConfiguration = runtimeConfig + } + + if sysRuntimeConfig != "" { + defaultSysConfRuntimeConfiguration = sysRuntimeConfig + } +} diff --git a/src/runtime/pkg/katautils/config_test.go b/src/runtime/pkg/katautils/config_test.go new file mode 100644 index 000000000..221a4b555 --- /dev/null +++ b/src/runtime/pkg/katautils/config_test.go @@ -0,0 +1,1792 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "reflect" + goruntime "runtime" + "strings" + "syscall" + "testing" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/kata-containers/runtime/virtcontainers/utils" + "github.com/stretchr/testify/assert" +) + +var ( + hypervisorDebug = false + proxyDebug = false + runtimeDebug = false + runtimeTrace = false + shimDebug = false + netmonDebug = false + agentDebug = false + agentTrace = false +) + +type testRuntimeConfig struct { + RuntimeConfig oci.RuntimeConfig + RuntimeConfigFile string + ConfigPath string + ConfigPathLink string + LogDir string + LogPath string +} + +func createConfig(configPath string, fileData string) error { + + err := ioutil.WriteFile(configPath, []byte(fileData), testFileMode) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to create config file %s %v\n", configPath, err) + return err + } + + return nil +} + +// createAllRuntimeConfigFiles creates all files necessary to call +// loadConfiguration(). +func createAllRuntimeConfigFiles(dir, hypervisor string) (config testRuntimeConfig, err error) { + if dir == "" { + return config, fmt.Errorf("BUG: need directory") + } + + if hypervisor == "" { + return config, fmt.Errorf("BUG: need hypervisor") + } + + hypervisorPath := path.Join(dir, "hypervisor") + kernelPath := path.Join(dir, "kernel") + kernelParams := "foo=bar xyz" + imagePath := path.Join(dir, "image") + shimPath := path.Join(dir, "shim") + proxyPath := path.Join(dir, "proxy") + netmonPath := path.Join(dir, "netmon") + logDir := path.Join(dir, "logs") + logPath := path.Join(logDir, "runtime.log") + machineType := "machineType" + disableBlockDevice := true + blockDeviceDriver := "virtio-scsi" + enableIOThreads := true + hotplugVFIOOnRootBus := true + pcieRootPort := uint32(2) + disableNewNetNs := false + sharedFS := "virtio-9p" + + configFileOptions := ktu.RuntimeConfigOptions{ + Hypervisor: "qemu", + HypervisorPath: hypervisorPath, + KernelPath: kernelPath, + ImagePath: imagePath, + KernelParams: kernelParams, + MachineType: machineType, + ShimPath: shimPath, + ProxyPath: proxyPath, + NetmonPath: netmonPath, + LogPath: logPath, + DefaultGuestHookPath: defaultGuestHookPath, + DisableBlock: disableBlockDevice, + BlockDeviceDriver: blockDeviceDriver, + EnableIOThreads: enableIOThreads, + HotplugVFIOOnRootBus: hotplugVFIOOnRootBus, + PCIeRootPort: pcieRootPort, + DisableNewNetNs: disableNewNetNs, + DefaultVCPUCount: defaultVCPUCount, + DefaultMaxVCPUCount: defaultMaxVCPUCount, + DefaultMemSize: defaultMemSize, + DefaultMsize9p: defaultMsize9p, + HypervisorDebug: hypervisorDebug, + RuntimeDebug: runtimeDebug, + RuntimeTrace: runtimeTrace, + ProxyDebug: proxyDebug, + ShimDebug: shimDebug, + NetmonDebug: netmonDebug, + AgentDebug: agentDebug, + AgentTrace: agentTrace, + SharedFS: sharedFS, + } + + runtimeConfigFileData := ktu.MakeRuntimeConfigFileData(configFileOptions) + + configPath := path.Join(dir, "runtime.toml") + err = createConfig(configPath, runtimeConfigFileData) + if err != nil { + return config, err + } + + configPathLink := path.Join(filepath.Dir(configPath), "link-to-configuration.toml") + + // create a link to the config file + err = syscall.Symlink(configPath, configPathLink) + if err != nil { + return config, err + } + + files := []string{hypervisorPath, kernelPath, imagePath, shimPath, proxyPath} + + for _, file := range files { + // create the resource (which must be >0 bytes) + err := WriteFile(file, "foo", testFileMode) + if err != nil { + return config, err + } + } + + hypervisorConfig := vc.HypervisorConfig{ + HypervisorPath: hypervisorPath, + KernelPath: kernelPath, + ImagePath: imagePath, + KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)), + HypervisorMachineType: machineType, + NumVCPUs: defaultVCPUCount, + DefaultMaxVCPUs: uint32(goruntime.NumCPU()), + MemorySize: defaultMemSize, + DisableBlockDeviceUse: disableBlockDevice, + BlockDeviceDriver: defaultBlockDeviceDriver, + DefaultBridges: defaultBridgesCount, + Mlock: !defaultEnableSwap, + EnableIOThreads: enableIOThreads, + HotplugVFIOOnRootBus: hotplugVFIOOnRootBus, + PCIeRootPort: pcieRootPort, + Msize9p: defaultMsize9p, + MemSlots: defaultMemSlots, + EntropySource: defaultEntropySource, + GuestHookPath: defaultGuestHookPath, + VhostUserStorePath: defaultVhostUserStorePath, + SharedFS: sharedFS, + VirtioFSDaemon: "/path/to/virtiofsd", + VirtioFSCache: defaultVirtioFSCacheMode, + } + + agentConfig := vc.KataAgentConfig{} + + proxyConfig := vc.ProxyConfig{ + Path: proxyPath, + } + + shimConfig := vc.ShimConfig{ + Path: shimPath, + } + + netmonConfig := vc.NetmonConfig{ + Path: netmonPath, + Debug: false, + Enable: false, + } + + factoryConfig := oci.FactoryConfig{ + TemplatePath: defaultTemplatePath, + VMCacheEndpoint: defaultVMCacheEndpoint, + } + + runtimeConfig := oci.RuntimeConfig{ + HypervisorType: defaultHypervisor, + HypervisorConfig: hypervisorConfig, + + AgentType: defaultAgent, + AgentConfig: agentConfig, + + ProxyType: defaultProxy, + ProxyConfig: proxyConfig, + + ShimType: defaultShim, + ShimConfig: shimConfig, + + NetmonConfig: netmonConfig, + DisableNewNetNs: disableNewNetNs, + + FactoryConfig: factoryConfig, + } + + err = SetKernelParams(&runtimeConfig) + if err != nil { + return config, err + } + + config = testRuntimeConfig{ + RuntimeConfig: runtimeConfig, + RuntimeConfigFile: configPath, + ConfigPath: configPath, + ConfigPathLink: configPathLink, + LogDir: logDir, + LogPath: logPath, + } + + return config, nil +} + +// testLoadConfiguration accepts an optional function that can be used +// to modify the test: if a function is specified, it indicates if the +// subsequent call to loadConfiguration() is expected to fail by +// returning a bool. If the function itself fails, that is considered an +// error. +func testLoadConfiguration(t *testing.T, dir string, + fn func(config testRuntimeConfig, configFile string, ignoreLogging bool) (bool, error)) { + subDir := path.Join(dir, "test") + + for _, hypervisor := range []string{"qemu"} { + Loop: + for _, ignoreLogging := range []bool{true, false} { + err := os.RemoveAll(subDir) + assert.NoError(t, err) + + err = os.MkdirAll(subDir, testDirMode) + assert.NoError(t, err) + + testConfig, err := createAllRuntimeConfigFiles(subDir, hypervisor) + assert.NoError(t, err) + + configFiles := []string{testConfig.ConfigPath, testConfig.ConfigPathLink, ""} + + // override + defaultRuntimeConfiguration = testConfig.ConfigPath + defaultSysConfRuntimeConfiguration = "" + + for _, file := range configFiles { + var err error + expectFail := false + + if fn != nil { + expectFail, err = fn(testConfig, file, ignoreLogging) + assert.NoError(t, err) + } + + resolvedConfigPath, config, err := LoadConfiguration(file, ignoreLogging, false) + if expectFail { + assert.Error(t, err) + + // no point proceeding in the error scenario. + break Loop + } else { + assert.NoError(t, err) + } + + if file == "" { + assert.Equal(t, defaultRuntimeConfiguration, resolvedConfigPath) + } else { + assert.Equal(t, testConfig.ConfigPath, resolvedConfigPath) + } + + assert.Equal(t, defaultRuntimeConfiguration, resolvedConfigPath) + result := reflect.DeepEqual(config, testConfig.RuntimeConfig) + if !result { + t.Fatalf("Expected\n%+v\nGot\n%+v", config, testConfig.RuntimeConfig) + } + assert.True(t, result) + + err = os.RemoveAll(testConfig.LogDir) + assert.NoError(t, err) + } + } + } +} + +func TestConfigLoadConfiguration(t *testing.T) { + tmpdir, err := ioutil.TempDir(testDir, "load-config-") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testLoadConfiguration(t, tmpdir, nil) +} + +func TestConfigLoadConfigurationFailBrokenSymLink(t *testing.T) { + tmpdir, err := ioutil.TempDir(testDir, "runtime-config-") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testLoadConfiguration(t, tmpdir, + func(config testRuntimeConfig, configFile string, ignoreLogging bool) (bool, error) { + expectFail := false + + if configFile == config.ConfigPathLink { + // break the symbolic link + err = os.Remove(config.ConfigPathLink) + if err != nil { + return expectFail, err + } + + expectFail = true + } + + return expectFail, nil + }) +} + +func TestConfigLoadConfigurationFailSymLinkLoop(t *testing.T) { + tmpdir, err := ioutil.TempDir(testDir, "runtime-config-") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testLoadConfiguration(t, tmpdir, + func(config testRuntimeConfig, configFile string, ignoreLogging bool) (bool, error) { + expectFail := false + + if configFile == config.ConfigPathLink { + // remove the config file + err = os.Remove(config.ConfigPath) + if err != nil { + return expectFail, err + } + + // now, create a sym-link loop + err := os.Symlink(config.ConfigPathLink, config.ConfigPath) + if err != nil { + return expectFail, err + } + + expectFail = true + } + + return expectFail, nil + }) +} + +func TestConfigLoadConfigurationFailMissingHypervisor(t *testing.T) { + tmpdir, err := ioutil.TempDir(testDir, "runtime-config-") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testLoadConfiguration(t, tmpdir, + func(config testRuntimeConfig, configFile string, ignoreLogging bool) (bool, error) { + expectFail := true + + err = os.Remove(config.RuntimeConfig.HypervisorConfig.HypervisorPath) + if err != nil { + return expectFail, err + } + + return expectFail, nil + }) +} + +func TestConfigLoadConfigurationFailMissingImage(t *testing.T) { + tmpdir, err := ioutil.TempDir(testDir, "runtime-config-") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testLoadConfiguration(t, tmpdir, + func(config testRuntimeConfig, configFile string, ignoreLogging bool) (bool, error) { + expectFail := true + + err = os.Remove(config.RuntimeConfig.HypervisorConfig.ImagePath) + if err != nil { + return expectFail, err + } + + return expectFail, nil + }) +} + +func TestConfigLoadConfigurationFailMissingKernel(t *testing.T) { + tmpdir, err := ioutil.TempDir(testDir, "runtime-config-") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testLoadConfiguration(t, tmpdir, + func(config testRuntimeConfig, configFile string, ignoreLogging bool) (bool, error) { + expectFail := true + + err = os.Remove(config.RuntimeConfig.HypervisorConfig.KernelPath) + if err != nil { + return expectFail, err + } + + return expectFail, nil + }) +} + +func TestConfigLoadConfigurationFailMissingShim(t *testing.T) { + tmpdir, err := ioutil.TempDir(testDir, "runtime-config-") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testLoadConfiguration(t, tmpdir, + func(config testRuntimeConfig, configFile string, ignoreLogging bool) (bool, error) { + expectFail := true + + shimConfig, ok := config.RuntimeConfig.ShimConfig.(vc.ShimConfig) + if !ok { + return expectFail, fmt.Errorf("cannot determine shim config") + } + err = os.Remove(shimConfig.Path) + if err != nil { + return expectFail, err + } + + return expectFail, nil + }) +} + +func TestConfigLoadConfigurationFailUnreadableConfig(t *testing.T) { + if tc.NotValid(ktu.NeedNonRoot()) { + t.Skip(ktu.TestDisabledNeedNonRoot) + } + + tmpdir, err := ioutil.TempDir(testDir, "runtime-config-") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testLoadConfiguration(t, tmpdir, + func(config testRuntimeConfig, configFile string, ignoreLogging bool) (bool, error) { + expectFail := true + + // make file unreadable by non-root user + err = os.Chmod(config.ConfigPath, 0000) + if err != nil { + return expectFail, err + } + + return expectFail, nil + }) +} + +func TestConfigLoadConfigurationFailTOMLConfigFileInvalidContents(t *testing.T) { + if tc.NotValid(ktu.NeedNonRoot()) { + t.Skip(ktu.TestDisabledNeedNonRoot) + } + + tmpdir, err := ioutil.TempDir(testDir, "runtime-config-") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testLoadConfiguration(t, tmpdir, + func(config testRuntimeConfig, configFile string, ignoreLogging bool) (bool, error) { + expectFail := true + + err := createFile(config.ConfigPath, + ` + I am not TOML! ;-) + I am invalid XML!`) + + if err != nil { + return expectFail, err + } + + return expectFail, nil + }) +} + +func TestConfigLoadConfigurationFailTOMLConfigFileDuplicatedData(t *testing.T) { + if tc.NotValid(ktu.NeedNonRoot()) { + t.Skip(ktu.TestDisabledNeedNonRoot) + } + + tmpdir, err := ioutil.TempDir(testDir, "runtime-config-") + assert.NoError(t, err) + defer os.RemoveAll(tmpdir) + + testLoadConfiguration(t, tmpdir, + func(config testRuntimeConfig, configFile string, ignoreLogging bool) (bool, error) { + expectFail := true + + text, err := GetFileContents(config.ConfigPath) + if err != nil { + return expectFail, err + } + + // create a config file containing two sets of + // data. + err = createFile(config.ConfigPath, fmt.Sprintf("%s\n%s\n", text, text)) + if err != nil { + return expectFail, err + } + + return expectFail, nil + }) +} + +func TestMinimalRuntimeConfig(t *testing.T) { + dir, err := ioutil.TempDir(testDir, "minimal-runtime-config-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + shimPath := path.Join(dir, "shim") + proxyPath := path.Join(dir, "proxy") + hypervisorPath := path.Join(dir, "hypervisor") + defaultHypervisorPath = hypervisorPath + jailerPath := path.Join(dir, "jailer") + defaultJailerPath = jailerPath + netmonPath := path.Join(dir, "netmon") + + imagePath := path.Join(dir, "image.img") + initrdPath := path.Join(dir, "initrd.img") + + kernelPath := path.Join(dir, "kernel") + + savedDefaultImagePath := defaultImagePath + savedDefaultInitrdPath := defaultInitrdPath + savedDefaultHypervisorPath := defaultHypervisorPath + savedDefaultJailerPath := defaultJailerPath + savedDefaultKernelPath := defaultKernelPath + + defer func() { + defaultImagePath = savedDefaultImagePath + defaultInitrdPath = savedDefaultInitrdPath + defaultHypervisorPath = savedDefaultHypervisorPath + defaultJailerPath = savedDefaultJailerPath + defaultKernelPath = savedDefaultKernelPath + }() + + // Temporarily change the defaults to avoid this test using the real + // resource files that might be installed on the system! + defaultImagePath = imagePath + defaultInitrdPath = initrdPath + defaultHypervisorPath = hypervisorPath + defaultJailerPath = jailerPath + defaultKernelPath = kernelPath + + for _, file := range []string{defaultImagePath, defaultInitrdPath, defaultHypervisorPath, defaultJailerPath, defaultKernelPath} { + err = WriteFile(file, "foo", testFileMode) + if err != nil { + t.Fatal(err) + } + } + + runtimeMinimalConfig := ` + # Runtime configuration file + + [proxy.kata] + path = "` + proxyPath + `" + + [shim.kata] + path = "` + shimPath + `" + + [agent.kata] + + [netmon] + path = "` + netmonPath + `" +` + + configPath := path.Join(dir, "runtime.toml") + err = createConfig(configPath, runtimeMinimalConfig) + if err != nil { + t.Fatal(err) + } + + _, config, err := LoadConfiguration(configPath, false, false) + if err == nil { + t.Fatalf("Expected loadConfiguration to fail as shim path does not exist: %+v", config) + } + + err = createEmptyFile(shimPath) + if err != nil { + t.Error(err) + } + + err = createEmptyFile(proxyPath) + if err != nil { + t.Error(err) + } + + err = createEmptyFile(hypervisorPath) + if err != nil { + t.Error(err) + } + + err = createEmptyFile(jailerPath) + if err != nil { + t.Error(err) + } + + err = createEmptyFile(netmonPath) + if err != nil { + t.Error(err) + } + + _, config, err = LoadConfiguration(configPath, false, false) + if err != nil { + t.Fatal(err) + } + + expectedHypervisorConfig := vc.HypervisorConfig{ + HypervisorPath: defaultHypervisorPath, + JailerPath: defaultJailerPath, + KernelPath: defaultKernelPath, + ImagePath: defaultImagePath, + InitrdPath: defaultInitrdPath, + HypervisorMachineType: defaultMachineType, + NumVCPUs: defaultVCPUCount, + DefaultMaxVCPUs: defaultMaxVCPUCount, + MemorySize: defaultMemSize, + DisableBlockDeviceUse: defaultDisableBlockDeviceUse, + DefaultBridges: defaultBridgesCount, + Mlock: !defaultEnableSwap, + BlockDeviceDriver: defaultBlockDeviceDriver, + Msize9p: defaultMsize9p, + GuestHookPath: defaultGuestHookPath, + VhostUserStorePath: defaultVhostUserStorePath, + VirtioFSCache: defaultVirtioFSCacheMode, + } + + expectedAgentConfig := vc.KataAgentConfig{} + + expectedProxyConfig := vc.ProxyConfig{ + Path: proxyPath, + } + + expectedShimConfig := vc.ShimConfig{ + Path: shimPath, + } + + expectedNetmonConfig := vc.NetmonConfig{ + Path: netmonPath, + Debug: false, + Enable: false, + } + + expectedFactoryConfig := oci.FactoryConfig{ + TemplatePath: defaultTemplatePath, + VMCacheEndpoint: defaultVMCacheEndpoint, + } + + expectedConfig := oci.RuntimeConfig{ + HypervisorType: defaultHypervisor, + HypervisorConfig: expectedHypervisorConfig, + + AgentType: defaultAgent, + AgentConfig: expectedAgentConfig, + + ProxyType: defaultProxy, + ProxyConfig: expectedProxyConfig, + + ShimType: defaultShim, + ShimConfig: expectedShimConfig, + + NetmonConfig: expectedNetmonConfig, + + FactoryConfig: expectedFactoryConfig, + } + err = SetKernelParams(&expectedConfig) + if err != nil { + t.Fatal(err) + } + + if reflect.DeepEqual(config, expectedConfig) == false { + t.Fatalf("Got %+v\n expecting %+v", config, expectedConfig) + } +} + +func TestMinimalRuntimeConfigWithVsock(t *testing.T) { + dir, err := ioutil.TempDir(testDir, "minimal-runtime-config-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + imagePath := path.Join(dir, "image.img") + initrdPath := path.Join(dir, "initrd.img") + proxyPath := path.Join(dir, "proxy") + shimPath := path.Join(dir, "shim") + hypervisorPath := path.Join(dir, "hypervisor") + kernelPath := path.Join(dir, "kernel") + + savedDefaultImagePath := defaultImagePath + savedDefaultInitrdPath := defaultInitrdPath + savedDefaultHypervisorPath := defaultHypervisorPath + savedDefaultKernelPath := defaultKernelPath + + defer func() { + defaultImagePath = savedDefaultImagePath + defaultInitrdPath = savedDefaultInitrdPath + defaultHypervisorPath = savedDefaultHypervisorPath + defaultKernelPath = savedDefaultKernelPath + }() + + // Temporarily change the defaults to avoid this test using the real + // resource files that might be installed on the system! + defaultImagePath = imagePath + defaultInitrdPath = initrdPath + defaultHypervisorPath = hypervisorPath + defaultKernelPath = kernelPath + + for _, file := range []string{proxyPath, shimPath, hypervisorPath, kernelPath, imagePath} { + err = WriteFile(file, "foo", testFileMode) + if err != nil { + t.Fatal(err) + } + } + + // minimal config with vsock enabled + runtimeMinimalConfig := ` + # Runtime configuration file + [hypervisor.qemu] + use_vsock = true + image = "` + imagePath + `" + + [proxy.kata] + path = "` + proxyPath + `" + + [shim.kata] + path = "` + shimPath + `" + + [agent.kata] +` + orgVHostVSockDevicePath := utils.VHostVSockDevicePath + defer func() { + utils.VHostVSockDevicePath = orgVHostVSockDevicePath + }() + utils.VHostVSockDevicePath = "/dev/null" + + configPath := path.Join(dir, "runtime.toml") + err = createConfig(configPath, runtimeMinimalConfig) + if err != nil { + t.Fatal(err) + } + + _, config, err := LoadConfiguration(configPath, false, false) + if err != nil { + t.Fatal(err) + } + + if config.ProxyType != vc.NoProxyType { + t.Fatalf("Proxy type must be NoProxy, got %+v", config.ProxyType) + } + + if !reflect.DeepEqual(config.ProxyConfig, vc.ProxyConfig{}) { + t.Fatalf("Got %+v\n expecting %+v", config.ProxyConfig, vc.ProxyConfig{}) + } + + if config.HypervisorConfig.UseVSock != true { + t.Fatalf("use_vsock must be true, got %v", config.HypervisorConfig.UseVSock) + } +} + +func TestNewQemuHypervisorConfig(t *testing.T) { + dir, err := ioutil.TempDir(testDir, "hypervisor-config-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + hypervisorPath := path.Join(dir, "hypervisor") + kernelPath := path.Join(dir, "kernel") + imagePath := path.Join(dir, "image") + machineType := "machineType" + disableBlock := true + enableIOThreads := true + hotplugVFIOOnRootBus := true + pcieRootPort := uint32(2) + orgVHostVSockDevicePath := utils.VHostVSockDevicePath + defer func() { + utils.VHostVSockDevicePath = orgVHostVSockDevicePath + }() + utils.VHostVSockDevicePath = "/dev/abc/xyz" + + hypervisor := hypervisor{ + Path: hypervisorPath, + Kernel: kernelPath, + Image: imagePath, + MachineType: machineType, + DisableBlockDeviceUse: disableBlock, + EnableIOThreads: enableIOThreads, + HotplugVFIOOnRootBus: hotplugVFIOOnRootBus, + PCIeRootPort: pcieRootPort, + UseVSock: true, + } + + files := []string{hypervisorPath, kernelPath, imagePath} + filesLen := len(files) + + for i, file := range files { + _, err := newQemuHypervisorConfig(hypervisor) + if err == nil { + t.Fatalf("Expected newQemuHypervisorConfig to fail as not all paths exist (not created %v)", + strings.Join(files[i:filesLen], ",")) + } + + // create the resource + err = createEmptyFile(file) + if err != nil { + t.Error(err) + } + } + + // falling back to legacy serial port + config, err := newQemuHypervisorConfig(hypervisor) + if err != nil { + t.Fatal(err) + } + + utils.VHostVSockDevicePath = "/dev/null" + + // all paths exist now + config, err = newQemuHypervisorConfig(hypervisor) + if err != nil { + t.Fatal(err) + } + + if config.HypervisorPath != hypervisor.Path { + t.Errorf("Expected hypervisor path %v, got %v", hypervisor.Path, config.HypervisorPath) + } + + if config.KernelPath != hypervisor.Kernel { + t.Errorf("Expected kernel path %v, got %v", hypervisor.Kernel, config.KernelPath) + } + + if config.ImagePath != hypervisor.Image { + t.Errorf("Expected image path %v, got %v", hypervisor.Image, config.ImagePath) + } + + if config.DisableBlockDeviceUse != disableBlock { + t.Errorf("Expected value for disable block usage %v, got %v", disableBlock, config.DisableBlockDeviceUse) + } + + if config.EnableIOThreads != enableIOThreads { + t.Errorf("Expected value for enable IOThreads %v, got %v", enableIOThreads, config.EnableIOThreads) + } + + if config.HotplugVFIOOnRootBus != hotplugVFIOOnRootBus { + t.Errorf("Expected value for HotplugVFIOOnRootBus %v, got %v", hotplugVFIOOnRootBus, config.HotplugVFIOOnRootBus) + } + + if config.PCIeRootPort != pcieRootPort { + t.Errorf("Expected value for PCIeRootPort %v, got %v", pcieRootPort, config.PCIeRootPort) + } +} + +func TestNewQemuHypervisorConfigImageAndInitrd(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + imagePath := filepath.Join(tmpdir, "image") + initrdPath := filepath.Join(tmpdir, "initrd") + hypervisorPath := path.Join(tmpdir, "hypervisor") + kernelPath := path.Join(tmpdir, "kernel") + + for _, file := range []string{imagePath, initrdPath, hypervisorPath, kernelPath} { + err = createEmptyFile(file) + assert.NoError(err) + } + + machineType := "machineType" + disableBlock := true + enableIOThreads := true + hotplugVFIOOnRootBus := true + pcieRootPort := uint32(2) + + hypervisor := hypervisor{ + Path: hypervisorPath, + Kernel: kernelPath, + Image: imagePath, + Initrd: initrdPath, + MachineType: machineType, + DisableBlockDeviceUse: disableBlock, + EnableIOThreads: enableIOThreads, + HotplugVFIOOnRootBus: hotplugVFIOOnRootBus, + PCIeRootPort: pcieRootPort, + } + + _, err = newQemuHypervisorConfig(hypervisor) + + // specifying both an image+initrd is invalid + assert.Error(err) +} + +func TestNewClhHypervisorConfig(t *testing.T) { + + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + hypervisorPath := path.Join(tmpdir, "hypervisor") + kernelPath := path.Join(tmpdir, "kernel") + imagePath := path.Join(tmpdir, "image") + virtioFsDaemon := path.Join(tmpdir, "virtiofsd") + + for _, file := range []string{imagePath, hypervisorPath, kernelPath, virtioFsDaemon} { + err = createEmptyFile(file) + assert.NoError(err) + } + + hypervisor := hypervisor{ + Path: hypervisorPath, + Kernel: kernelPath, + Image: imagePath, + VirtioFSDaemon: virtioFsDaemon, + VirtioFSCache: "always", + } + config, err := newClhHypervisorConfig(hypervisor) + if err != nil { + t.Fatal(err) + } + + if config.HypervisorPath != hypervisor.Path { + t.Errorf("Expected hypervisor path %v, got %v", hypervisor.Path, config.HypervisorPath) + } + + if config.KernelPath != hypervisor.Kernel { + t.Errorf("Expected kernel path %v, got %v", hypervisor.Kernel, config.KernelPath) + } + + if config.ImagePath != hypervisor.Image { + t.Errorf("Expected image path %v, got %v", hypervisor.Image, config.ImagePath) + } + + if config.ImagePath != hypervisor.Image { + t.Errorf("Expected image path %v, got %v", hypervisor.Image, config.ImagePath) + } + + if config.UseVSock != true { + t.Errorf("Expected UseVSock %v, got %v", true, config.UseVSock) + } + + if config.DisableVhostNet != true { + t.Errorf("Expected DisableVhostNet %v, got %v", true, config.DisableVhostNet) + } + + if config.VirtioFSCache != "always" { + t.Errorf("Expected VirtioFSCache %v, got %v", true, config.VirtioFSCache) + } + +} + +func TestNewShimConfig(t *testing.T) { + dir, err := ioutil.TempDir(testDir, "shim-config-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + shimPath := path.Join(dir, "shim") + + shim := shim{ + Path: shimPath, + } + + _, err = newShimConfig(shim) + if err == nil { + t.Fatalf("Expected newShimConfig to fail as no paths exist") + } + + err = createEmptyFile(shimPath) + if err != nil { + t.Error(err) + } + + shConfig, err := newShimConfig(shim) + if err != nil { + t.Fatalf("newShimConfig failed unexpectedly: %v", err) + } + + if shConfig.Path != shimPath { + t.Errorf("Expected shim path %v, got %v", shimPath, shConfig.Path) + } +} + +func TestHypervisorDefaults(t *testing.T) { + assert := assert.New(t) + + numCPUs := goruntime.NumCPU() + + h := hypervisor{} + + assert.Equal(h.machineType(), defaultMachineType, "default hypervisor machine type wrong") + assert.Equal(h.defaultVCPUs(), defaultVCPUCount, "default vCPU number is wrong") + assert.Equal(h.defaultMaxVCPUs(), uint32(numCPUs), "default max vCPU number is wrong") + assert.Equal(h.defaultMemSz(), defaultMemSize, "default memory size is wrong") + + machineType := "foo" + h.MachineType = machineType + assert.Equal(h.machineType(), machineType, "custom hypervisor machine type wrong") + + // auto inferring + h.NumVCPUs = -1 + assert.Equal(h.defaultVCPUs(), uint32(numCPUs), "default vCPU number is wrong") + + h.NumVCPUs = 2 + assert.Equal(h.defaultVCPUs(), uint32(2), "default vCPU number is wrong") + + h.NumVCPUs = int32(numCPUs) + 1 + assert.Equal(h.defaultVCPUs(), uint32(numCPUs), "default vCPU number is wrong") + + h.DefaultMaxVCPUs = 2 + assert.Equal(h.defaultMaxVCPUs(), uint32(2), "default max vCPU number is wrong") + + h.DefaultMaxVCPUs = uint32(numCPUs) + 1 + assert.Equal(h.defaultMaxVCPUs(), uint32(numCPUs), "default max vCPU number is wrong") + + maxvcpus := vc.MaxQemuVCPUs() + h.DefaultMaxVCPUs = maxvcpus + 1 + assert.Equal(h.defaultMaxVCPUs(), uint32(numCPUs), "default max vCPU number is wrong") + + h.MemorySize = 1024 + assert.Equal(h.defaultMemSz(), uint32(1024), "default memory size is wrong") +} + +func TestHypervisorDefaultsHypervisor(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + testHypervisorPath := filepath.Join(tmpdir, "hypervisor") + testHypervisorLinkPath := filepath.Join(tmpdir, "hypervisor-link") + + err = createEmptyFile(testHypervisorPath) + assert.NoError(err) + + err = syscall.Symlink(testHypervisorPath, testHypervisorLinkPath) + assert.NoError(err) + + savedHypervisorPath := defaultHypervisorPath + + defer func() { + defaultHypervisorPath = savedHypervisorPath + }() + + defaultHypervisorPath = testHypervisorPath + h := hypervisor{} + p, err := h.path() + assert.NoError(err) + assert.Equal(p, defaultHypervisorPath, "default hypervisor path wrong") + + // test path resolution + defaultHypervisorPath = testHypervisorLinkPath + h = hypervisor{} + p, err = h.path() + assert.NoError(err) + assert.Equal(p, testHypervisorPath) +} + +func TestHypervisorDefaultsKernel(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + testKernelPath := filepath.Join(tmpdir, "kernel") + testKernelLinkPath := filepath.Join(tmpdir, "kernel-link") + + err = createEmptyFile(testKernelPath) + assert.NoError(err) + + err = syscall.Symlink(testKernelPath, testKernelLinkPath) + assert.NoError(err) + + savedKernelPath := defaultKernelPath + + defer func() { + defaultKernelPath = savedKernelPath + }() + + defaultKernelPath = testKernelPath + + h := hypervisor{} + p, err := h.kernel() + assert.NoError(err) + assert.Equal(p, defaultKernelPath, "default Kernel path wrong") + + // test path resolution + defaultKernelPath = testKernelLinkPath + h = hypervisor{} + p, err = h.kernel() + assert.NoError(err) + assert.Equal(p, testKernelPath) + + assert.Equal(h.kernelParams(), defaultKernelParams, "default hypervisor image wrong") + kernelParams := "foo=bar xyz" + h.KernelParams = kernelParams + assert.Equal(h.kernelParams(), kernelParams, "custom hypervisor kernel parameterms wrong") +} + +// The default initrd path is not returned by h.initrd() +func TestHypervisorDefaultsInitrd(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + testInitrdPath := filepath.Join(tmpdir, "initrd") + testInitrdLinkPath := filepath.Join(tmpdir, "initrd-link") + + err = createEmptyFile(testInitrdPath) + assert.NoError(err) + + err = syscall.Symlink(testInitrdPath, testInitrdLinkPath) + assert.NoError(err) + + savedInitrdPath := defaultInitrdPath + + defer func() { + defaultInitrdPath = savedInitrdPath + }() + + defaultInitrdPath = testInitrdPath + h := hypervisor{} + p, err := h.initrd() + assert.Error(err) + assert.Equal(p, "", "default Image path wrong") + + // test path resolution + defaultInitrdPath = testInitrdLinkPath + h = hypervisor{} + p, err = h.initrd() + assert.Error(err) + assert.Equal(p, "") +} + +// The default image path is not returned by h.image() +func TestHypervisorDefaultsImage(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + testImagePath := filepath.Join(tmpdir, "image") + testImageLinkPath := filepath.Join(tmpdir, "image-link") + + err = createEmptyFile(testImagePath) + assert.NoError(err) + + err = syscall.Symlink(testImagePath, testImageLinkPath) + assert.NoError(err) + + savedImagePath := defaultImagePath + + defer func() { + defaultImagePath = savedImagePath + }() + + defaultImagePath = testImagePath + h := hypervisor{} + p, err := h.image() + assert.Error(err) + assert.Equal(p, "", "default Image path wrong") + + // test path resolution + defaultImagePath = testImageLinkPath + h = hypervisor{} + p, err = h.image() + assert.Error(err) + assert.Equal(p, "") +} + +func TestHypervisorDefaultsGuestHookPath(t *testing.T) { + assert := assert.New(t) + + h := hypervisor{} + guestHookPath := h.guestHookPath() + assert.Equal(guestHookPath, defaultGuestHookPath, "default guest hook path wrong") + + testGuestHookPath := "/test/guest/hook/path" + h = hypervisor{ + GuestHookPath: testGuestHookPath, + } + guestHookPath = h.guestHookPath() + assert.Equal(guestHookPath, testGuestHookPath, "custom guest hook path wrong") +} + +func TestHypervisorDefaultsVhostUserStorePath(t *testing.T) { + assert := assert.New(t) + + h := hypervisor{} + vhostUserStorePath := h.vhostUserStorePath() + assert.Equal(vhostUserStorePath, defaultVhostUserStorePath, "default vhost-user store path wrong") + + testVhostUserStorePath := "/test/vhost/user/store/path" + h = hypervisor{ + VhostUserStorePath: testVhostUserStorePath, + } + vhostUserStorePath = h.vhostUserStorePath() + assert.Equal(vhostUserStorePath, testVhostUserStorePath, "custom vhost-user store path wrong") +} + +func TestProxyDefaults(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + testProxyPath := filepath.Join(tmpdir, "proxy") + testProxyLinkPath := filepath.Join(tmpdir, "proxy-link") + + err = createEmptyFile(testProxyPath) + assert.NoError(err) + + err = syscall.Symlink(testProxyPath, testProxyLinkPath) + assert.NoError(err) + + savedProxyPath := defaultProxyPath + + defer func() { + defaultProxyPath = savedProxyPath + }() + + defaultProxyPath = testProxyPath + p := proxy{} + path, err := p.path() + assert.NoError(err) + assert.Equal(path, defaultProxyPath, "default proxy path wrong") + + // test path resolution + defaultProxyPath = testProxyLinkPath + p = proxy{} + path, err = p.path() + assert.NoError(err) + assert.Equal(path, testProxyPath) + + assert.False(p.debug()) + p.Debug = true + assert.True(p.debug()) +} + +func TestShimDefaults(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + testShimPath := filepath.Join(tmpdir, "shim") + testShimLinkPath := filepath.Join(tmpdir, "shim-link") + + err = createEmptyFile(testShimPath) + assert.NoError(err) + + err = syscall.Symlink(testShimPath, testShimLinkPath) + assert.NoError(err) + + savedShimPath := defaultShimPath + + defer func() { + defaultShimPath = savedShimPath + }() + + defaultShimPath = testShimPath + s := shim{} + p, err := s.path() + assert.NoError(err) + assert.Equal(p, defaultShimPath, "default shim path wrong") + + // test path resolution + defaultShimPath = testShimLinkPath + s = shim{} + p, err = s.path() + assert.NoError(err) + assert.Equal(p, testShimPath) + + assert.False(s.debug()) + s.Debug = true + assert.True(s.debug()) + + assert.False(s.trace()) + s.Tracing = true + assert.True(s.trace()) +} + +func TestAgentDefaults(t *testing.T) { + assert := assert.New(t) + + a := agent{} + + assert.Equal(a.debug(), a.Debug) + + a.Debug = true + assert.Equal(a.debug(), a.Debug) + + assert.Equal(a.trace(), a.Tracing) + + a.Tracing = true + assert.Equal(a.trace(), a.Tracing) + + assert.Equal(a.traceMode(), a.TraceMode) + assert.Equal(a.traceType(), a.TraceType) +} + +func TestGetDefaultConfigFilePaths(t *testing.T) { + assert := assert.New(t) + + results := GetDefaultConfigFilePaths() + // There should be atleast two config file locations + assert.True(len(results) >= 2) + + for _, f := range results { + // Paths cannot be empty + assert.NotNil(f) + } +} + +func TestGetDefaultConfigFile(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + hypervisor := "qemu" + confDir := filepath.Join(tmpdir, "conf") + sysConfDir := filepath.Join(tmpdir, "sysconf") + + for _, dir := range []string{confDir, sysConfDir} { + err = os.MkdirAll(dir, testDirMode) + assert.NoError(err) + } + + confDirConfig, err := createAllRuntimeConfigFiles(confDir, hypervisor) + assert.NoError(err) + + sysConfDirConfig, err := createAllRuntimeConfigFiles(sysConfDir, hypervisor) + assert.NoError(err) + + savedConf := defaultRuntimeConfiguration + savedSysConf := defaultSysConfRuntimeConfiguration + + defaultRuntimeConfiguration = confDirConfig.ConfigPath + defaultSysConfRuntimeConfiguration = sysConfDirConfig.ConfigPath + + defer func() { + defaultRuntimeConfiguration = savedConf + defaultSysConfRuntimeConfiguration = savedSysConf + + }() + + got, err := getDefaultConfigFile() + assert.NoError(err) + // defaultSysConfRuntimeConfiguration has priority over defaultRuntimeConfiguration + assert.Equal(got, defaultSysConfRuntimeConfiguration) + + // force defaultRuntimeConfiguration to be returned + os.Remove(defaultSysConfRuntimeConfiguration) + + got, err = getDefaultConfigFile() + assert.NoError(err) + assert.Equal(got, defaultRuntimeConfiguration) + + // force error + os.Remove(defaultRuntimeConfiguration) + + _, err = getDefaultConfigFile() + assert.Error(err) +} + +func TestDefaultBridges(t *testing.T) { + assert := assert.New(t) + + h := hypervisor{DefaultBridges: 0} + + bridges := h.defaultBridges() + assert.Equal(defaultBridgesCount, bridges) + + h.DefaultBridges = maxPCIBridges + 1 + bridges = h.defaultBridges() + assert.Equal(maxPCIBridges, bridges) + + h.DefaultBridges = maxPCIBridges + bridges = h.defaultBridges() + assert.Equal(maxPCIBridges, bridges) +} + +func TestDefaultVirtioFSCache(t *testing.T) { + assert := assert.New(t) + + h := hypervisor{VirtioFSCache: ""} + + cache := h.defaultVirtioFSCache() + assert.Equal(defaultVirtioFSCacheMode, cache) + + h.VirtioFSCache = "always" + cache = h.defaultVirtioFSCache() + assert.Equal("always", cache) + + h.VirtioFSCache = "none" + cache = h.defaultVirtioFSCache() + assert.Equal("none", cache) +} + +func TestDefaultFirmware(t *testing.T) { + assert := assert.New(t) + + // save default firmware path + oldDefaultFirmwarePath := defaultFirmwarePath + + f, err := ioutil.TempFile(os.TempDir(), "qboot.bin") + assert.NoError(err) + assert.NoError(f.Close()) + defer os.RemoveAll(f.Name()) + + h := hypervisor{} + defaultFirmwarePath = "" + p, err := h.firmware() + assert.NoError(err) + assert.Empty(p) + + defaultFirmwarePath = f.Name() + p, err = h.firmware() + assert.NoError(err) + assert.NotEmpty(p) + + // restore default firmware path + defaultFirmwarePath = oldDefaultFirmwarePath +} + +func TestDefaultMachineAccelerators(t *testing.T) { + assert := assert.New(t) + machineAccelerators := "abc,123,rgb" + h := hypervisor{MachineAccelerators: machineAccelerators} + assert.Equal(machineAccelerators, h.machineAccelerators()) + + machineAccelerators = "" + h.MachineAccelerators = machineAccelerators + assert.Equal(machineAccelerators, h.machineAccelerators()) + + machineAccelerators = "abc" + h.MachineAccelerators = machineAccelerators + assert.Equal(machineAccelerators, h.machineAccelerators()) + + machineAccelerators = "abc,123" + h.MachineAccelerators = "abc,,123" + assert.Equal(machineAccelerators, h.machineAccelerators()) + + machineAccelerators = "abc,123" + h.MachineAccelerators = ",,abc,,123,,," + assert.Equal(machineAccelerators, h.machineAccelerators()) + + machineAccelerators = "abc,123" + h.MachineAccelerators = "abc,,123,,," + assert.Equal(machineAccelerators, h.machineAccelerators()) + + machineAccelerators = "abc" + h.MachineAccelerators = ",,abc," + assert.Equal(machineAccelerators, h.machineAccelerators()) + + machineAccelerators = "abc" + h.MachineAccelerators = ", , abc , ," + assert.Equal(machineAccelerators, h.machineAccelerators()) + + machineAccelerators = "abc" + h.MachineAccelerators = " abc " + assert.Equal(machineAccelerators, h.machineAccelerators()) + + machineAccelerators = "abc,123" + h.MachineAccelerators = ", abc , 123 ," + assert.Equal(machineAccelerators, h.machineAccelerators()) + + machineAccelerators = "abc,123" + h.MachineAccelerators = ",, abc ,,, 123 ,," + assert.Equal(machineAccelerators, h.machineAccelerators()) +} + +func TestUpdateRuntimeConfiguration(t *testing.T) { + assert := assert.New(t) + + assert.Equal(defaultAgent, vc.KataContainersAgent) + + config := oci.RuntimeConfig{} + + tomlConf := tomlConfig{ + Agent: map[string]agent{ + // force a non-default value + kataAgentTableType: {}, + }, + } + + assert.NotEqual(config.AgentType, vc.AgentType(kataAgentTableType)) + assert.NotEqual(config.AgentConfig, vc.KataAgentConfig{}) + + err := updateRuntimeConfig("", tomlConf, &config, false) + assert.NoError(err) + + assert.Equal(config.AgentType, vc.AgentType(kataAgentTableType)) + assert.Equal(config.AgentConfig, vc.KataAgentConfig{}) +} + +func TestUpdateRuntimeConfigurationVMConfig(t *testing.T) { + assert := assert.New(t) + + vcpus := uint(2) + mem := uint32(2048) + + config := oci.RuntimeConfig{} + expectedVMConfig := mem + + tomlConf := tomlConfig{ + Hypervisor: map[string]hypervisor{ + qemuHypervisorTableType: { + NumVCPUs: int32(vcpus), + MemorySize: mem, + Path: "/", + Kernel: "/", + Image: "/", + Firmware: "/", + }, + }, + } + + err := updateRuntimeConfig("", tomlConf, &config, false) + assert.NoError(err) + + assert.Equal(expectedVMConfig, config.HypervisorConfig.MemorySize) +} + +func TestUpdateRuntimeConfigurationFactoryConfig(t *testing.T) { + assert := assert.New(t) + + config := oci.RuntimeConfig{} + expectedFactoryConfig := oci.FactoryConfig{ + Template: true, + TemplatePath: defaultTemplatePath, + VMCacheEndpoint: defaultVMCacheEndpoint, + } + + tomlConf := tomlConfig{Factory: factory{Template: true}} + + err := updateRuntimeConfig("", tomlConf, &config, false) + assert.NoError(err) + + assert.Equal(expectedFactoryConfig, config.FactoryConfig) +} + +func TestUpdateRuntimeConfigurationInvalidKernelParams(t *testing.T) { + assert := assert.New(t) + + assert.Equal(defaultAgent, vc.KataContainersAgent) + + config := oci.RuntimeConfig{} + + tomlConf := tomlConfig{} + + savedFunc := GetKernelParamsFunc + defer func() { + GetKernelParamsFunc = savedFunc + }() + + GetKernelParamsFunc = func(needSystemd, trace bool) []vc.Param { + return []vc.Param{ + { + Key: "", + Value: "", + }, + } + } + + err := updateRuntimeConfig("", tomlConf, &config, false) + assert.EqualError(err, "Empty kernel parameter") +} + +func TestCheckHypervisorConfig(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir(testDir, "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + // Not created on purpose + imageENOENT := filepath.Join(dir, "image-ENOENT.img") + initrdENOENT := filepath.Join(dir, "initrd-ENOENT.img") + + imageEmpty := filepath.Join(dir, "image-empty.img") + initrdEmpty := filepath.Join(dir, "initrd-empty.img") + + for _, file := range []string{imageEmpty, initrdEmpty} { + err = createEmptyFile(file) + assert.NoError(err) + } + + image := filepath.Join(dir, "image.img") + initrd := filepath.Join(dir, "initrd.img") + + mb := uint32(1024 * 1024) + + fileSizeMB := uint32(3) + fileSizeBytes := fileSizeMB * mb + + fileData := strings.Repeat("X", int(fileSizeBytes)) + + for _, file := range []string{image, initrd} { + err = WriteFile(file, fileData, testFileMode) + assert.NoError(err) + } + + type testData struct { + imagePath string + initrdPath string + memBytes uint32 + expectError bool + expectLogWarning bool + } + + // Note that checkHypervisorConfig() does not check to ensure an image + // or an initrd has been specified - that's handled by a separate + // function, hence no test for it here. + + data := []testData{ + {"", "", 0, true, false}, + + {imageENOENT, "", 2, true, false}, + {"", initrdENOENT, 2, true, false}, + + {imageEmpty, "", 2, true, false}, + {"", initrdEmpty, 2, true, false}, + + {image, "", fileSizeMB + 2, false, false}, + {image, "", fileSizeMB + 1, false, false}, + {image, "", fileSizeMB + 0, false, true}, + {image, "", fileSizeMB - 1, false, true}, + {image, "", fileSizeMB - 2, false, true}, + + {"", initrd, fileSizeMB + 2, false, false}, + {"", initrd, fileSizeMB + 1, false, false}, + {"", initrd, fileSizeMB + 0, true, false}, + {"", initrd, fileSizeMB - 1, true, false}, + {"", initrd, fileSizeMB - 2, true, false}, + } + + for i, d := range data { + savedOut := kataUtilsLogger.Logger.Out + + // create buffer to save logger output + logBuf := &bytes.Buffer{} + + // capture output to buffer + kataUtilsLogger.Logger.Out = logBuf + + config := vc.HypervisorConfig{ + ImagePath: d.imagePath, + InitrdPath: d.initrdPath, + MemorySize: d.memBytes, + } + + err := checkHypervisorConfig(config) + + if d.expectError { + assert.Error(err, "test %d (%+v)", i, d) + } else { + assert.NoError(err, "test %d (%+v)", i, d) + } + + if d.expectLogWarning { + assert.True(strings.Contains(logBuf.String(), "warning")) + } else { + assert.Empty(logBuf.String()) + } + + // reset logger + kataUtilsLogger.Logger.Out = savedOut + } +} + +func TestCheckNetNsConfig(t *testing.T) { + assert := assert.New(t) + + config := oci.RuntimeConfig{ + DisableNewNetNs: true, + NetmonConfig: vc.NetmonConfig{ + Enable: true, + }, + } + err := checkNetNsConfig(config) + assert.Error(err) + + config = oci.RuntimeConfig{ + DisableNewNetNs: true, + InterNetworkModel: vc.NetXConnectDefaultModel, + } + err = checkNetNsConfig(config) + assert.Error(err) +} + +func TestCheckFactoryConfig(t *testing.T) { + assert := assert.New(t) + + type testData struct { + factoryEnabled bool + expectError bool + imagePath string + initrdPath string + } + + data := []testData{ + {false, false, "", ""}, + {false, false, "image", ""}, + {false, false, "", "initrd"}, + + {true, false, "", "initrd"}, + {true, true, "image", ""}, + } + + for i, d := range data { + config := oci.RuntimeConfig{ + HypervisorConfig: vc.HypervisorConfig{ + ImagePath: d.imagePath, + InitrdPath: d.initrdPath, + }, + + FactoryConfig: oci.FactoryConfig{ + Template: d.factoryEnabled, + }, + } + + err := checkFactoryConfig(config) + + if d.expectError { + assert.Error(err, "test %d (%+v)", i, d) + } else { + assert.NoError(err, "test %d (%+v)", i, d) + } + } +} + +func TestCheckNetNsConfigShimTrace(t *testing.T) { + assert := assert.New(t) + + type testData struct { + networkModel vc.NetInterworkingModel + disableNetNs bool + shimTrace bool + expectError bool + } + + data := []testData{ + {vc.NetXConnectMacVtapModel, false, false, false}, + {vc.NetXConnectMacVtapModel, false, true, true}, + {vc.NetXConnectMacVtapModel, true, true, true}, + {vc.NetXConnectMacVtapModel, true, false, true}, + {vc.NetXConnectNoneModel, true, false, false}, + {vc.NetXConnectNoneModel, true, true, false}, + } + + for i, d := range data { + config := oci.RuntimeConfig{ + DisableNewNetNs: d.disableNetNs, + InterNetworkModel: d.networkModel, + ShimConfig: vc.ShimConfig{ + Trace: d.shimTrace, + }, + } + + err := checkNetNsConfig(config) + + if d.expectError { + assert.Error(err, "test %d (%+v)", i, d) + } else { + assert.NoError(err, "test %d (%+v)", i, d) + } + } +} diff --git a/src/runtime/pkg/katautils/container_engine.go b/src/runtime/pkg/katautils/container_engine.go new file mode 100644 index 000000000..3b0caf5ab --- /dev/null +++ b/src/runtime/pkg/katautils/container_engine.go @@ -0,0 +1,75 @@ +// Copyright (c) 2019 SUSE LLC +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "os/exec" +) + +type CtrEngine struct { + Name string +} + +var ( + DockerLikeCtrEngines = []string{"docker", "podman"} +) + +func (e *CtrEngine) Init(name string) (string, error) { + var out string + out, err := RunCommandFull([]string{name, "version"}, true) + if err != nil { + return out, err + } + + e.Name = name + return out, nil +} + +func (e *CtrEngine) Inspect(image string) (string, error) { + // Only hit the network if the image doesn't exist locally + return RunCommand([]string{e.Name, "inspect", "--type=image", image}) +} + +func (e *CtrEngine) Pull(image string) (string, error) { + return RunCommand([]string{e.Name, "pull", image}) +} + +func (e *CtrEngine) Create(image string) (string, error) { + return RunCommand([]string{e.Name, "create", image}) +} + +func (e *CtrEngine) Rm(ctrID string) (string, error) { + return RunCommand([]string{e.Name, "rm", ctrID}) +} + +func (e *CtrEngine) GetRootfs(ctrID string, dir string) error { + cmd1 := exec.Command(e.Name, "export", ctrID) + cmd2 := exec.Command("tar", "-C", dir, "-xvf", "-") + + cmd1Stdout, err := cmd1.StdoutPipe() + if err != nil { + return err + } + + cmd2.Stdin = cmd1Stdout + + err = cmd2.Start() + if err != nil { + return err + } + + err = cmd1.Run() + if err != nil { + return err + } + + err = cmd2.Wait() + if err != nil { + return err + } + + return nil +} diff --git a/src/runtime/pkg/katautils/create.go b/src/runtime/pkg/katautils/create.go new file mode 100644 index 000000000..424d42ff3 --- /dev/null +++ b/src/runtime/pkg/katautils/create.go @@ -0,0 +1,273 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "context" + "fmt" + "io/ioutil" + "strconv" + "strings" + + vc "github.com/kata-containers/runtime/virtcontainers" + vf "github.com/kata-containers/runtime/virtcontainers/factory" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +// GetKernelParamsFunc use a variable to allow tests to modify its value +var GetKernelParamsFunc = getKernelParams + +var systemdKernelParam = []vc.Param{ + { + Key: "systemd.unit", + Value: systemdUnitName, + }, + { + Key: "systemd.mask", + Value: "systemd-networkd.service", + }, + { + Key: "systemd.mask", + Value: "systemd-networkd.socket", + }, +} + +func getKernelParams(needSystemd, trace bool) []vc.Param { + p := []vc.Param{} + + if needSystemd { + p = append(p, systemdKernelParam...) + } + + return p +} + +func needSystemd(config vc.HypervisorConfig) bool { + return config.ImagePath != "" +} + +// HandleFactory set the factory +func HandleFactory(ctx context.Context, vci vc.VC, runtimeConfig *oci.RuntimeConfig) { + if !runtimeConfig.FactoryConfig.Template && runtimeConfig.FactoryConfig.VMCacheNumber == 0 { + return + } + factoryConfig := vf.Config{ + Template: runtimeConfig.FactoryConfig.Template, + TemplatePath: runtimeConfig.FactoryConfig.TemplatePath, + VMCache: runtimeConfig.FactoryConfig.VMCacheNumber > 0, + VMCacheEndpoint: runtimeConfig.FactoryConfig.VMCacheEndpoint, + VMConfig: vc.VMConfig{ + HypervisorType: runtimeConfig.HypervisorType, + HypervisorConfig: runtimeConfig.HypervisorConfig, + AgentType: runtimeConfig.AgentType, + AgentConfig: runtimeConfig.AgentConfig, + ProxyType: runtimeConfig.ProxyType, + ProxyConfig: runtimeConfig.ProxyConfig, + }, + } + + kataUtilsLogger.WithField("factory", factoryConfig).Info("load vm factory") + + f, err := vf.NewFactory(ctx, factoryConfig, true) + if err != nil && !factoryConfig.VMCache { + kataUtilsLogger.WithError(err).Warn("load vm factory failed, about to create new one") + f, err = vf.NewFactory(ctx, factoryConfig, false) + } + if err != nil { + kataUtilsLogger.WithError(err).Warn("create vm factory failed") + return + } + + vci.SetFactory(ctx, f) +} + +// SetEphemeralStorageType sets the mount type to 'ephemeral' +// if the mount source path is provisioned by k8s for ephemeral storage. +// For the given pod ephemeral volume is created only once +// backed by tmpfs inside the VM. For successive containers +// of the same pod the already existing volume is reused. +func SetEphemeralStorageType(ociSpec specs.Spec) specs.Spec { + for idx, mnt := range ociSpec.Mounts { + if vc.IsEphemeralStorage(mnt.Source) { + ociSpec.Mounts[idx].Type = vc.KataEphemeralDevType + } + if vc.Isk8sHostEmptyDir(mnt.Source) { + ociSpec.Mounts[idx].Type = vc.KataLocalDevType + } + } + return ociSpec +} + +// CreateSandbox create a sandbox container +func CreateSandbox(ctx context.Context, vci vc.VC, ociSpec specs.Spec, runtimeConfig oci.RuntimeConfig, rootFs vc.RootFs, + containerID, bundlePath, console string, disableOutput, systemdCgroup, builtIn bool) (_ vc.VCSandbox, _ vc.Process, err error) { + span, ctx := Trace(ctx, "createSandbox") + defer span.Finish() + + sandboxConfig, err := oci.SandboxConfig(ociSpec, runtimeConfig, bundlePath, containerID, console, disableOutput, systemdCgroup) + if err != nil { + return nil, vc.Process{}, err + } + + if builtIn { + sandboxConfig.Stateful = true + } + + if err := checkForFIPS(&sandboxConfig); err != nil { + return nil, vc.Process{}, err + } + + if !rootFs.Mounted && len(sandboxConfig.Containers) == 1 { + if rootFs.Source != "" { + realPath, err := ResolvePath(rootFs.Source) + if err != nil { + return nil, vc.Process{}, err + } + rootFs.Source = realPath + } + sandboxConfig.Containers[0].RootFs = rootFs + } + + // Important to create the network namespace before the sandbox is + // created, because it is not responsible for the creation of the + // netns if it does not exist. + if err := SetupNetworkNamespace(&sandboxConfig.NetworkConfig); err != nil { + return nil, vc.Process{}, err + } + + defer func() { + // cleanup netns if kata creates it + ns := sandboxConfig.NetworkConfig + if err != nil && ns.NetNsCreated { + if ex := cleanupNetNS(ns.NetNSPath); ex != nil { + kataUtilsLogger.WithField("path", ns.NetNSPath).WithError(ex).Warn("failed to cleanup netns") + } + } + }() + + // Run pre-start OCI hooks. + err = EnterNetNS(sandboxConfig.NetworkConfig.NetNSPath, func() error { + return PreStartHooks(ctx, ociSpec, containerID, bundlePath) + }) + if err != nil { + return nil, vc.Process{}, err + } + + sandbox, err := vci.CreateSandbox(ctx, sandboxConfig) + if err != nil { + return nil, vc.Process{}, err + } + + sid := sandbox.ID() + kataUtilsLogger = kataUtilsLogger.WithField("sandbox", sid) + span.SetTag("sandbox", sid) + + containers := sandbox.GetAllContainers() + if len(containers) != 1 { + return nil, vc.Process{}, fmt.Errorf("BUG: Container list from sandbox is wrong, expecting only one container, found %d containers", len(containers)) + } + + if !builtIn { + err = AddContainerIDMapping(ctx, containerID, sandbox.ID()) + if err != nil { + return nil, vc.Process{}, err + } + } + + return sandbox, containers[0].Process(), nil +} + +var procFIPS = "/proc/sys/crypto/fips_enabled" + +func checkForFIPS(sandboxConfig *vc.SandboxConfig) error { + content, err := ioutil.ReadFile(procFIPS) + if err != nil { + // In case file cannot be found or read, simply return + return nil + } + + enabled, err := strconv.Atoi(strings.Trim(string(content), "\n\t ")) + if err != nil { + // Unexpected format, ignore and simply return early + return nil + } + + if enabled == 1 { + param := vc.Param{ + Key: "fips", + Value: "1", + } + + if err := sandboxConfig.HypervisorConfig.AddKernelParam(param); err != nil { + return fmt.Errorf("Error enabling fips mode : %v", err) + } + } + + return nil +} + +// CreateContainer create a container +func CreateContainer(ctx context.Context, vci vc.VC, sandbox vc.VCSandbox, ociSpec specs.Spec, rootFs vc.RootFs, containerID, bundlePath, console string, disableOutput, builtIn bool) (vc.Process, error) { + var c vc.VCContainer + + span, ctx := Trace(ctx, "createContainer") + defer span.Finish() + + ociSpec = SetEphemeralStorageType(ociSpec) + + contConfig, err := oci.ContainerConfig(ociSpec, bundlePath, containerID, console, disableOutput) + if err != nil { + return vc.Process{}, err + } + + if !rootFs.Mounted { + if rootFs.Source != "" { + realPath, err := ResolvePath(rootFs.Source) + if err != nil { + return vc.Process{}, err + } + rootFs.Source = realPath + } + contConfig.RootFs = rootFs + } + + sandboxID, err := oci.SandboxID(ociSpec) + if err != nil { + return vc.Process{}, err + } + + span.SetTag("sandbox", sandboxID) + + if builtIn { + c, err = sandbox.CreateContainer(contConfig) + if err != nil { + return vc.Process{}, err + } + } else { + kataUtilsLogger = kataUtilsLogger.WithField("sandbox", sandboxID) + + sandbox, c, err = vci.CreateContainer(ctx, sandboxID, contConfig) + if err != nil { + return vc.Process{}, err + } + + if err := AddContainerIDMapping(ctx, containerID, sandboxID); err != nil { + return vc.Process{}, err + } + } + + // Run pre-start OCI hooks. + err = EnterNetNS(sandbox.GetNetNs(), func() error { + return PreStartHooks(ctx, ociSpec, containerID, bundlePath) + }) + if err != nil { + return vc.Process{}, err + } + + return c.Process(), nil +} diff --git a/src/runtime/pkg/katautils/create_test.go b/src/runtime/pkg/katautils/create_test.go new file mode 100644 index 000000000..e750cc2d4 --- /dev/null +++ b/src/runtime/pkg/katautils/create_test.go @@ -0,0 +1,515 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + "syscall" + "testing" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/kata-containers/runtime/virtcontainers/pkg/oci" + "github.com/kata-containers/runtime/virtcontainers/pkg/vcmock" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" +) + +const ( + testConsole = "/dev/pts/999" + testContainerTypeAnnotation = "io.kubernetes.cri-o.ContainerType" + testSandboxIDAnnotation = "io.kubernetes.cri-o.SandboxID" + testContainerTypeContainer = "container" +) + +var ( + testBundleDir = "" + + // testingImpl is a concrete mock RVC implementation used for testing + testingImpl = &vcmock.VCMock{} + + tc ktu.TestConstraint +) + +func init() { + tc = ktu.NewTestConstraint(false) +} + +func writeOCIConfigFile(spec specs.Spec, configPath string) error { + if configPath == "" { + return errors.New("BUG: need config file path") + } + + bytes, err := json.MarshalIndent(spec, "", "\t") + if err != nil { + return err + } + + return ioutil.WriteFile(configPath, bytes, testFileMode) +} + +// Create an OCI bundle in the specified directory. +// +// Note that the directory will be created, but it's parent is expected to exist. +// +// This function works by copying the already-created test bundle. Ideally, +// the bundle would be recreated for each test, but createRootfs() uses +// docker which on some systems is too slow, resulting in the tests timing +// out. +func makeOCIBundle(bundleDir string) error { + from := testBundleDir + to := bundleDir + + // only the basename of bundleDir needs to exist as bundleDir + // will get created by cp(1). + base := filepath.Dir(bundleDir) + + for _, dir := range []string{from, base} { + if !FileExists(dir) { + return fmt.Errorf("BUG: directory %v should exist", dir) + } + } + + output, err := RunCommandFull([]string{"cp", "-a", from, to}, true) + if err != nil { + return fmt.Errorf("failed to copy test OCI bundle from %v to %v: %v (output: %v)", from, to, err, output) + } + + return nil +} + +// newTestRuntimeConfig creates a new RuntimeConfig +func newTestRuntimeConfig(dir, consolePath string, create bool) (oci.RuntimeConfig, error) { + if dir == "" { + return oci.RuntimeConfig{}, errors.New("BUG: need directory") + } + + hypervisorConfig, err := newTestHypervisorConfig(dir, create) + if err != nil { + return oci.RuntimeConfig{}, err + } + + return oci.RuntimeConfig{ + HypervisorType: vc.QemuHypervisor, + HypervisorConfig: hypervisorConfig, + AgentType: vc.KataContainersAgent, + ProxyType: vc.KataProxyType, + ShimType: vc.KataShimType, + Console: consolePath, + }, nil +} + +// newTestHypervisorConfig creaets a new virtcontainers +// HypervisorConfig, ensuring that the required resources are also +// created. +// +// Note: no parameter validation in case caller wishes to create an invalid +// object. +func newTestHypervisorConfig(dir string, create bool) (vc.HypervisorConfig, error) { + kernelPath := path.Join(dir, "kernel") + imagePath := path.Join(dir, "image") + hypervisorPath := path.Join(dir, "hypervisor") + + if create { + for _, file := range []string{kernelPath, imagePath, hypervisorPath} { + err := createEmptyFile(file) + if err != nil { + return vc.HypervisorConfig{}, err + } + } + } + + return vc.HypervisorConfig{ + KernelPath: kernelPath, + ImagePath: imagePath, + HypervisorPath: hypervisorPath, + HypervisorMachineType: "pc-lite", + }, nil +} + +// return the value of the *last* param with the specified key +func findLastParam(key string, params []vc.Param) (string, error) { + if key == "" { + return "", errors.New("ERROR: need non-nil key") + } + + l := len(params) + if l == 0 { + return "", errors.New("ERROR: no params") + } + + for i := l - 1; i >= 0; i-- { + p := params[i] + + if key == p.Key { + return p.Value, nil + } + } + + return "", fmt.Errorf("no param called %q found", name) +} + +func TestSetEphemeralStorageType(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + dir, err := ioutil.TempDir(testDir, "foo") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + ephePath := filepath.Join(dir, vc.K8sEmptyDir, "tmp-volume") + err = os.MkdirAll(ephePath, testDirMode) + assert.Nil(err) + + err = syscall.Mount("tmpfs", ephePath, "tmpfs", 0, "") + assert.Nil(err) + defer syscall.Unmount(ephePath, 0) + + ociSpec := specs.Spec{} + var ociMounts []specs.Mount + mount := specs.Mount{ + Source: ephePath, + } + + ociMounts = append(ociMounts, mount) + ociSpec.Mounts = ociMounts + ociSpec = SetEphemeralStorageType(ociSpec) + + mountType := ociSpec.Mounts[0].Type + assert.Equal(mountType, "ephemeral", + "Unexpected mount type, got %s expected ephemeral", mountType) +} + +func TestSetKernelParams(t *testing.T) { + assert := assert.New(t) + + config := oci.RuntimeConfig{} + + assert.Empty(config.HypervisorConfig.KernelParams) + + err := SetKernelParams(&config) + assert.NoError(err) + + config.HypervisorConfig.BlockDeviceDriver = "virtio-scsi" + err = SetKernelParams(&config) + assert.NoError(err) + + if needSystemd(config.HypervisorConfig) { + assert.NotEmpty(config.HypervisorConfig.KernelParams) + } +} + +func TestSetKernelParamsUserOptionTakesPriority(t *testing.T) { + assert := assert.New(t) + + initName := "init" + initValue := "/sbin/myinit" + + ipName := "ip" + ipValue := "127.0.0.1" + + params := []vc.Param{ + {Key: initName, Value: initValue}, + {Key: ipName, Value: ipValue}, + } + + hypervisorConfig := vc.HypervisorConfig{ + KernelParams: params, + } + + // Config containing user-specified kernel parameters + config := oci.RuntimeConfig{ + HypervisorConfig: hypervisorConfig, + } + + assert.NotEmpty(config.HypervisorConfig.KernelParams) + + err := SetKernelParams(&config) + assert.NoError(err) + + kernelParams := config.HypervisorConfig.KernelParams + + init, err := findLastParam(initName, kernelParams) + assert.NoError(err) + assert.Equal(initValue, init) + + ip, err := findLastParam(ipName, kernelParams) + assert.NoError(err) + assert.Equal(ipValue, ip) + +} + +func TestCreateSandboxConfigFail(t *testing.T) { + assert := assert.New(t) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + quota := int64(0) + limit := int64(0) + + spec.Linux.Resources.Memory = &specs.LinuxMemory{ + Limit: &limit, + } + + spec.Linux.Resources.CPU = &specs.LinuxCPU{ + // specify an invalid value + Quota: "a, + } + + rootFs := vc.RootFs{Mounted: true} + + _, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, rootFs, testContainerID, bundlePath, testConsole, true, true, false) + assert.Error(err) +} + +func TestCreateSandboxFail(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true) + assert.NoError(err) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + rootFs := vc.RootFs{Mounted: true} + + _, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, rootFs, testContainerID, bundlePath, testConsole, true, true, false) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) +} + +func TestCheckForFips(t *testing.T) { + assert := assert.New(t) + + path, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(path) + + val := procFIPS + procFIPS = filepath.Join(path, "fips-enabled") + defer func() { + procFIPS = val + }() + + err = ioutil.WriteFile(procFIPS, []byte("1"), 0644) + assert.NoError(err) + + hconfig := vc.HypervisorConfig{ + KernelParams: []vc.Param{ + {Key: "init", Value: "/sys/init"}, + }, + } + config := vc.SandboxConfig{ + HypervisorConfig: hconfig, + } + assert.NoError(checkForFIPS(&config)) + + params := config.HypervisorConfig.KernelParams + assert.Equal(len(params), 2) + assert.Equal(params[1].Key, "fips") + assert.Equal(params[1].Value, "1") + + config.HypervisorConfig = hconfig + err = ioutil.WriteFile(procFIPS, []byte("unexpected contents"), 0644) + assert.NoError(err) + assert.NoError(checkForFIPS(&config)) + assert.Equal(config.HypervisorConfig, hconfig) + + assert.NoError(os.Remove(procFIPS)) + assert.NoError(checkForFIPS(&config)) + assert.Equal(config.HypervisorConfig, hconfig) +} + +func TestCreateContainerContainerConfigFail(t *testing.T) { + assert := assert.New(t) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + // Set invalid container type + containerType := "你好,世界" + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = containerType + + // rewrite file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + rootFs := vc.RootFs{Mounted: true} + + for _, disableOutput := range []bool{true, false} { + _, err = CreateContainer(context.Background(), testingImpl, nil, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false) + assert.Error(err) + assert.False(vcmock.IsMockError(err)) + assert.True(strings.Contains(err.Error(), containerType)) + os.RemoveAll(path) + } +} + +func TestCreateContainerFail(t *testing.T) { + assert := assert.New(t) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + // set expected container type and sandboxID + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer + spec.Annotations[testSandboxIDAnnotation] = testSandboxID + + // rewrite file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + rootFs := vc.RootFs{Mounted: true} + + for _, disableOutput := range []bool{true, false} { + _, err = CreateContainer(context.Background(), testingImpl, nil, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false) + assert.Error(err) + assert.True(vcmock.IsMockError(err)) + os.RemoveAll(path) + } +} + +func TestCreateContainer(t *testing.T) { + assert := assert.New(t) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + testingImpl.CreateContainerFunc = func(ctx context.Context, sandboxID string, containerConfig vc.ContainerConfig) (vc.VCSandbox, vc.VCContainer, error) { + return &vcmock.Sandbox{}, &vcmock.Container{}, nil + } + + defer func() { + testingImpl.CreateContainerFunc = nil + }() + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + bundlePath := filepath.Join(tmpdir, "bundle") + + err = makeOCIBundle(bundlePath) + assert.NoError(err) + + ociConfigFile := filepath.Join(bundlePath, "config.json") + assert.True(FileExists(ociConfigFile)) + + spec, err := compatoci.ParseConfigJSON(bundlePath) + assert.NoError(err) + + // set expected container type and sandboxID + spec.Annotations = make(map[string]string) + spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer + spec.Annotations[testSandboxIDAnnotation] = testSandboxID + + // rewrite file + err = writeOCIConfigFile(spec, ociConfigFile) + assert.NoError(err) + + rootFs := vc.RootFs{Mounted: true} + + for _, disableOutput := range []bool{true, false} { + _, err = CreateContainer(context.Background(), testingImpl, nil, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false) + assert.NoError(err) + os.RemoveAll(path) + } +} diff --git a/src/runtime/pkg/katautils/hook.go b/src/runtime/pkg/katautils/hook.go new file mode 100644 index 000000000..0f20a95ba --- /dev/null +++ b/src/runtime/pkg/katautils/hook.go @@ -0,0 +1,140 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os/exec" + "strings" + "syscall" + "time" + + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opentracing/opentracing-go/log" + "github.com/sirupsen/logrus" +) + +// Logger returns a logrus logger appropriate for logging hook messages +func hookLogger() *logrus.Entry { + return kataUtilsLogger.WithField("subsystem", "hook") +} + +func runHook(ctx context.Context, hook specs.Hook, cid, bundlePath string) error { + span, _ := Trace(ctx, "hook") + defer span.Finish() + + span.SetTag("subsystem", "runHook") + + span.LogFields( + log.String("hook-name", hook.Path), + log.String("hook-args", strings.Join(hook.Args, " "))) + + state := specs.State{ + Pid: syscall.Gettid(), + Bundle: bundlePath, + ID: cid, + } + + stateJSON, err := json.Marshal(state) + if err != nil { + return err + } + + var stdout, stderr bytes.Buffer + cmd := &exec.Cmd{ + Path: hook.Path, + Args: hook.Args, + Env: hook.Env, + Stdin: bytes.NewReader(stateJSON), + Stdout: &stdout, + Stderr: &stderr, + } + + if err := cmd.Start(); err != nil { + return err + } + + if hook.Timeout == nil { + if err := cmd.Wait(); err != nil { + return fmt.Errorf("%s: stdout: %s, stderr: %s", err, stdout.String(), stderr.String()) + } + } else { + done := make(chan error, 1) + go func() { + done <- cmd.Wait() + close(done) + }() + + select { + case err := <-done: + if err != nil { + return fmt.Errorf("%s: stdout: %s, stderr: %s", err, stdout.String(), stderr.String()) + } + case <-time.After(time.Duration(*hook.Timeout) * time.Second): + if err := syscall.Kill(cmd.Process.Pid, syscall.SIGKILL); err != nil { + return err + } + + return fmt.Errorf("Hook timeout") + } + } + + return nil +} + +func runHooks(ctx context.Context, hooks []specs.Hook, cid, bundlePath, hookType string) error { + span, _ := Trace(ctx, "hooks") + defer span.Finish() + + span.SetTag("subsystem", hookType) + + for _, hook := range hooks { + if err := runHook(ctx, hook, cid, bundlePath); err != nil { + hookLogger().WithFields(logrus.Fields{ + "hook-type": hookType, + "error": err, + }).Error("hook error") + + return err + } + } + + return nil +} + +// PreStartHooks run the hooks before start container +func PreStartHooks(ctx context.Context, spec specs.Spec, cid, bundlePath string) error { + // If no hook available, nothing needs to be done. + if spec.Hooks == nil { + return nil + } + + return runHooks(ctx, spec.Hooks.Prestart, cid, bundlePath, "pre-start") +} + +// PostStartHooks run the hooks just after start container +func PostStartHooks(ctx context.Context, spec specs.Spec, cid, bundlePath string) error { + // If no hook available, nothing needs to be done. + if spec.Hooks == nil { + return nil + } + + return runHooks(ctx, spec.Hooks.Poststart, cid, bundlePath, "post-start") +} + +// PostStopHooks run the hooks after stop container +func PostStopHooks(ctx context.Context, spec specs.Spec, cid, bundlePath string) error { + // If no hook available, nothing needs to be done. + if spec.Hooks == nil { + return nil + } + + return runHooks(ctx, spec.Hooks.Poststop, cid, bundlePath, "post-stop") +} diff --git a/src/runtime/pkg/katautils/hook_test.go b/src/runtime/pkg/katautils/hook_test.go new file mode 100644 index 000000000..4cc0b6227 --- /dev/null +++ b/src/runtime/pkg/katautils/hook_test.go @@ -0,0 +1,212 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "context" + "os" + "testing" + + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + . "github.com/kata-containers/runtime/virtcontainers/pkg/mock" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/stretchr/testify/assert" +) + +// Important to keep these values in sync with hook test binary +var testKeyHook = "test-key" +var testContainerIDHook = "test-container-id" +var testControllerIDHook = "test-controller-id" +var testBinHookPath = "/usr/bin/virtcontainers/bin/test/hook" +var testBundlePath = "/test/bundle" + +func getMockHookBinPath() string { + if DefaultMockHookBinPath == "" { + return testBinHookPath + } + + return DefaultMockHookBinPath +} + +func createHook(timeout int) specs.Hook { + to := &timeout + if timeout == 0 { + to = nil + } + + return specs.Hook{ + Path: getMockHookBinPath(), + Args: []string{testKeyHook, testContainerIDHook, testControllerIDHook}, + Env: os.Environ(), + Timeout: to, + } +} + +func createWrongHook() specs.Hook { + return specs.Hook{ + Path: getMockHookBinPath(), + Args: []string{"wrong-args"}, + Env: os.Environ(), + } +} + +func TestRunHook(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + ctx := context.Background() + + // Run with timeout 0 + hook := createHook(0) + err := runHook(ctx, hook, testSandboxID, testBundlePath) + assert.NoError(err) + + // Run with timeout 1 + hook = createHook(1) + err = runHook(ctx, hook, testSandboxID, testBundlePath) + assert.NoError(err) + + // Run timeout failure + hook = createHook(1) + hook.Args = append(hook.Args, "2") + err = runHook(ctx, hook, testSandboxID, testBundlePath) + assert.Error(err) + + // Failure due to wrong hook + hook = createWrongHook() + err = runHook(ctx, hook, testSandboxID, testBundlePath) + assert.Error(err) +} + +func TestPreStartHooks(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + ctx := context.Background() + + // Hooks field is nil + spec := specs.Spec{} + err := PreStartHooks(ctx, spec, "", "") + assert.NoError(err) + + // Hooks list is empty + spec = specs.Spec{ + Hooks: &specs.Hooks{}, + } + err = PreStartHooks(ctx, spec, "", "") + assert.NoError(err) + + // Run with timeout 0 + hook := createHook(0) + spec = specs.Spec{ + Hooks: &specs.Hooks{ + Prestart: []specs.Hook{hook}, + }, + } + err = PreStartHooks(ctx, spec, testSandboxID, testBundlePath) + assert.NoError(err) + + // Failure due to wrong hook + hook = createWrongHook() + spec = specs.Spec{ + Hooks: &specs.Hooks{ + Prestart: []specs.Hook{hook}, + }, + } + err = PreStartHooks(ctx, spec, testSandboxID, testBundlePath) + assert.Error(err) +} + +func TestPostStartHooks(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + ctx := context.Background() + + // Hooks field is nil + spec := specs.Spec{} + err := PostStartHooks(ctx, spec, "", "") + assert.NoError(err) + + // Hooks list is empty + spec = specs.Spec{ + Hooks: &specs.Hooks{}, + } + err = PostStartHooks(ctx, spec, "", "") + assert.NoError(err) + + // Run with timeout 0 + hook := createHook(0) + spec = specs.Spec{ + Hooks: &specs.Hooks{ + Poststart: []specs.Hook{hook}, + }, + } + err = PostStartHooks(ctx, spec, testSandboxID, testBundlePath) + assert.NoError(err) + + // Failure due to wrong hook + hook = createWrongHook() + spec = specs.Spec{ + Hooks: &specs.Hooks{ + Poststart: []specs.Hook{hook}, + }, + } + err = PostStartHooks(ctx, spec, testSandboxID, testBundlePath) + assert.Error(err) +} + +func TestPostStopHooks(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + ctx := context.Background() + + // Hooks field is nil + spec := specs.Spec{} + err := PostStopHooks(ctx, spec, "", "") + assert.NoError(err) + + // Hooks list is empty + spec = specs.Spec{ + Hooks: &specs.Hooks{}, + } + err = PostStopHooks(ctx, spec, "", "") + assert.NoError(err) + + // Run with timeout 0 + hook := createHook(0) + spec = specs.Spec{ + Hooks: &specs.Hooks{ + Poststop: []specs.Hook{hook}, + }, + } + err = PostStopHooks(ctx, spec, testSandboxID, testBundlePath) + assert.NoError(err) + + // Failure due to wrong hook + hook = createWrongHook() + spec = specs.Spec{ + Hooks: &specs.Hooks{ + Poststop: []specs.Hook{hook}, + }, + } + err = PostStopHooks(ctx, spec, testSandboxID, testBundlePath) + assert.Error(err) +} diff --git a/src/runtime/pkg/katautils/logger.go b/src/runtime/pkg/katautils/logger.go new file mode 100644 index 000000000..699d5a95e --- /dev/null +++ b/src/runtime/pkg/katautils/logger.go @@ -0,0 +1,87 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "context" + "log/syslog" + "time" + + "github.com/sirupsen/logrus" + lSyslog "github.com/sirupsen/logrus/hooks/syslog" +) + +// Default our log level to 'Warn', rather than the logrus default +// of 'Info', which is rather noisy. +var originalLoggerLevel = logrus.WarnLevel +var kataUtilsLogger = logrus.NewEntry(logrus.New()) + +// SetLogger sets the logger for the factory. +func SetLogger(ctx context.Context, logger *logrus.Entry, level logrus.Level) { + fields := logrus.Fields{ + "source": "katautils", + } + + originalLoggerLevel = level + kataUtilsLogger = logger.WithFields(fields) +} + +// sysLogHook wraps a syslog logrus hook and a formatter to be used for all +// syslog entries. +// +// This is necessary to allow the main logger (for "--log=") to use a custom +// formatter ("--log-format=") whilst allowing the system logger to use a +// different formatter. +type sysLogHook struct { + shook *lSyslog.SyslogHook + formatter logrus.Formatter +} + +func (h *sysLogHook) Levels() []logrus.Level { + return h.shook.Levels() +} + +// Fire is responsible for adding a log entry to the system log. It switches +// formatter before adding the system log entry, then reverts the original log +// formatter. +func (h *sysLogHook) Fire(e *logrus.Entry) (err error) { + formatter := e.Logger.Formatter + + e.Logger.Formatter = h.formatter + + err = h.shook.Fire(e) + + e.Logger.Formatter = formatter + + return err +} + +func newSystemLogHook(network, raddr string) (*sysLogHook, error) { + hook, err := lSyslog.NewSyslogHook(network, raddr, syslog.LOG_INFO, name) + if err != nil { + return nil, err + } + + return &sysLogHook{ + formatter: &logrus.TextFormatter{ + TimestampFormat: time.RFC3339Nano, + }, + shook: hook, + }, nil +} + +// handleSystemLog sets up the system-level logger. +func handleSystemLog(network, raddr string) error { + hook, err := newSystemLogHook(network, raddr) + if err != nil { + return err + } + + kataUtilsLogger.Logger.Hooks.Add(hook) + + return nil +} diff --git a/src/runtime/pkg/katautils/logger_test.go b/src/runtime/pkg/katautils/logger_test.go new file mode 100644 index 000000000..ff8720fc1 --- /dev/null +++ b/src/runtime/pkg/katautils/logger_test.go @@ -0,0 +1,155 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "fmt" + "io/ioutil" + "regexp" + "strings" + "testing" + "time" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +type testData struct { + network string + raddr string + expectError bool +} + +func init() { + // Ensure all log levels are logged + kataUtilsLogger.Logger.Level = logrus.DebugLevel + + // Discard log output + kataUtilsLogger.Logger.Out = ioutil.Discard +} + +func TestHandleSystemLog(t *testing.T) { + assert := assert.New(t) + + data := []testData{ + {"invalid-net-type", "999.999.999.999", true}, + {"invalid net-type", "a a ", true}, + {"invalid-net-type", ".", true}, + {"moo", "999.999.999.999", true}, + {"moo", "999.999.999.999:99999999999999999", true}, + {"qwerty", "uiop:ftw!", true}, + {"", "", false}, + } + + for _, d := range data { + err := handleSystemLog(d.network, d.raddr) + if d.expectError { + assert.Error(err, fmt.Sprintf("%+v", d)) + } else { + assert.NoError(err, fmt.Sprintf("%+v", d)) + } + } +} + +func TestNewSystemLogHook(t *testing.T) { + assert := assert.New(t) + + hook, err := newSystemLogHook("", "") + assert.NoError(err) + + msg := "wibble" + level := logrus.DebugLevel + + logger := logrus.New() + + // ensure all output is displayed + logger.Level = logrus.DebugLevel + + // throw away all stdout so that the Format() call + // below returns the data in structured form. + logger.Out = ioutil.Discard + + entry := &logrus.Entry{ + Logger: logger, + + // UTC for time.Parse() + Time: time.Now().UTC(), + + Message: msg, + Level: level, + } + + // call the formatting function directly and see if the output + // matches what we expect. + bytes, err := hook.formatter.Format(entry) + assert.NoError(err) + + output := string(bytes) + output = strings.TrimSpace(output) + output = strings.Replace(output, `"`, "", -1) + + fields := strings.Fields(output) + + msgFound := "" + timeFound := "" + levelFound := "" + + // look for the expected fields + for _, field := range fields { + + // split each structured field into name and value fields + f := strings.Split(field, "=") + assert.True(len(f) >= 2) + + switch f[0] { + case "level": + levelFound = f[1] + case "msg": + msgFound = f[1] + case "time": + timeFound = f[1] + } + } + + assert.Equal(levelFound, level.String()) + assert.Equal(msgFound, msg) + assert.NotEqual(timeFound, "") + + // Tell time.Parse() how to handle the timestamps by putting it into + // the standard golang time format equivalent to: + // + // "Mon Jan 2 15:04:05 -0700 MST 2006". + // + expectedTimeFormat := "2006-01-02T15:04:05.999999999Z" + + // Note that time.Parse() assumes a UTC time. + _, err = time.Parse(expectedTimeFormat, timeFound) + assert.NoError(err) + + // time.Parse() is "clever" but also doesn't check anything more + // granular than a second, so let's be completely paranoid and check + // via regular expression too. + expectedPattern := + // YYYY-MM-DD + `\d{4}-\d{2}-\d{2}` + + // time separator + `T` + + // HH:MM:SS + `\d{2}:\d{2}:\d{2}` + + // high-precision separator + `.` + + // nano-seconds. Note that the quantifier range is + // required because the time.RFC3339Nano format + // trunctates trailing zeros. + `\d{1,9}` + + // UTC timezone specifier + `Z` + + expectedRE := regexp.MustCompile(expectedPattern) + matched := expectedRE.FindAllStringSubmatch(timeFound, -1) + assert.NotNil(matched, "expected time in format %q, got %q", expectedPattern, timeFound) +} diff --git a/src/runtime/pkg/katautils/network.go b/src/runtime/pkg/katautils/network.go new file mode 100644 index 000000000..714fb4614 --- /dev/null +++ b/src/runtime/pkg/katautils/network.go @@ -0,0 +1,216 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + goruntime "runtime" + "strings" + + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/kata-containers/runtime/virtcontainers/pkg/rootless" + "golang.org/x/sys/unix" +) + +const procMountInfoFile = "/proc/self/mountinfo" + +// EnterNetNS is free from any call to a go routine, and it calls +// into runtime.LockOSThread(), meaning it won't be executed in a +// different thread than the one expected by the caller. +func EnterNetNS(netNSPath string, cb func() error) error { + if netNSPath == "" { + return cb() + } + + goruntime.LockOSThread() + defer goruntime.UnlockOSThread() + + currentNS, err := ns.GetCurrentNS() + if err != nil { + return err + } + defer currentNS.Close() + + targetNS, err := ns.GetNS(netNSPath) + if err != nil { + return err + } + + if err := targetNS.Set(); err != nil { + return err + } + defer currentNS.Set() + + return cb() +} + +// SetupNetworkNamespace create a network namespace +func SetupNetworkNamespace(config *vc.NetworkConfig) error { + if config.DisableNewNetNs { + kataUtilsLogger.Info("DisableNewNetNs is on, shim and hypervisor are running in the host netns") + return nil + } + + var err error + var n ns.NetNS + + if config.NetNSPath == "" { + if rootless.IsRootless() { + n, err = rootless.NewNS() + if err != nil { + return err + } + } else { + n, err = testutils.NewNS() + if err != nil { + return err + } + } + + config.NetNSPath = n.Path() + config.NetNsCreated = true + kataUtilsLogger.WithField("netns", n.Path()).Info("create netns") + + return nil + } + + isHostNs, err := hostNetworkingRequested(config.NetNSPath) + if err != nil { + return err + } + if isHostNs { + return fmt.Errorf("Host networking requested, not supported by runtime") + } + + return nil +} + +// getNetNsFromBindMount returns the network namespace for the bind-mounted path +func getNetNsFromBindMount(nsPath string, procMountFile string) (string, error) { + netNsMountType := "nsfs" + + // Resolve all symlinks in the path as the mountinfo file contains + // resolved paths. + nsPath, err := filepath.EvalSymlinks(nsPath) + if err != nil { + return "", err + } + + f, err := os.Open(procMountFile) + if err != nil { + return "", err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + text := scanner.Text() + + // Scan the mountinfo file to search for the network namespace path + // This file contains mounts in the eg format: + // "711 26 0:3 net:[4026532009] /run/docker/netns/default rw shared:535 - nsfs nsfs rw" + // + // Reference: https://www.kernel.org/doc/Documentation/filesystems/proc.txt + + // We are interested in the first 9 fields of this file, + // to check for the correct mount type. + fields := strings.Split(text, " ") + if len(fields) < 9 { + continue + } + + // We check here if the mount type is a network namespace mount type, namely "nsfs" + mountTypeFieldIdx := 8 + if fields[mountTypeFieldIdx] != netNsMountType { + continue + } + + // This is the mount point/destination for the mount + mntDestIdx := 4 + if fields[mntDestIdx] != nsPath { + continue + } + + // This is the root/source of the mount + return fields[3], nil + } + + return "", nil +} + +// hostNetworkingRequested checks if the network namespace requested is the +// same as the current process. +func hostNetworkingRequested(configNetNs string) (bool, error) { + var evalNS, nsPath, currentNsPath string + var err error + + // Net namespace provided as "/proc/pid/ns/net" or "/proc//task//ns/net" + if strings.HasPrefix(configNetNs, "/proc") && strings.HasSuffix(configNetNs, "/ns/net") { + if _, err := os.Stat(configNetNs); err != nil { + return false, err + } + + // Here we are trying to resolve the path but it fails because + // namespaces links don't really exist. For this reason, the + // call to EvalSymlinks will fail when it will try to stat the + // resolved path found. As we only care about the path, we can + // retrieve it from the PathError structure. + if _, err = filepath.EvalSymlinks(configNetNs); err != nil { + nsPath = err.(*os.PathError).Path + } else { + return false, fmt.Errorf("Net namespace path %s is not a symlink", configNetNs) + } + + _, evalNS = filepath.Split(nsPath) + + } else { + // Bind-mounted path provided + evalNS, _ = getNetNsFromBindMount(configNetNs, procMountInfoFile) + } + + currentNS := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) + if _, err = filepath.EvalSymlinks(currentNS); err != nil { + currentNsPath = err.(*os.PathError).Path + } else { + return false, fmt.Errorf("Unexpected: Current network namespace path is not a symlink") + } + + _, evalCurrentNS := filepath.Split(currentNsPath) + + if evalNS == evalCurrentNS { + return true, nil + } + + return false, nil +} + +// cleanupNetNS cleanup netns created by kata, trigger only create sandbox fails +func cleanupNetNS(netNSPath string) error { + n, err := ns.GetNS(netNSPath) + if err != nil { + return fmt.Errorf("failed to get netns %s: %v", netNSPath, err) + } + + err = n.Close() + if err != nil { + return fmt.Errorf("failed to close netns %s: %v", netNSPath, err) + } + + if err = unix.Unmount(netNSPath, unix.MNT_DETACH); err != nil { + return fmt.Errorf("failed to unmount namespace %s: %v", netNSPath, err) + } + if err := os.RemoveAll(netNSPath); err != nil { + return fmt.Errorf("failed to clean up namespace %s: %v", netNSPath, err) + } + + return nil +} diff --git a/src/runtime/pkg/katautils/network_test.go b/src/runtime/pkg/katautils/network_test.go new file mode 100644 index 000000000..097b94a87 --- /dev/null +++ b/src/runtime/pkg/katautils/network_test.go @@ -0,0 +1,156 @@ +// Copyright (c) 2018 Huawei Corporation. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "syscall" + "testing" + + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containernetworking/plugins/pkg/testutils" + ktu "github.com/kata-containers/runtime/pkg/katatestutils" + vc "github.com/kata-containers/runtime/virtcontainers" + "github.com/stretchr/testify/assert" + "golang.org/x/sys/unix" +) + +func TestGetNetNsFromBindMount(t *testing.T) { + assert := assert.New(t) + + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + mountFile := filepath.Join(tmpdir, "mountInfo") + nsPath := filepath.Join(tmpdir, "ns123") + + // Non-existent namespace path + _, err = getNetNsFromBindMount(nsPath, mountFile) + assert.NotNil(err) + + tmpNSPath := filepath.Join(tmpdir, "testNetNs") + f, err := os.Create(tmpNSPath) + assert.NoError(err) + defer f.Close() + + type testData struct { + contents string + expectedResult string + } + + data := []testData{ + {fmt.Sprintf("711 26 0:3 net:[4026532008] %s rw shared:535 - nsfs nsfs rw", tmpNSPath), "net:[4026532008]"}, + {"711 26 0:3 net:[4026532008] /run/netns/ns123 rw shared:535 - tmpfs tmpfs rw", ""}, + {"a a a a a a a - b c d", ""}, + {"", ""}, + } + + for i, d := range data { + err := ioutil.WriteFile(mountFile, []byte(d.contents), 0640) + assert.NoError(err) + + path, err := getNetNsFromBindMount(tmpNSPath, mountFile) + assert.NoError(err, fmt.Sprintf("got %q, test data: %+v", path, d)) + + assert.Equal(d.expectedResult, path, "Test %d, expected %s, got %s", i, d.expectedResult, path) + } +} + +func TestHostNetworkingRequested(t *testing.T) { + assert := assert.New(t) + + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + if tc.NotValid(ktu.NeedKernelVersionGE("3.19.0")) { + t.Skip("skipping this test as it requires a greater kernel version") + } + + // Network namespace same as the host + selfNsPath := "/proc/self/ns/net" + isHostNs, err := hostNetworkingRequested(selfNsPath) + assert.NoError(err) + assert.True(isHostNs) + + // Non-existent netns path + nsPath := "/proc/123456789/ns/net" + _, err = hostNetworkingRequested(nsPath) + assert.Error(err) + + // Bind-mounted Netns + tmpdir, err := ioutil.TempDir("", "") + assert.NoError(err) + defer os.RemoveAll(tmpdir) + + // Create a bind mount to the current network namespace. + tmpFile := filepath.Join(tmpdir, "testNetNs") + f, err := os.Create(tmpFile) + assert.NoError(err) + defer f.Close() + + err = syscall.Mount(selfNsPath, tmpFile, "bind", syscall.MS_BIND, "") + assert.Nil(err) + + isHostNs, err = hostNetworkingRequested(tmpFile) + assert.NoError(err) + assert.True(isHostNs) + + syscall.Unmount(tmpFile, 0) +} + +func TestSetupNetworkNamespace(t *testing.T) { + if tc.NotValid(ktu.NeedRoot()) { + t.Skip(ktu.TestDisabledNeedRoot) + } + + assert := assert.New(t) + + // Network namespace same as the host + config := &vc.NetworkConfig{ + NetNSPath: "/proc/self/ns/net", + } + err := SetupNetworkNamespace(config) + assert.Error(err) + + // Non-existent netns path + config = &vc.NetworkConfig{ + NetNSPath: "/proc/123456789/ns/net", + } + err = SetupNetworkNamespace(config) + assert.Error(err) + + // Existent netns path + n, err := testutils.NewNS() + assert.NoError(err) + config = &vc.NetworkConfig{ + NetNSPath: n.Path(), + } + err = SetupNetworkNamespace(config) + assert.NoError(err) + n.Close() + + // Empty netns path + config = &vc.NetworkConfig{} + err = SetupNetworkNamespace(config) + assert.NoError(err) + n, err = ns.GetNS(config.NetNSPath) + assert.NoError(err) + assert.NotNil(n) + assert.True(config.NetNsCreated) + n.Close() + unix.Unmount(config.NetNSPath, unix.MNT_DETACH) + os.RemoveAll(config.NetNSPath) + + // Config with DisableNewNetNs + config = &vc.NetworkConfig{DisableNewNetNs: true} + err = SetupNetworkNamespace(config) + assert.NoError(err) +} diff --git a/src/runtime/pkg/katautils/oci.go b/src/runtime/pkg/katautils/oci.go new file mode 100644 index 000000000..6de8101e9 --- /dev/null +++ b/src/runtime/pkg/katautils/oci.go @@ -0,0 +1,110 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/kata-containers/runtime/virtcontainers/pkg/rootless" +) + +const ctrsMappingDirMode = os.FileMode(0750) + +var ctrsMapTreePath = "/var/run/kata-containers/containers-mapping" + +// SetCtrsMapTreePath let the testcases change the ctrsMapTreePath to a test dir +func SetCtrsMapTreePath(path string) { + ctrsMapTreePath = path +} + +// doUpdatePath returns whether a ctrsMapTreePath needs to be updated with a rootless prefix +func doUpdatePath() bool { + return rootless.IsRootless() && !strings.HasPrefix(ctrsMapTreePath, rootless.GetRootlessDir()) +} + +// FetchContainerIDMapping This function assumes it should find only one file inside the container +// ID directory. If there are several files, we could not determine which +// file name corresponds to the sandbox ID associated, and this would throw +// an error. +func FetchContainerIDMapping(containerID string) (string, error) { + if containerID == "" { + return "", fmt.Errorf("Missing container ID") + } + + if doUpdatePath() { + SetCtrsMapTreePath(filepath.Join(rootless.GetRootlessDir(), ctrsMapTreePath)) + } + + dirPath := filepath.Join(ctrsMapTreePath, containerID) + + files, err := ioutil.ReadDir(dirPath) + if err != nil { + if os.IsNotExist(err) { + return "", nil + } + + return "", err + } + + if len(files) != 1 { + return "", fmt.Errorf("Too many files (%d) in %q", len(files), dirPath) + } + + return files[0].Name(), nil +} + +// AddContainerIDMapping add a container id mapping to sandbox id +func AddContainerIDMapping(ctx context.Context, containerID, sandboxID string) error { + span, _ := Trace(ctx, "addContainerIDMapping") + defer span.Finish() + + if containerID == "" { + return fmt.Errorf("Missing container ID") + } + + if sandboxID == "" { + return fmt.Errorf("Missing sandbox ID") + } + + if doUpdatePath() { + SetCtrsMapTreePath(filepath.Join(rootless.GetRootlessDir(), ctrsMapTreePath)) + } + parentPath := filepath.Join(ctrsMapTreePath, containerID) + + if err := os.RemoveAll(parentPath); err != nil { + return err + } + + path := filepath.Join(parentPath, sandboxID) + + if err := os.MkdirAll(path, ctrsMappingDirMode); err != nil { + return err + } + + return nil +} + +// DelContainerIDMapping delete container id mapping from a sandbox +func DelContainerIDMapping(ctx context.Context, containerID string) error { + span, _ := Trace(ctx, "delContainerIDMapping") + defer span.Finish() + + if containerID == "" { + return fmt.Errorf("Missing container ID") + } + + if doUpdatePath() { + SetCtrsMapTreePath(filepath.Join(rootless.GetRootlessDir(), ctrsMapTreePath)) + } + path := filepath.Join(ctrsMapTreePath, containerID) + + return os.RemoveAll(path) +} diff --git a/src/runtime/pkg/katautils/oci_test.go b/src/runtime/pkg/katautils/oci_test.go new file mode 100644 index 000000000..a5e67a140 --- /dev/null +++ b/src/runtime/pkg/katautils/oci_test.go @@ -0,0 +1,140 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/kata-containers/runtime/virtcontainers/pkg/rootless" + "github.com/stretchr/testify/assert" +) + +func init() { + rootless.IsRootless = func() bool { return false } +} + +func createTempContainerIDMapping(containerID, sandboxID string) (string, error) { + tmpDir, err := ioutil.TempDir("", "containers-mapping") + if err != nil { + return "", err + } + ctrsMapTreePath = tmpDir + + path := filepath.Join(ctrsMapTreePath, containerID, sandboxID) + if err := os.MkdirAll(path, 0750); err != nil { + return "", err + } + + return tmpDir, nil +} + +func TestFetchContainerIDMappingContainerIDEmptyFailure(t *testing.T) { + assert := assert.New(t) + + sandboxID, err := FetchContainerIDMapping("") + assert.Error(err) + assert.Empty(sandboxID) +} + +func TestFetchContainerIDMappingEmptyMappingSuccess(t *testing.T) { + assert := assert.New(t) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + sandboxID, err := FetchContainerIDMapping(testContainerID) + assert.NoError(err) + assert.Empty(sandboxID) +} + +func TestFetchContainerIDMappingTooManyFilesFailure(t *testing.T) { + assert := assert.New(t) + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + err = os.MkdirAll(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID+"2"), ctrsMappingDirMode) + assert.NoError(err) + + sandboxID, err := FetchContainerIDMapping(testContainerID) + assert.Error(err) + assert.Empty(sandboxID) +} + +func TestFetchContainerIDMappingSuccess(t *testing.T) { + assert := assert.New(t) + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + sandboxID, err := FetchContainerIDMapping(testContainerID) + assert.NoError(err) + assert.Equal(sandboxID, testSandboxID) +} + +func TestAddContainerIDMappingContainerIDEmptyFailure(t *testing.T) { + assert := assert.New(t) + + err := AddContainerIDMapping(context.Background(), "", testSandboxID) + assert.Error(err) +} + +func TestAddContainerIDMappingSandboxIDEmptyFailure(t *testing.T) { + assert := assert.New(t) + + err := AddContainerIDMapping(context.Background(), testContainerID, "") + assert.Error(err) +} + +func TestAddContainerIDMappingSuccess(t *testing.T) { + assert := assert.New(t) + + path, err := ioutil.TempDir("", "containers-mapping") + assert.NoError(err) + defer os.RemoveAll(path) + ctrsMapTreePath = path + + _, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID)) + assert.True(os.IsNotExist(err)) + + err = AddContainerIDMapping(context.Background(), testContainerID, testSandboxID) + assert.NoError(err) + + _, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID)) + assert.NoError(err) +} + +func TestDelContainerIDMappingContainerIDEmptyFailure(t *testing.T) { + assert := assert.New(t) + + err := DelContainerIDMapping(context.Background(), "") + assert.Error(err) +} + +func TestDelContainerIDMappingSuccess(t *testing.T) { + assert := assert.New(t) + + path, err := createTempContainerIDMapping(testContainerID, testSandboxID) + assert.NoError(err) + defer os.RemoveAll(path) + + _, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID)) + assert.NoError(err) + + err = DelContainerIDMapping(context.Background(), testContainerID) + assert.NoError(err) + + _, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID)) + assert.True(os.IsNotExist(err)) +} diff --git a/src/runtime/pkg/katautils/tracing.go b/src/runtime/pkg/katautils/tracing.go new file mode 100644 index 000000000..2ed2d78f7 --- /dev/null +++ b/src/runtime/pkg/katautils/tracing.go @@ -0,0 +1,109 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "context" + "io" + + opentracing "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go/config" +) + +// Implements jaeger-client-go.Logger interface +type traceLogger struct { +} + +// tracerCloser contains a copy of the closer returned by createTracer() which +// is used by stopTracing(). +var tracerCloser io.Closer + +func (t traceLogger) Error(msg string) { + kataUtilsLogger.Error(msg) +} + +func (t traceLogger) Infof(msg string, args ...interface{}) { + kataUtilsLogger.Infof(msg, args...) +} + +// CreateTracer create a tracer +func CreateTracer(name string) (opentracing.Tracer, error) { + cfg := &config.Configuration{ + ServiceName: name, + + // If tracing is disabled, use a NOP trace implementation + Disabled: !tracing, + + // Note that span logging reporter option cannot be enabled as + // it pollutes the output stream which causes (atleast) the + // "state" command to fail under Docker. + Sampler: &config.SamplerConfig{ + Type: "const", + Param: 1, + }, + + // Ensure that Jaeger logs each span. + // This is essential as it is used by: + // + // https: //github.com/kata-containers/tests/blob/master/tracing/tracing-test.sh + Reporter: &config.ReporterConfig{ + LogSpans: tracing, + }, + } + + logger := traceLogger{} + + tracer, closer, err := cfg.NewTracer(config.Logger(logger)) + if err != nil { + return nil, err + } + + // save for stopTracing()'s exclusive use + tracerCloser = closer + + // Seems to be essential to ensure non-root spans are logged + opentracing.SetGlobalTracer(tracer) + + return tracer, nil +} + +// StopTracing ends all tracing, reporting the spans to the collector. +func StopTracing(ctx context.Context) { + if !tracing { + return + } + + span := opentracing.SpanFromContext(ctx) + + if span != nil { + span.Finish() + } + + // report all possible spans to the collector + if tracerCloser != nil { + tracerCloser.Close() + } +} + +// Trace creates a new tracing span based on the specified name and parent +// context. +func Trace(parent context.Context, name string) (opentracing.Span, context.Context) { + span, ctx := opentracing.StartSpanFromContext(parent, name) + + span.SetTag("source", "runtime") + span.SetTag("component", "cli") + + // This is slightly confusing: when tracing is disabled, trace spans + // are still created - but the tracer used is a NOP. Therefore, only + // display the message when tracing is really enabled. + if tracing { + // This log message is *essential*: it is used by: + // https: //github.com/kata-containers/tests/blob/master/tracing/tracing-test.sh + kataUtilsLogger.Debugf("created span %v", span) + } + + return span, ctx +} diff --git a/src/runtime/pkg/katautils/utils.go b/src/runtime/pkg/katautils/utils.go new file mode 100644 index 000000000..d89257699 --- /dev/null +++ b/src/runtime/pkg/katautils/utils.go @@ -0,0 +1,136 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "fmt" + "golang.org/x/sys/unix" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "syscall" +) + +// FileExists test is a file exiting or not +func FileExists(path string) bool { + if _, err := os.Stat(path); os.IsNotExist(err) { + return false + } + + return true +} + +// ResolvePath returns the fully resolved and expanded value of the +// specified path. +func ResolvePath(path string) (string, error) { + if path == "" { + return "", fmt.Errorf("path must be specified") + } + + absolute, err := filepath.Abs(path) + if err != nil { + return "", err + } + + resolved, err := filepath.EvalSymlinks(absolute) + if err != nil { + if os.IsNotExist(err) { + // Make the error clearer than the default + return "", fmt.Errorf("file %v does not exist", absolute) + } + + return "", err + } + + return resolved, nil +} + +// IsBlockDevice returns true if the give path is a block device +func IsBlockDevice(filePath string) bool { + var stat unix.Stat_t + + if filePath == "" { + return false + } + + devicePath, err := ResolvePath(filePath) + if err != nil { + return false + } + + if err := unix.Stat(devicePath, &stat); err != nil { + return false + } + + if stat.Mode&unix.S_IFBLK == unix.S_IFBLK { + return true + } + return false +} + +// fileSize returns the number of bytes in the specified file +func fileSize(file string) (int64, error) { + st := syscall.Stat_t{} + + err := syscall.Stat(file, &st) + if err != nil { + return 0, err + } + + return st.Size, nil +} + +// WriteFile write data into specified file +func WriteFile(filePath string, data string, fileMode os.FileMode) error { + // Normally dir should not be empty, one case is that cgroup subsystem + // is not mounted, we will get empty dir, and we want it fail here. + if filePath == "" { + return fmt.Errorf("no such file for %s", filePath) + } + + if err := ioutil.WriteFile(filePath, []byte(data), fileMode); err != nil { + return fmt.Errorf("failed to write %v to %v: %v", data, filePath, err) + } + + return nil +} + +// GetFileContents return the file contents as a string. +func GetFileContents(file string) (string, error) { + bytes, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + + return string(bytes), nil +} + +// RunCommandFull returns the commands space-trimmed standard output and +// error on success. Note that if the command fails, the requested output will +// still be returned, along with an error. +func RunCommandFull(args []string, includeStderr bool) (string, error) { + cmd := exec.Command(args[0], args[1:]...) + var err error + var bytes []byte + + if includeStderr { + bytes, err = cmd.CombinedOutput() + } else { + bytes, err = cmd.Output() + } + + trimmed := strings.TrimSpace(string(bytes)) + + return trimmed, err +} + +// RunCommand returns the commands space-trimmed standard output on success +func RunCommand(args []string) (string, error) { + return RunCommandFull(args, false) +} diff --git a/src/runtime/pkg/katautils/utils_test.go b/src/runtime/pkg/katautils/utils_test.go new file mode 100644 index 000000000..4806c18fa --- /dev/null +++ b/src/runtime/pkg/katautils/utils_test.go @@ -0,0 +1,367 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2018 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package katautils + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path" + "path/filepath" + "strings" + "syscall" + "testing" + + "github.com/kata-containers/runtime/virtcontainers/pkg/compatoci" + "github.com/stretchr/testify/assert" +) + +const ( + testDirMode = os.FileMode(0750) + testFileMode = os.FileMode(0640) + + // small docker image used to create root filesystems from + testDockerImage = "busybox" + + testSandboxID = "99999999-9999-9999-99999999999999999" + testContainerID = "1" + testBundle = "bundle" + specConfig = "config.json" +) + +var ( + testDir = "" +) + +func init() { + var err error + + fmt.Printf("INFO: creating test directory\n") + testDir, err = ioutil.TempDir("", fmt.Sprintf("%s-", name)) + if err != nil { + panic(fmt.Sprintf("ERROR: failed to create test directory: %v", err)) + } + + fmt.Printf("INFO: test directory is %v\n", testDir) + + testBundleDir = filepath.Join(testDir, testBundle) + err = os.MkdirAll(testBundleDir, testDirMode) + if err != nil { + panic(fmt.Sprintf("ERROR: failed to create bundle directory %v: %v", testBundleDir, err)) + } + + fmt.Printf("INFO: creating OCI bundle in %v for tests to use\n", testBundleDir) + err = realMakeOCIBundle(testBundleDir) + if err != nil { + panic(fmt.Sprintf("ERROR: failed to create OCI bundle: %v", err)) + } +} + +// createOCIConfig creates an OCI configuration (spec) file in +// the bundle directory specified (which must exist). +func createOCIConfig(bundleDir string) error { + if bundleDir == "" { + return errors.New("BUG: Need bundle directory") + } + + if !FileExists(bundleDir) { + return fmt.Errorf("BUG: Bundle directory %s does not exist", bundleDir) + } + + var configCmd string + + // Search for a suitable version of runc to use to generate + // the OCI config file. + for _, cmd := range []string{"docker-runc", "runc"} { + fullPath, err := exec.LookPath(cmd) + if err == nil { + configCmd = fullPath + break + } + } + + if configCmd == "" { + return errors.New("Cannot find command to generate OCI config file") + } + + _, err := RunCommand([]string{configCmd, "spec", "--bundle", bundleDir}) + if err != nil { + return err + } + + specFile := filepath.Join(bundleDir, specConfig) + if !FileExists(specFile) { + return fmt.Errorf("generated OCI config file does not exist: %v", specFile) + } + + return nil +} + +// realMakeOCIBundle will create an OCI bundle (including the "config.json" +// config file) in the directory specified (which must already exist). +// +// XXX: Note that tests should *NOT* call this function - they should +// XXX: instead call makeOCIBundle(). +func realMakeOCIBundle(bundleDir string) error { + if bundleDir == "" { + return errors.New("BUG: Need bundle directory") + } + + if !FileExists(bundleDir) { + return fmt.Errorf("BUG: Bundle directory %v does not exist", bundleDir) + } + + err := createOCIConfig(bundleDir) + if err != nil { + return err + } + + // Note the unusual parameter (a directory, not the config + // file to parse!) + spec, err := compatoci.ParseConfigJSON(bundleDir) + if err != nil { + return err + } + + // Determine the rootfs directory name the OCI config refers to + ociRootPath := spec.Root.Path + + rootfsDir := filepath.Join(bundleDir, ociRootPath) + + if strings.HasPrefix(ociRootPath, "/") { + return fmt.Errorf("Cannot handle absolute rootfs as bundle must be unique to each test") + } + + err = createRootfs(rootfsDir) + if err != nil { + return err + } + + return nil +} + +// createRootfs creates a minimal root filesystem below the specified +// directory. +func createRootfs(dir string) error { + var ( + output string + err error + ) + + ctrEngine := CtrEngine{} + for _, name := range DockerLikeCtrEngines { + fmt.Printf("INFO: checking for container engine: %s\n", name) + + output, err = ctrEngine.Init(name) + if err == nil { + break + } + } + + if ctrEngine.Name == "" { + panic(fmt.Sprintf("ERROR: Docker-like container engine not accessible to current user: %v (error %v)", + output, err)) + } + + err = os.MkdirAll(dir, testDirMode) + if err != nil { + return err + } + + container, err := ctrEngine.Create(testDockerImage) + if err != nil { + return err + } + + err = ctrEngine.GetRootfs(container, dir) + if err != nil { + return err + } + + // Clean up + _, err = ctrEngine.Rm(container) + if err != nil { + return err + } + + return nil +} + +func createFile(file, contents string) error { + return ioutil.WriteFile(file, []byte(contents), testFileMode) +} + +func createEmptyFile(path string) (err error) { + return ioutil.WriteFile(path, []byte(""), testFileMode) +} + +func TestUtilsResolvePathEmptyPath(t *testing.T) { + _, err := ResolvePath("") + assert.Error(t, err) +} + +func TestUtilsResolvePathValidPath(t *testing.T) { + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + target := path.Join(dir, "target") + linkDir := path.Join(dir, "a/b/c") + linkFile := path.Join(linkDir, "link") + + err = createEmptyFile(target) + assert.NoError(t, err) + + absolute, err := filepath.Abs(target) + assert.NoError(t, err) + + resolvedTarget, err := filepath.EvalSymlinks(absolute) + assert.NoError(t, err) + + err = os.MkdirAll(linkDir, testDirMode) + assert.NoError(t, err) + + err = syscall.Symlink(target, linkFile) + assert.NoError(t, err) + + resolvedLink, err := ResolvePath(linkFile) + assert.NoError(t, err) + + assert.Equal(t, resolvedTarget, resolvedLink) +} + +func TestUtilsResolvePathENOENT(t *testing.T) { + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + + target := path.Join(dir, "target") + linkDir := path.Join(dir, "a/b/c") + linkFile := path.Join(linkDir, "link") + + err = createEmptyFile(target) + assert.NoError(t, err) + + err = os.MkdirAll(linkDir, testDirMode) + assert.NoError(t, err) + + err = syscall.Symlink(target, linkFile) + assert.NoError(t, err) + + cwd, err := os.Getwd() + assert.NoError(t, err) + defer os.Chdir(cwd) + + err = os.Chdir(dir) + assert.NoError(t, err) + + err = os.RemoveAll(dir) + assert.NoError(t, err) + + _, err = ResolvePath(filepath.Base(linkFile)) + assert.Error(t, err) +} + +func TestFileSize(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir(testDir, "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + file := filepath.Join(dir, "foo") + + // ENOENT + _, err = fileSize(file) + assert.Error(err) + + err = createEmptyFile(file) + assert.NoError(err) + + // zero size + size, err := fileSize(file) + assert.NoError(err) + assert.Equal(size, int64(0)) + + msg := "hello" + msgLen := len(msg) + + err = WriteFile(file, msg, testFileMode) + assert.NoError(err) + + size, err = fileSize(file) + assert.NoError(err) + assert.Equal(size, int64(msgLen)) +} + +func TestWriteFileErrWriteFail(t *testing.T) { + assert := assert.New(t) + + err := WriteFile("", "", 0000) + assert.Error(err) +} + +func TestWriteFileErrNoPath(t *testing.T) { + assert := assert.New(t) + + dir, err := ioutil.TempDir(testDir, "") + assert.NoError(err) + defer os.RemoveAll(dir) + + // attempt to write a file over an existing directory + err = WriteFile(dir, "", 0000) + assert.Error(err) +} + +func TestGetFileContents(t *testing.T) { + type testData struct { + contents string + } + + data := []testData{ + {""}, + {" "}, + {"\n"}, + {"\n\n"}, + {"\n\n\n"}, + {"foo"}, + {"foo\nbar"}, + {"processor : 0\nvendor_id : GenuineIntel\n"}, + } + + dir, err := ioutil.TempDir(testDir, "") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + file := filepath.Join(dir, "foo") + + // file doesn't exist + _, err = GetFileContents(file) + assert.Error(t, err) + + for _, d := range data { + // create the file + err = ioutil.WriteFile(file, []byte(d.contents), testFileMode) + if err != nil { + t.Fatal(err) + } + defer os.Remove(file) + + contents, err := GetFileContents(file) + assert.NoError(t, err) + assert.Equal(t, contents, d.contents) + } +} diff --git a/src/runtime/pkg/signals/README.md b/src/runtime/pkg/signals/README.md new file mode 100644 index 000000000..87a79d8c3 --- /dev/null +++ b/src/runtime/pkg/signals/README.md @@ -0,0 +1,4 @@ +# Signals package + +The `signals` package contains common signal handling functions that are shared by +the binaries built in this repository. diff --git a/src/runtime/pkg/signals/signals.go b/src/runtime/pkg/signals/signals.go new file mode 100644 index 000000000..a405ad09d --- /dev/null +++ b/src/runtime/pkg/signals/signals.go @@ -0,0 +1,127 @@ +// Copyright 2018 Intel Corporation. +// +// SPDX-License-Identifier: Apache-2.0 +// + +package signals + +import ( + "bytes" + "fmt" + "os" + "os/signal" + "runtime/pprof" + "strings" + "syscall" + + "github.com/sirupsen/logrus" +) + +var signalLog = logrus.WithField("default-signal-logger", true) + +// CrashOnError causes a coredump to be produced when an internal error occurs +// or a fatal signal is received. +var CrashOnError = false + +// List of handled signals. +// +// The value is true if receiving the signal should be fatal. +var handledSignalsMap = map[syscall.Signal]bool{ + syscall.SIGABRT: true, + syscall.SIGBUS: true, + syscall.SIGILL: true, + syscall.SIGQUIT: true, + syscall.SIGSEGV: true, + syscall.SIGSTKFLT: true, + syscall.SIGSYS: true, + syscall.SIGTRAP: true, + syscall.SIGUSR1: false, +} + +// DieCb is the callback function type that needs to be defined for every call +// into the Die() function. This callback will be run as the first function of +// the Die() implementation. +type DieCb func() + +// SetLogger sets the custom logger to be used by this package. If not called, +// the package will create its own logger. +func SetLogger(logger *logrus.Entry) { + signalLog = logger +} + +// HandlePanic writes a message to the logger and then calls Die(). +func HandlePanic(dieCb DieCb) { + r := recover() + + if r != nil { + msg := fmt.Sprintf("%s", r) + signalLog.WithField("panic", msg).Error("fatal error") + + Die(dieCb) + } +} + +// Backtrace writes a multi-line backtrace to the logger. +func Backtrace() { + profiles := pprof.Profiles() + + buf := &bytes.Buffer{} + + for _, p := range profiles { + // The magic number requests a full stacktrace. See + // https://golang.org/pkg/runtime/pprof/#Profile.WriteTo. + pprof.Lookup(p.Name()).WriteTo(buf, 2) + } + + for _, line := range strings.Split(buf.String(), "\n") { + signalLog.Error(line) + } +} + +// FatalSignal returns true if the specified signal should cause the program +// to abort. +func FatalSignal(sig syscall.Signal) bool { + s, exists := handledSignalsMap[sig] + if !exists { + return false + } + + return s +} + +// NonFatalSignal returns true if the specified signal should simply cause the +// program to Backtrace() but continue running. +func NonFatalSignal(sig syscall.Signal) bool { + s, exists := handledSignalsMap[sig] + if !exists { + return false + } + + return !s +} + +// HandledSignals returns a list of signals the package can deal with. +func HandledSignals() []syscall.Signal { + var signals []syscall.Signal + + for sig := range handledSignalsMap { + signals = append(signals, sig) + } + + return signals +} + +// Die causes a backtrace to be produced. If CrashOnError is set a coredump +// will be produced, else the program will exit. +func Die(dieCb DieCb) { + dieCb() + + Backtrace() + + if CrashOnError { + signal.Reset(syscall.SIGABRT) + syscall.Kill(0, syscall.SIGABRT) + } + + os.Exit(1) +} diff --git a/src/runtime/pkg/signals/signals_test.go b/src/runtime/pkg/signals/signals_test.go new file mode 100644 index 000000000..1ea68546f --- /dev/null +++ b/src/runtime/pkg/signals/signals_test.go @@ -0,0 +1,140 @@ +// Copyright (c) 2018 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package signals + +import ( + "bytes" + "os" + "reflect" + goruntime "runtime" + "sort" + "strings" + "syscall" + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestSignalFatalSignal(t *testing.T) { + assert := assert.New(t) + + for sig, fatal := range handledSignalsMap { + result := NonFatalSignal(sig) + if fatal { + assert.False(result) + } else { + assert.True(result) + } + } +} + +func TestSignalHandledSignalsMap(t *testing.T) { + assert := assert.New(t) + + for sig, fatal := range handledSignalsMap { + result := FatalSignal(sig) + if fatal { + assert.True(result) + } else { + assert.False(result) + } + } +} + +func TestSignalHandledSignals(t *testing.T) { + assert := assert.New(t) + + var expected []syscall.Signal + + for sig := range handledSignalsMap { + expected = append(expected, sig) + } + + got := HandledSignals() + + sort.Slice(expected, func(i, j int) bool { + return int(expected[i]) < int(expected[j]) + }) + + sort.Slice(got, func(i, j int) bool { + return int(got[i]) < int(got[j]) + }) + + assert.True(reflect.DeepEqual(expected, got)) +} + +func TestSignalNonFatalSignal(t *testing.T) { + assert := assert.New(t) + + for sig, fatal := range handledSignalsMap { + result := NonFatalSignal(sig) + if fatal { + assert.False(result) + } else { + assert.True(result) + } + } +} + +func TestSignalFatalSignalInvalidSignal(t *testing.T) { + assert := assert.New(t) + + sig := syscall.SIGXCPU + + result := FatalSignal(sig) + assert.False(result) +} + +func TestSignalNonFatalSignalInvalidSignal(t *testing.T) { + assert := assert.New(t) + + sig := syscall.SIGXCPU + + result := NonFatalSignal(sig) + assert.False(result) +} + +func TestSignalBacktrace(t *testing.T) { + assert := assert.New(t) + + savedLog := signalLog + defer func() { + signalLog = savedLog + }() + + signalLog = logrus.WithFields(logrus.Fields{ + "name": "name", + "pid": os.Getpid(), + "source": "throttler", + "test-logger": true}) + + // create buffer to save logger output + buf := &bytes.Buffer{} + + savedOut := signalLog.Logger.Out + defer func() { + signalLog.Logger.Out = savedOut + }() + + // capture output to buffer + signalLog.Logger.Out = buf + + // determine name of *this* function + pc := make([]uintptr, 1) + goruntime.Callers(1, pc) + fn := goruntime.FuncForPC(pc[0]) + name := fn.Name() + + Backtrace() + + b := buf.String() + + // very basic tests to check if a backtrace was produced + assert.True(strings.Contains(b, "contention:")) + assert.True(strings.Contains(b, `level=error`)) + assert.True(strings.Contains(b, name)) +} diff --git a/src/runtime/protocols/cache/cache.pb.go b/src/runtime/protocols/cache/cache.pb.go new file mode 100644 index 000000000..c4120d97b --- /dev/null +++ b/src/runtime/protocols/cache/cache.pb.go @@ -0,0 +1,1280 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cache.proto + +/* + Package cache is a generated protocol buffer package. + + It is generated from these files: + cache.proto + + It has these top-level messages: + GrpcVMConfig + GrpcVM + GrpcStatus + GrpcVMStatus +*/ +package cache + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import google_protobuf "github.com/gogo/protobuf/types" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type GrpcVMConfig struct { + Data []byte `protobuf:"bytes,1,opt,name=Data,proto3" json:"Data,omitempty"` + AgentConfig []byte `protobuf:"bytes,2,opt,name=AgentConfig,proto3" json:"AgentConfig,omitempty"` +} + +func (m *GrpcVMConfig) Reset() { *m = GrpcVMConfig{} } +func (m *GrpcVMConfig) String() string { return proto.CompactTextString(m) } +func (*GrpcVMConfig) ProtoMessage() {} +func (*GrpcVMConfig) Descriptor() ([]byte, []int) { return fileDescriptorCache, []int{0} } + +func (m *GrpcVMConfig) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *GrpcVMConfig) GetAgentConfig() []byte { + if m != nil { + return m.AgentConfig + } + return nil +} + +type GrpcVM struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Hypervisor []byte `protobuf:"bytes,2,opt,name=hypervisor,proto3" json:"hypervisor,omitempty"` + ProxyPid int64 `protobuf:"varint,3,opt,name=proxyPid,proto3" json:"proxyPid,omitempty"` + ProxyURL string `protobuf:"bytes,4,opt,name=proxyURL,proto3" json:"proxyURL,omitempty"` + Cpu uint32 `protobuf:"varint,5,opt,name=cpu,proto3" json:"cpu,omitempty"` + Memory uint32 `protobuf:"varint,6,opt,name=memory,proto3" json:"memory,omitempty"` + CpuDelta uint32 `protobuf:"varint,7,opt,name=cpuDelta,proto3" json:"cpuDelta,omitempty"` +} + +func (m *GrpcVM) Reset() { *m = GrpcVM{} } +func (m *GrpcVM) String() string { return proto.CompactTextString(m) } +func (*GrpcVM) ProtoMessage() {} +func (*GrpcVM) Descriptor() ([]byte, []int) { return fileDescriptorCache, []int{1} } + +func (m *GrpcVM) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *GrpcVM) GetHypervisor() []byte { + if m != nil { + return m.Hypervisor + } + return nil +} + +func (m *GrpcVM) GetProxyPid() int64 { + if m != nil { + return m.ProxyPid + } + return 0 +} + +func (m *GrpcVM) GetProxyURL() string { + if m != nil { + return m.ProxyURL + } + return "" +} + +func (m *GrpcVM) GetCpu() uint32 { + if m != nil { + return m.Cpu + } + return 0 +} + +func (m *GrpcVM) GetMemory() uint32 { + if m != nil { + return m.Memory + } + return 0 +} + +func (m *GrpcVM) GetCpuDelta() uint32 { + if m != nil { + return m.CpuDelta + } + return 0 +} + +type GrpcStatus struct { + Pid int64 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + Vmstatus []*GrpcVMStatus `protobuf:"bytes,2,rep,name=vmstatus" json:"vmstatus,omitempty"` +} + +func (m *GrpcStatus) Reset() { *m = GrpcStatus{} } +func (m *GrpcStatus) String() string { return proto.CompactTextString(m) } +func (*GrpcStatus) ProtoMessage() {} +func (*GrpcStatus) Descriptor() ([]byte, []int) { return fileDescriptorCache, []int{2} } + +func (m *GrpcStatus) GetPid() int64 { + if m != nil { + return m.Pid + } + return 0 +} + +func (m *GrpcStatus) GetVmstatus() []*GrpcVMStatus { + if m != nil { + return m.Vmstatus + } + return nil +} + +type GrpcVMStatus struct { + Pid int64 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + Cpu uint32 `protobuf:"varint,2,opt,name=cpu,proto3" json:"cpu,omitempty"` + Memory uint32 `protobuf:"varint,3,opt,name=memory,proto3" json:"memory,omitempty"` +} + +func (m *GrpcVMStatus) Reset() { *m = GrpcVMStatus{} } +func (m *GrpcVMStatus) String() string { return proto.CompactTextString(m) } +func (*GrpcVMStatus) ProtoMessage() {} +func (*GrpcVMStatus) Descriptor() ([]byte, []int) { return fileDescriptorCache, []int{3} } + +func (m *GrpcVMStatus) GetPid() int64 { + if m != nil { + return m.Pid + } + return 0 +} + +func (m *GrpcVMStatus) GetCpu() uint32 { + if m != nil { + return m.Cpu + } + return 0 +} + +func (m *GrpcVMStatus) GetMemory() uint32 { + if m != nil { + return m.Memory + } + return 0 +} + +func init() { + proto.RegisterType((*GrpcVMConfig)(nil), "cache.GrpcVMConfig") + proto.RegisterType((*GrpcVM)(nil), "cache.GrpcVM") + proto.RegisterType((*GrpcStatus)(nil), "cache.GrpcStatus") + proto.RegisterType((*GrpcVMStatus)(nil), "cache.GrpcVMStatus") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for CacheService service + +type CacheServiceClient interface { + Config(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*GrpcVMConfig, error) + GetBaseVM(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*GrpcVM, error) + Status(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*GrpcStatus, error) + Quit(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*google_protobuf.Empty, error) +} + +type cacheServiceClient struct { + cc *grpc.ClientConn +} + +func NewCacheServiceClient(cc *grpc.ClientConn) CacheServiceClient { + return &cacheServiceClient{cc} +} + +func (c *cacheServiceClient) Config(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*GrpcVMConfig, error) { + out := new(GrpcVMConfig) + err := grpc.Invoke(ctx, "/cache.CacheService/Config", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheServiceClient) GetBaseVM(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*GrpcVM, error) { + out := new(GrpcVM) + err := grpc.Invoke(ctx, "/cache.CacheService/GetBaseVM", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheServiceClient) Status(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*GrpcStatus, error) { + out := new(GrpcStatus) + err := grpc.Invoke(ctx, "/cache.CacheService/Status", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cacheServiceClient) Quit(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*google_protobuf.Empty, error) { + out := new(google_protobuf.Empty) + err := grpc.Invoke(ctx, "/cache.CacheService/Quit", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for CacheService service + +type CacheServiceServer interface { + Config(context.Context, *google_protobuf.Empty) (*GrpcVMConfig, error) + GetBaseVM(context.Context, *google_protobuf.Empty) (*GrpcVM, error) + Status(context.Context, *google_protobuf.Empty) (*GrpcStatus, error) + Quit(context.Context, *google_protobuf.Empty) (*google_protobuf.Empty, error) +} + +func RegisterCacheServiceServer(s *grpc.Server, srv CacheServiceServer) { + s.RegisterService(&_CacheService_serviceDesc, srv) +} + +func _CacheService_Config_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(google_protobuf.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServiceServer).Config(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cache.CacheService/Config", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServiceServer).Config(ctx, req.(*google_protobuf.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _CacheService_GetBaseVM_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(google_protobuf.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServiceServer).GetBaseVM(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cache.CacheService/GetBaseVM", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServiceServer).GetBaseVM(ctx, req.(*google_protobuf.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _CacheService_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(google_protobuf.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServiceServer).Status(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cache.CacheService/Status", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServiceServer).Status(ctx, req.(*google_protobuf.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _CacheService_Quit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(google_protobuf.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CacheServiceServer).Quit(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cache.CacheService/Quit", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CacheServiceServer).Quit(ctx, req.(*google_protobuf.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +var _CacheService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "cache.CacheService", + HandlerType: (*CacheServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Config", + Handler: _CacheService_Config_Handler, + }, + { + MethodName: "GetBaseVM", + Handler: _CacheService_GetBaseVM_Handler, + }, + { + MethodName: "Status", + Handler: _CacheService_Status_Handler, + }, + { + MethodName: "Quit", + Handler: _CacheService_Quit_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "cache.proto", +} + +func (m *GrpcVMConfig) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GrpcVMConfig) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Data) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintCache(dAtA, i, uint64(len(m.Data))) + i += copy(dAtA[i:], m.Data) + } + if len(m.AgentConfig) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintCache(dAtA, i, uint64(len(m.AgentConfig))) + i += copy(dAtA[i:], m.AgentConfig) + } + return i, nil +} + +func (m *GrpcVM) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GrpcVM) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Id) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintCache(dAtA, i, uint64(len(m.Id))) + i += copy(dAtA[i:], m.Id) + } + if len(m.Hypervisor) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintCache(dAtA, i, uint64(len(m.Hypervisor))) + i += copy(dAtA[i:], m.Hypervisor) + } + if m.ProxyPid != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintCache(dAtA, i, uint64(m.ProxyPid)) + } + if len(m.ProxyURL) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintCache(dAtA, i, uint64(len(m.ProxyURL))) + i += copy(dAtA[i:], m.ProxyURL) + } + if m.Cpu != 0 { + dAtA[i] = 0x28 + i++ + i = encodeVarintCache(dAtA, i, uint64(m.Cpu)) + } + if m.Memory != 0 { + dAtA[i] = 0x30 + i++ + i = encodeVarintCache(dAtA, i, uint64(m.Memory)) + } + if m.CpuDelta != 0 { + dAtA[i] = 0x38 + i++ + i = encodeVarintCache(dAtA, i, uint64(m.CpuDelta)) + } + return i, nil +} + +func (m *GrpcStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GrpcStatus) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Pid != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintCache(dAtA, i, uint64(m.Pid)) + } + if len(m.Vmstatus) > 0 { + for _, msg := range m.Vmstatus { + dAtA[i] = 0x12 + i++ + i = encodeVarintCache(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *GrpcVMStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GrpcVMStatus) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Pid != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintCache(dAtA, i, uint64(m.Pid)) + } + if m.Cpu != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintCache(dAtA, i, uint64(m.Cpu)) + } + if m.Memory != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintCache(dAtA, i, uint64(m.Memory)) + } + return i, nil +} + +func encodeVarintCache(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *GrpcVMConfig) Size() (n int) { + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovCache(uint64(l)) + } + l = len(m.AgentConfig) + if l > 0 { + n += 1 + l + sovCache(uint64(l)) + } + return n +} + +func (m *GrpcVM) Size() (n int) { + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovCache(uint64(l)) + } + l = len(m.Hypervisor) + if l > 0 { + n += 1 + l + sovCache(uint64(l)) + } + if m.ProxyPid != 0 { + n += 1 + sovCache(uint64(m.ProxyPid)) + } + l = len(m.ProxyURL) + if l > 0 { + n += 1 + l + sovCache(uint64(l)) + } + if m.Cpu != 0 { + n += 1 + sovCache(uint64(m.Cpu)) + } + if m.Memory != 0 { + n += 1 + sovCache(uint64(m.Memory)) + } + if m.CpuDelta != 0 { + n += 1 + sovCache(uint64(m.CpuDelta)) + } + return n +} + +func (m *GrpcStatus) Size() (n int) { + var l int + _ = l + if m.Pid != 0 { + n += 1 + sovCache(uint64(m.Pid)) + } + if len(m.Vmstatus) > 0 { + for _, e := range m.Vmstatus { + l = e.Size() + n += 1 + l + sovCache(uint64(l)) + } + } + return n +} + +func (m *GrpcVMStatus) Size() (n int) { + var l int + _ = l + if m.Pid != 0 { + n += 1 + sovCache(uint64(m.Pid)) + } + if m.Cpu != 0 { + n += 1 + sovCache(uint64(m.Cpu)) + } + if m.Memory != 0 { + n += 1 + sovCache(uint64(m.Memory)) + } + return n +} + +func sovCache(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozCache(x uint64) (n int) { + return sovCache(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GrpcVMConfig) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GrpcVMConfig: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GrpcVMConfig: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCache + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AgentConfig", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCache + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AgentConfig = append(m.AgentConfig[:0], dAtA[iNdEx:postIndex]...) + if m.AgentConfig == nil { + m.AgentConfig = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCache(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCache + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GrpcVM) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GrpcVM: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GrpcVM: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCache + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hypervisor", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCache + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hypervisor = append(m.Hypervisor[:0], dAtA[iNdEx:postIndex]...) + if m.Hypervisor == nil { + m.Hypervisor = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProxyPid", wireType) + } + m.ProxyPid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProxyPid |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProxyURL", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCache + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProxyURL = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cpu", wireType) + } + m.Cpu = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Cpu |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType) + } + m.Memory = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Memory |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CpuDelta", wireType) + } + m.CpuDelta = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CpuDelta |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipCache(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCache + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GrpcStatus) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GrpcStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GrpcStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Vmstatus", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCache + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Vmstatus = append(m.Vmstatus, &GrpcVMStatus{}) + if err := m.Vmstatus[len(m.Vmstatus)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCache(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCache + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GrpcVMStatus) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GrpcVMStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GrpcVMStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cpu", wireType) + } + m.Cpu = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Cpu |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType) + } + m.Memory = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCache + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Memory |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipCache(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCache + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCache(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCache + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCache + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCache + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthCache + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCache + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipCache(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthCache = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCache = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("cache.proto", fileDescriptorCache) } + +var fileDescriptorCache = []byte{ + // 374 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x51, 0xcd, 0x0a, 0xda, 0x40, + 0x10, 0x66, 0x13, 0x4d, 0x75, 0x8c, 0xa5, 0xdd, 0x82, 0x2c, 0x16, 0x42, 0xc8, 0x29, 0xa7, 0x08, + 0x4a, 0x7b, 0xaf, 0x5a, 0x84, 0x52, 0x69, 0x1b, 0xa9, 0xf7, 0x18, 0xd7, 0x18, 0x30, 0xee, 0x92, + 0x6c, 0xa4, 0x79, 0xb1, 0x3e, 0x43, 0x8f, 0x7d, 0x84, 0xe2, 0xa1, 0xcf, 0x51, 0x76, 0xb3, 0x86, + 0x08, 0xcd, 0x6d, 0x66, 0xbe, 0x9f, 0xfd, 0x76, 0x06, 0x46, 0x71, 0x14, 0x9f, 0x69, 0xc0, 0x73, + 0x26, 0x18, 0xee, 0xab, 0x66, 0xfa, 0x36, 0x61, 0x2c, 0xb9, 0xd0, 0x99, 0x1a, 0x1e, 0xca, 0xd3, + 0x8c, 0x66, 0x5c, 0x54, 0x35, 0xc7, 0x5b, 0x83, 0xbd, 0xc9, 0x79, 0xbc, 0xdf, 0xae, 0xd8, 0xf5, + 0x94, 0x26, 0x18, 0x43, 0x6f, 0x1d, 0x89, 0x88, 0x20, 0x17, 0xf9, 0x76, 0xa8, 0x6a, 0xec, 0xc2, + 0xe8, 0x43, 0x42, 0xaf, 0xa2, 0xa6, 0x10, 0x43, 0x41, 0xed, 0x91, 0xf7, 0x13, 0x81, 0x55, 0xdb, + 0xe0, 0x97, 0x60, 0xa4, 0x47, 0x25, 0x1f, 0x86, 0x46, 0x7a, 0xc4, 0x0e, 0xc0, 0xb9, 0xe2, 0x34, + 0xbf, 0xa5, 0x05, 0xcb, 0xb5, 0xb6, 0x35, 0xc1, 0x53, 0x18, 0xf0, 0x9c, 0xfd, 0xa8, 0xbe, 0xa6, + 0x47, 0x62, 0xba, 0xc8, 0x37, 0xc3, 0xa6, 0x6f, 0xb0, 0xef, 0xe1, 0x67, 0xd2, 0x53, 0x8e, 0x4d, + 0x8f, 0x5f, 0x81, 0x19, 0xf3, 0x92, 0xf4, 0x5d, 0xe4, 0x8f, 0x43, 0x59, 0xe2, 0x09, 0x58, 0x19, + 0xcd, 0x58, 0x5e, 0x11, 0x4b, 0x0d, 0x75, 0x27, 0x5d, 0x62, 0x5e, 0xae, 0xe9, 0x45, 0x44, 0xe4, + 0x85, 0x42, 0x9a, 0xde, 0xfb, 0x02, 0x20, 0x73, 0xef, 0x44, 0x24, 0xca, 0x42, 0x7a, 0x72, 0x1d, + 0xde, 0x0c, 0x65, 0x89, 0x67, 0x30, 0xb8, 0x65, 0x85, 0x42, 0x89, 0xe1, 0x9a, 0xfe, 0x68, 0xfe, + 0x26, 0xa8, 0x57, 0x5c, 0x7f, 0xb7, 0x16, 0x86, 0x0d, 0xc9, 0xfb, 0xf4, 0xd8, 0x67, 0xa7, 0xa5, + 0x0e, 0x6e, 0xfc, 0x2f, 0xb8, 0xd9, 0x0e, 0x3e, 0xff, 0x8b, 0xc0, 0x5e, 0xc9, 0xc7, 0x76, 0x72, + 0x59, 0x31, 0xc5, 0xef, 0xc0, 0xd2, 0x67, 0x9a, 0x04, 0xf5, 0x51, 0x83, 0xc7, 0x51, 0x83, 0x8f, + 0xf2, 0xa8, 0xd3, 0xe7, 0x74, 0x9a, 0x3c, 0x87, 0xe1, 0x86, 0x8a, 0x65, 0x54, 0xd0, 0xfd, 0xb6, + 0x53, 0x39, 0x7e, 0x52, 0xe2, 0x05, 0x58, 0xfa, 0x07, 0x5d, 0x82, 0xd7, 0x2d, 0x81, 0xa6, 0xbe, + 0x87, 0xde, 0xb7, 0x32, 0x15, 0x9d, 0x92, 0x8e, 0xf9, 0xd2, 0xfe, 0x75, 0x77, 0xd0, 0xef, 0xbb, + 0x83, 0xfe, 0xdc, 0x1d, 0x74, 0xb0, 0x14, 0xba, 0xf8, 0x17, 0x00, 0x00, 0xff, 0xff, 0x40, 0x5e, + 0x19, 0x6e, 0xcc, 0x02, 0x00, 0x00, +} diff --git a/src/runtime/protocols/cache/cache.proto b/src/runtime/protocols/cache/cache.proto new file mode 100644 index 000000000..5c2aabdd4 --- /dev/null +++ b/src/runtime/protocols/cache/cache.proto @@ -0,0 +1,50 @@ +// +// Copyright 2019 HyperHQ Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// + +syntax = "proto3"; + +package cache; + +import "google/protobuf/empty.proto"; + +service CacheService { + rpc Config(google.protobuf.Empty) returns (GrpcVMConfig); + rpc GetBaseVM(google.protobuf.Empty) returns (GrpcVM); + rpc Status(google.protobuf.Empty) returns (GrpcStatus); + rpc Quit(google.protobuf.Empty) returns (google.protobuf.Empty); +} + +message GrpcVMConfig { + bytes Data = 1; + bytes AgentConfig = 2; +} + +message GrpcVM { + string id = 1; + + bytes hypervisor = 2; + + int64 proxyPid = 3; + string proxyURL = 4; + + uint32 cpu = 5; + uint32 memory = 6; + + uint32 cpuDelta = 7; +} + +message GrpcStatus { + int64 pid = 1; + + repeated GrpcVMStatus vmstatus = 2; +} + +message GrpcVMStatus { + int64 pid = 1; + + uint32 cpu = 2; + uint32 memory = 3; +} diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/.gitignore b/src/runtime/vendor/github.com/BurntSushi/toml/.gitignore new file mode 100644 index 000000000..0cd380037 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/.gitignore @@ -0,0 +1,5 @@ +TAGS +tags +.*.swp +tomlcheck/tomlcheck +toml.test diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/.travis.yml b/src/runtime/vendor/github.com/BurntSushi/toml/.travis.yml new file mode 100644 index 000000000..8b8afc4f0 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/.travis.yml @@ -0,0 +1,15 @@ +language: go +go: + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - 1.6 + - tip +install: + - go install ./... + - go get github.com/BurntSushi/toml-test +script: + - export PATH="$PATH:$HOME/gopath/bin" + - make test diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/COMPATIBLE b/src/runtime/vendor/github.com/BurntSushi/toml/COMPATIBLE new file mode 100644 index 000000000..6efcfd0ce --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/COMPATIBLE @@ -0,0 +1,3 @@ +Compatible with TOML version +[v0.4.0](https://github.com/toml-lang/toml/blob/v0.4.0/versions/en/toml-v0.4.0.md) + diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/COPYING b/src/runtime/vendor/github.com/BurntSushi/toml/COPYING new file mode 100644 index 000000000..01b574320 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/COPYING @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 TOML authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/Makefile b/src/runtime/vendor/github.com/BurntSushi/toml/Makefile new file mode 100644 index 000000000..3600848d3 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/Makefile @@ -0,0 +1,19 @@ +install: + go install ./... + +test: install + go test -v + toml-test toml-test-decoder + toml-test -encoder toml-test-encoder + +fmt: + gofmt -w *.go */*.go + colcheck *.go */*.go + +tags: + find ./ -name '*.go' -print0 | xargs -0 gotags > TAGS + +push: + git push origin master + git push github master + diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/README.md b/src/runtime/vendor/github.com/BurntSushi/toml/README.md new file mode 100644 index 000000000..7c1b37ecc --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/README.md @@ -0,0 +1,218 @@ +## TOML parser and encoder for Go with reflection + +TOML stands for Tom's Obvious, Minimal Language. This Go package provides a +reflection interface similar to Go's standard library `json` and `xml` +packages. This package also supports the `encoding.TextUnmarshaler` and +`encoding.TextMarshaler` interfaces so that you can define custom data +representations. (There is an example of this below.) + +Spec: https://github.com/toml-lang/toml + +Compatible with TOML version +[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md) + +Documentation: https://godoc.org/github.com/BurntSushi/toml + +Installation: + +```bash +go get github.com/BurntSushi/toml +``` + +Try the toml validator: + +```bash +go get github.com/BurntSushi/toml/cmd/tomlv +tomlv some-toml-file.toml +``` + +[![Build Status](https://travis-ci.org/BurntSushi/toml.svg?branch=master)](https://travis-ci.org/BurntSushi/toml) [![GoDoc](https://godoc.org/github.com/BurntSushi/toml?status.svg)](https://godoc.org/github.com/BurntSushi/toml) + +### Testing + +This package passes all tests in +[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder +and the encoder. + +### Examples + +This package works similarly to how the Go standard library handles `XML` +and `JSON`. Namely, data is loaded into Go values via reflection. + +For the simplest example, consider some TOML file as just a list of keys +and values: + +```toml +Age = 25 +Cats = [ "Cauchy", "Plato" ] +Pi = 3.14 +Perfection = [ 6, 28, 496, 8128 ] +DOB = 1987-07-05T05:45:00Z +``` + +Which could be defined in Go as: + +```go +type Config struct { + Age int + Cats []string + Pi float64 + Perfection []int + DOB time.Time // requires `import time` +} +``` + +And then decoded with: + +```go +var conf Config +if _, err := toml.Decode(tomlData, &conf); err != nil { + // handle error +} +``` + +You can also use struct tags if your struct field name doesn't map to a TOML +key value directly: + +```toml +some_key_NAME = "wat" +``` + +```go +type TOML struct { + ObscureKey string `toml:"some_key_NAME"` +} +``` + +### Using the `encoding.TextUnmarshaler` interface + +Here's an example that automatically parses duration strings into +`time.Duration` values: + +```toml +[[song]] +name = "Thunder Road" +duration = "4m49s" + +[[song]] +name = "Stairway to Heaven" +duration = "8m03s" +``` + +Which can be decoded with: + +```go +type song struct { + Name string + Duration duration +} +type songs struct { + Song []song +} +var favorites songs +if _, err := toml.Decode(blob, &favorites); err != nil { + log.Fatal(err) +} + +for _, s := range favorites.Song { + fmt.Printf("%s (%s)\n", s.Name, s.Duration) +} +``` + +And you'll also need a `duration` type that satisfies the +`encoding.TextUnmarshaler` interface: + +```go +type duration struct { + time.Duration +} + +func (d *duration) UnmarshalText(text []byte) error { + var err error + d.Duration, err = time.ParseDuration(string(text)) + return err +} +``` + +### More complex usage + +Here's an example of how to load the example from the official spec page: + +```toml +# This is a TOML document. Boom. + +title = "TOML Example" + +[owner] +name = "Tom Preston-Werner" +organization = "GitHub" +bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." +dob = 1979-05-27T07:32:00Z # First class dates? Why not? + +[database] +server = "192.168.1.1" +ports = [ 8001, 8001, 8002 ] +connection_max = 5000 +enabled = true + +[servers] + + # You can indent as you please. Tabs or spaces. TOML don't care. + [servers.alpha] + ip = "10.0.0.1" + dc = "eqdc10" + + [servers.beta] + ip = "10.0.0.2" + dc = "eqdc10" + +[clients] +data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it + +# Line breaks are OK when inside arrays +hosts = [ + "alpha", + "omega" +] +``` + +And the corresponding Go types are: + +```go +type tomlConfig struct { + Title string + Owner ownerInfo + DB database `toml:"database"` + Servers map[string]server + Clients clients +} + +type ownerInfo struct { + Name string + Org string `toml:"organization"` + Bio string + DOB time.Time +} + +type database struct { + Server string + Ports []int + ConnMax int `toml:"connection_max"` + Enabled bool +} + +type server struct { + IP string + DC string +} + +type clients struct { + Data [][]interface{} + Hosts []string +} +``` + +Note that a case insensitive match will be tried if an exact match can't be +found. + +A working example of the above can be found in `_examples/example.{go,toml}`. diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/decode.go b/src/runtime/vendor/github.com/BurntSushi/toml/decode.go new file mode 100644 index 000000000..b0fd51d5b --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/decode.go @@ -0,0 +1,509 @@ +package toml + +import ( + "fmt" + "io" + "io/ioutil" + "math" + "reflect" + "strings" + "time" +) + +func e(format string, args ...interface{}) error { + return fmt.Errorf("toml: "+format, args...) +} + +// Unmarshaler is the interface implemented by objects that can unmarshal a +// TOML description of themselves. +type Unmarshaler interface { + UnmarshalTOML(interface{}) error +} + +// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`. +func Unmarshal(p []byte, v interface{}) error { + _, err := Decode(string(p), v) + return err +} + +// Primitive is a TOML value that hasn't been decoded into a Go value. +// When using the various `Decode*` functions, the type `Primitive` may +// be given to any value, and its decoding will be delayed. +// +// A `Primitive` value can be decoded using the `PrimitiveDecode` function. +// +// The underlying representation of a `Primitive` value is subject to change. +// Do not rely on it. +// +// N.B. Primitive values are still parsed, so using them will only avoid +// the overhead of reflection. They can be useful when you don't know the +// exact type of TOML data until run time. +type Primitive struct { + undecoded interface{} + context Key +} + +// DEPRECATED! +// +// Use MetaData.PrimitiveDecode instead. +func PrimitiveDecode(primValue Primitive, v interface{}) error { + md := MetaData{decoded: make(map[string]bool)} + return md.unify(primValue.undecoded, rvalue(v)) +} + +// PrimitiveDecode is just like the other `Decode*` functions, except it +// decodes a TOML value that has already been parsed. Valid primitive values +// can *only* be obtained from values filled by the decoder functions, +// including this method. (i.e., `v` may contain more `Primitive` +// values.) +// +// Meta data for primitive values is included in the meta data returned by +// the `Decode*` functions with one exception: keys returned by the Undecoded +// method will only reflect keys that were decoded. Namely, any keys hidden +// behind a Primitive will be considered undecoded. Executing this method will +// update the undecoded keys in the meta data. (See the example.) +func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { + md.context = primValue.context + defer func() { md.context = nil }() + return md.unify(primValue.undecoded, rvalue(v)) +} + +// Decode will decode the contents of `data` in TOML format into a pointer +// `v`. +// +// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be +// used interchangeably.) +// +// TOML arrays of tables correspond to either a slice of structs or a slice +// of maps. +// +// TOML datetimes correspond to Go `time.Time` values. +// +// All other TOML types (float, string, int, bool and array) correspond +// to the obvious Go types. +// +// An exception to the above rules is if a type implements the +// encoding.TextUnmarshaler interface. In this case, any primitive TOML value +// (floats, strings, integers, booleans and datetimes) will be converted to +// a byte string and given to the value's UnmarshalText method. See the +// Unmarshaler example for a demonstration with time duration strings. +// +// Key mapping +// +// TOML keys can map to either keys in a Go map or field names in a Go +// struct. The special `toml` struct tag may be used to map TOML keys to +// struct fields that don't match the key name exactly. (See the example.) +// A case insensitive match to struct names will be tried if an exact match +// can't be found. +// +// The mapping between TOML values and Go values is loose. That is, there +// may exist TOML values that cannot be placed into your representation, and +// there may be parts of your representation that do not correspond to +// TOML values. This loose mapping can be made stricter by using the IsDefined +// and/or Undecoded methods on the MetaData returned. +// +// This decoder will not handle cyclic types. If a cyclic type is passed, +// `Decode` will not terminate. +func Decode(data string, v interface{}) (MetaData, error) { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr { + return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v)) + } + if rv.IsNil() { + return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v)) + } + p, err := parse(data) + if err != nil { + return MetaData{}, err + } + md := MetaData{ + p.mapping, p.types, p.ordered, + make(map[string]bool, len(p.ordered)), nil, + } + return md, md.unify(p.mapping, indirect(rv)) +} + +// DecodeFile is just like Decode, except it will automatically read the +// contents of the file at `fpath` and decode it for you. +func DecodeFile(fpath string, v interface{}) (MetaData, error) { + bs, err := ioutil.ReadFile(fpath) + if err != nil { + return MetaData{}, err + } + return Decode(string(bs), v) +} + +// DecodeReader is just like Decode, except it will consume all bytes +// from the reader and decode it for you. +func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { + bs, err := ioutil.ReadAll(r) + if err != nil { + return MetaData{}, err + } + return Decode(string(bs), v) +} + +// unify performs a sort of type unification based on the structure of `rv`, +// which is the client representation. +// +// Any type mismatch produces an error. Finding a type that we don't know +// how to handle produces an unsupported type error. +func (md *MetaData) unify(data interface{}, rv reflect.Value) error { + + // Special case. Look for a `Primitive` value. + if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() { + // Save the undecoded data and the key context into the primitive + // value. + context := make(Key, len(md.context)) + copy(context, md.context) + rv.Set(reflect.ValueOf(Primitive{ + undecoded: data, + context: context, + })) + return nil + } + + // Special case. Unmarshaler Interface support. + if rv.CanAddr() { + if v, ok := rv.Addr().Interface().(Unmarshaler); ok { + return v.UnmarshalTOML(data) + } + } + + // Special case. Handle time.Time values specifically. + // TODO: Remove this code when we decide to drop support for Go 1.1. + // This isn't necessary in Go 1.2 because time.Time satisfies the encoding + // interfaces. + if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) { + return md.unifyDatetime(data, rv) + } + + // Special case. Look for a value satisfying the TextUnmarshaler interface. + if v, ok := rv.Interface().(TextUnmarshaler); ok { + return md.unifyText(data, v) + } + // BUG(burntsushi) + // The behavior here is incorrect whenever a Go type satisfies the + // encoding.TextUnmarshaler interface but also corresponds to a TOML + // hash or array. In particular, the unmarshaler should only be applied + // to primitive TOML values. But at this point, it will be applied to + // all kinds of values and produce an incorrect error whenever those values + // are hashes or arrays (including arrays of tables). + + k := rv.Kind() + + // laziness + if k >= reflect.Int && k <= reflect.Uint64 { + return md.unifyInt(data, rv) + } + switch k { + case reflect.Ptr: + elem := reflect.New(rv.Type().Elem()) + err := md.unify(data, reflect.Indirect(elem)) + if err != nil { + return err + } + rv.Set(elem) + return nil + case reflect.Struct: + return md.unifyStruct(data, rv) + case reflect.Map: + return md.unifyMap(data, rv) + case reflect.Array: + return md.unifyArray(data, rv) + case reflect.Slice: + return md.unifySlice(data, rv) + case reflect.String: + return md.unifyString(data, rv) + case reflect.Bool: + return md.unifyBool(data, rv) + case reflect.Interface: + // we only support empty interfaces. + if rv.NumMethod() > 0 { + return e("unsupported type %s", rv.Type()) + } + return md.unifyAnything(data, rv) + case reflect.Float32: + fallthrough + case reflect.Float64: + return md.unifyFloat64(data, rv) + } + return e("unsupported type %s", rv.Kind()) +} + +func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { + tmap, ok := mapping.(map[string]interface{}) + if !ok { + if mapping == nil { + return nil + } + return e("type mismatch for %s: expected table but found %T", + rv.Type().String(), mapping) + } + + for key, datum := range tmap { + var f *field + fields := cachedTypeFields(rv.Type()) + for i := range fields { + ff := &fields[i] + if ff.name == key { + f = ff + break + } + if f == nil && strings.EqualFold(ff.name, key) { + f = ff + } + } + if f != nil { + subv := rv + for _, i := range f.index { + subv = indirect(subv.Field(i)) + } + if isUnifiable(subv) { + md.decoded[md.context.add(key).String()] = true + md.context = append(md.context, key) + if err := md.unify(datum, subv); err != nil { + return err + } + md.context = md.context[0 : len(md.context)-1] + } else if f.name != "" { + // Bad user! No soup for you! + return e("cannot write unexported field %s.%s", + rv.Type().String(), f.name) + } + } + } + return nil +} + +func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { + tmap, ok := mapping.(map[string]interface{}) + if !ok { + if tmap == nil { + return nil + } + return badtype("map", mapping) + } + if rv.IsNil() { + rv.Set(reflect.MakeMap(rv.Type())) + } + for k, v := range tmap { + md.decoded[md.context.add(k).String()] = true + md.context = append(md.context, k) + + rvkey := indirect(reflect.New(rv.Type().Key())) + rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) + if err := md.unify(v, rvval); err != nil { + return err + } + md.context = md.context[0 : len(md.context)-1] + + rvkey.SetString(k) + rv.SetMapIndex(rvkey, rvval) + } + return nil +} + +func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + if !datav.IsValid() { + return nil + } + return badtype("slice", data) + } + sliceLen := datav.Len() + if sliceLen != rv.Len() { + return e("expected array length %d; got TOML array of length %d", + rv.Len(), sliceLen) + } + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + if !datav.IsValid() { + return nil + } + return badtype("slice", data) + } + n := datav.Len() + if rv.IsNil() || rv.Cap() < n { + rv.Set(reflect.MakeSlice(rv.Type(), n, n)) + } + rv.SetLen(n) + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySliceArray(data, rv reflect.Value) error { + sliceLen := data.Len() + for i := 0; i < sliceLen; i++ { + v := data.Index(i).Interface() + sliceval := indirect(rv.Index(i)) + if err := md.unify(v, sliceval); err != nil { + return err + } + } + return nil +} + +func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error { + if _, ok := data.(time.Time); ok { + rv.Set(reflect.ValueOf(data)) + return nil + } + return badtype("time.Time", data) +} + +func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error { + if s, ok := data.(string); ok { + rv.SetString(s) + return nil + } + return badtype("string", data) +} + +func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { + if num, ok := data.(float64); ok { + switch rv.Kind() { + case reflect.Float32: + fallthrough + case reflect.Float64: + rv.SetFloat(num) + default: + panic("bug") + } + return nil + } + return badtype("float", data) +} + +func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { + if num, ok := data.(int64); ok { + if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 { + switch rv.Kind() { + case reflect.Int, reflect.Int64: + // No bounds checking necessary. + case reflect.Int8: + if num < math.MinInt8 || num > math.MaxInt8 { + return e("value %d is out of range for int8", num) + } + case reflect.Int16: + if num < math.MinInt16 || num > math.MaxInt16 { + return e("value %d is out of range for int16", num) + } + case reflect.Int32: + if num < math.MinInt32 || num > math.MaxInt32 { + return e("value %d is out of range for int32", num) + } + } + rv.SetInt(num) + } else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 { + unum := uint64(num) + switch rv.Kind() { + case reflect.Uint, reflect.Uint64: + // No bounds checking necessary. + case reflect.Uint8: + if num < 0 || unum > math.MaxUint8 { + return e("value %d is out of range for uint8", num) + } + case reflect.Uint16: + if num < 0 || unum > math.MaxUint16 { + return e("value %d is out of range for uint16", num) + } + case reflect.Uint32: + if num < 0 || unum > math.MaxUint32 { + return e("value %d is out of range for uint32", num) + } + } + rv.SetUint(unum) + } else { + panic("unreachable") + } + return nil + } + return badtype("integer", data) +} + +func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { + if b, ok := data.(bool); ok { + rv.SetBool(b) + return nil + } + return badtype("boolean", data) +} + +func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { + rv.Set(reflect.ValueOf(data)) + return nil +} + +func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error { + var s string + switch sdata := data.(type) { + case TextMarshaler: + text, err := sdata.MarshalText() + if err != nil { + return err + } + s = string(text) + case fmt.Stringer: + s = sdata.String() + case string: + s = sdata + case bool: + s = fmt.Sprintf("%v", sdata) + case int64: + s = fmt.Sprintf("%d", sdata) + case float64: + s = fmt.Sprintf("%f", sdata) + default: + return badtype("primitive (string-like)", data) + } + if err := v.UnmarshalText([]byte(s)); err != nil { + return err + } + return nil +} + +// rvalue returns a reflect.Value of `v`. All pointers are resolved. +func rvalue(v interface{}) reflect.Value { + return indirect(reflect.ValueOf(v)) +} + +// indirect returns the value pointed to by a pointer. +// Pointers are followed until the value is not a pointer. +// New values are allocated for each nil pointer. +// +// An exception to this rule is if the value satisfies an interface of +// interest to us (like encoding.TextUnmarshaler). +func indirect(v reflect.Value) reflect.Value { + if v.Kind() != reflect.Ptr { + if v.CanSet() { + pv := v.Addr() + if _, ok := pv.Interface().(TextUnmarshaler); ok { + return pv + } + } + return v + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + return indirect(reflect.Indirect(v)) +} + +func isUnifiable(rv reflect.Value) bool { + if rv.CanSet() { + return true + } + if _, ok := rv.Interface().(TextUnmarshaler); ok { + return true + } + return false +} + +func badtype(expected string, data interface{}) error { + return e("cannot load TOML value of type %T into a Go %s", data, expected) +} diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/decode_meta.go b/src/runtime/vendor/github.com/BurntSushi/toml/decode_meta.go new file mode 100644 index 000000000..b9914a679 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/decode_meta.go @@ -0,0 +1,121 @@ +package toml + +import "strings" + +// MetaData allows access to meta information about TOML data that may not +// be inferrable via reflection. In particular, whether a key has been defined +// and the TOML type of a key. +type MetaData struct { + mapping map[string]interface{} + types map[string]tomlType + keys []Key + decoded map[string]bool + context Key // Used only during decoding. +} + +// IsDefined returns true if the key given exists in the TOML data. The key +// should be specified hierarchially. e.g., +// +// // access the TOML key 'a.b.c' +// IsDefined("a", "b", "c") +// +// IsDefined will return false if an empty key given. Keys are case sensitive. +func (md *MetaData) IsDefined(key ...string) bool { + if len(key) == 0 { + return false + } + + var hash map[string]interface{} + var ok bool + var hashOrVal interface{} = md.mapping + for _, k := range key { + if hash, ok = hashOrVal.(map[string]interface{}); !ok { + return false + } + if hashOrVal, ok = hash[k]; !ok { + return false + } + } + return true +} + +// Type returns a string representation of the type of the key specified. +// +// Type will return the empty string if given an empty key or a key that +// does not exist. Keys are case sensitive. +func (md *MetaData) Type(key ...string) string { + fullkey := strings.Join(key, ".") + if typ, ok := md.types[fullkey]; ok { + return typ.typeString() + } + return "" +} + +// Key is the type of any TOML key, including key groups. Use (MetaData).Keys +// to get values of this type. +type Key []string + +func (k Key) String() string { + return strings.Join(k, ".") +} + +func (k Key) maybeQuotedAll() string { + var ss []string + for i := range k { + ss = append(ss, k.maybeQuoted(i)) + } + return strings.Join(ss, ".") +} + +func (k Key) maybeQuoted(i int) string { + quote := false + for _, c := range k[i] { + if !isBareKeyChar(c) { + quote = true + break + } + } + if quote { + return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\"" + } + return k[i] +} + +func (k Key) add(piece string) Key { + newKey := make(Key, len(k)+1) + copy(newKey, k) + newKey[len(k)] = piece + return newKey +} + +// Keys returns a slice of every key in the TOML data, including key groups. +// Each key is itself a slice, where the first element is the top of the +// hierarchy and the last is the most specific. +// +// The list will have the same order as the keys appeared in the TOML data. +// +// All keys returned are non-empty. +func (md *MetaData) Keys() []Key { + return md.keys +} + +// Undecoded returns all keys that have not been decoded in the order in which +// they appear in the original TOML document. +// +// This includes keys that haven't been decoded because of a Primitive value. +// Once the Primitive value is decoded, the keys will be considered decoded. +// +// Also note that decoding into an empty interface will result in no decoding, +// and so no keys will be considered decoded. +// +// In this sense, the Undecoded keys correspond to keys in the TOML document +// that do not have a concrete type in your representation. +func (md *MetaData) Undecoded() []Key { + undecoded := make([]Key, 0, len(md.keys)) + for _, key := range md.keys { + if !md.decoded[key.String()] { + undecoded = append(undecoded, key) + } + } + return undecoded +} diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/doc.go b/src/runtime/vendor/github.com/BurntSushi/toml/doc.go new file mode 100644 index 000000000..b371f396e --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/doc.go @@ -0,0 +1,27 @@ +/* +Package toml provides facilities for decoding and encoding TOML configuration +files via reflection. There is also support for delaying decoding with +the Primitive type, and querying the set of keys in a TOML document with the +MetaData type. + +The specification implemented: https://github.com/toml-lang/toml + +The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify +whether a file is a valid TOML document. It can also be used to print the +type of each key in a TOML document. + +Testing + +There are two important types of tests used for this package. The first is +contained inside '*_test.go' files and uses the standard Go unit testing +framework. These tests are primarily devoted to holistically testing the +decoder and encoder. + +The second type of testing is used to verify the implementation's adherence +to the TOML specification. These tests have been factored into their own +project: https://github.com/BurntSushi/toml-test + +The reason the tests are in a separate project is so that they can be used by +any implementation of TOML. Namely, it is language agnostic. +*/ +package toml diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/encode.go b/src/runtime/vendor/github.com/BurntSushi/toml/encode.go new file mode 100644 index 000000000..d905c21a2 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/encode.go @@ -0,0 +1,568 @@ +package toml + +import ( + "bufio" + "errors" + "fmt" + "io" + "reflect" + "sort" + "strconv" + "strings" + "time" +) + +type tomlEncodeError struct{ error } + +var ( + errArrayMixedElementTypes = errors.New( + "toml: cannot encode array with mixed element types") + errArrayNilElement = errors.New( + "toml: cannot encode array with nil element") + errNonString = errors.New( + "toml: cannot encode a map with non-string key type") + errAnonNonStruct = errors.New( + "toml: cannot encode an anonymous field that is not a struct") + errArrayNoTable = errors.New( + "toml: TOML array element cannot contain a table") + errNoKey = errors.New( + "toml: top-level values must be Go maps or structs") + errAnything = errors.New("") // used in testing +) + +var quotedReplacer = strings.NewReplacer( + "\t", "\\t", + "\n", "\\n", + "\r", "\\r", + "\"", "\\\"", + "\\", "\\\\", +) + +// Encoder controls the encoding of Go values to a TOML document to some +// io.Writer. +// +// The indentation level can be controlled with the Indent field. +type Encoder struct { + // A single indentation level. By default it is two spaces. + Indent string + + // hasWritten is whether we have written any output to w yet. + hasWritten bool + w *bufio.Writer +} + +// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer +// given. By default, a single indentation level is 2 spaces. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + w: bufio.NewWriter(w), + Indent: " ", + } +} + +// Encode writes a TOML representation of the Go value to the underlying +// io.Writer. If the value given cannot be encoded to a valid TOML document, +// then an error is returned. +// +// The mapping between Go values and TOML values should be precisely the same +// as for the Decode* functions. Similarly, the TextMarshaler interface is +// supported by encoding the resulting bytes as strings. (If you want to write +// arbitrary binary data then you will need to use something like base64 since +// TOML does not have any binary types.) +// +// When encoding TOML hashes (i.e., Go maps or structs), keys without any +// sub-hashes are encoded first. +// +// If a Go map is encoded, then its keys are sorted alphabetically for +// deterministic output. More control over this behavior may be provided if +// there is demand for it. +// +// Encoding Go values without a corresponding TOML representation---like map +// types with non-string keys---will cause an error to be returned. Similarly +// for mixed arrays/slices, arrays/slices with nil elements, embedded +// non-struct types and nested slices containing maps or structs. +// (e.g., [][]map[string]string is not allowed but []map[string]string is OK +// and so is []map[string][]string.) +func (enc *Encoder) Encode(v interface{}) error { + rv := eindirect(reflect.ValueOf(v)) + if err := enc.safeEncode(Key([]string{}), rv); err != nil { + return err + } + return enc.w.Flush() +} + +func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) { + defer func() { + if r := recover(); r != nil { + if terr, ok := r.(tomlEncodeError); ok { + err = terr.error + return + } + panic(r) + } + }() + enc.encode(key, rv) + return nil +} + +func (enc *Encoder) encode(key Key, rv reflect.Value) { + // Special case. Time needs to be in ISO8601 format. + // Special case. If we can marshal the type to text, then we used that. + // Basically, this prevents the encoder for handling these types as + // generic structs (or whatever the underlying type of a TextMarshaler is). + switch rv.Interface().(type) { + case time.Time, TextMarshaler: + enc.keyEqElement(key, rv) + return + } + + k := rv.Kind() + switch k { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64, + reflect.Float32, reflect.Float64, reflect.String, reflect.Bool: + enc.keyEqElement(key, rv) + case reflect.Array, reflect.Slice: + if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) { + enc.eArrayOfTables(key, rv) + } else { + enc.keyEqElement(key, rv) + } + case reflect.Interface: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Map: + if rv.IsNil() { + return + } + enc.eTable(key, rv) + case reflect.Ptr: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Struct: + enc.eTable(key, rv) + default: + panic(e("unsupported type for key '%s': %s", key, k)) + } +} + +// eElement encodes any value that can be an array element (primitives and +// arrays). +func (enc *Encoder) eElement(rv reflect.Value) { + switch v := rv.Interface().(type) { + case time.Time: + // Special case time.Time as a primitive. Has to come before + // TextMarshaler below because time.Time implements + // encoding.TextMarshaler, but we need to always use UTC. + enc.wf(v.UTC().Format("2006-01-02T15:04:05Z")) + return + case TextMarshaler: + // Special case. Use text marshaler if it's available for this value. + if s, err := v.MarshalText(); err != nil { + encPanic(err) + } else { + enc.writeQuoted(string(s)) + } + return + } + switch rv.Kind() { + case reflect.Bool: + enc.wf(strconv.FormatBool(rv.Bool())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64: + enc.wf(strconv.FormatInt(rv.Int(), 10)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, + reflect.Uint32, reflect.Uint64: + enc.wf(strconv.FormatUint(rv.Uint(), 10)) + case reflect.Float32: + enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32))) + case reflect.Float64: + enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64))) + case reflect.Array, reflect.Slice: + enc.eArrayOrSliceElement(rv) + case reflect.Interface: + enc.eElement(rv.Elem()) + case reflect.String: + enc.writeQuoted(rv.String()) + default: + panic(e("unexpected primitive type: %s", rv.Kind())) + } +} + +// By the TOML spec, all floats must have a decimal with at least one +// number on either side. +func floatAddDecimal(fstr string) string { + if !strings.Contains(fstr, ".") { + return fstr + ".0" + } + return fstr +} + +func (enc *Encoder) writeQuoted(s string) { + enc.wf("\"%s\"", quotedReplacer.Replace(s)) +} + +func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) { + length := rv.Len() + enc.wf("[") + for i := 0; i < length; i++ { + elem := rv.Index(i) + enc.eElement(elem) + if i != length-1 { + enc.wf(", ") + } + } + enc.wf("]") +} + +func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) { + if len(key) == 0 { + encPanic(errNoKey) + } + for i := 0; i < rv.Len(); i++ { + trv := rv.Index(i) + if isNil(trv) { + continue + } + panicIfInvalidKey(key) + enc.newline() + enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll()) + enc.newline() + enc.eMapOrStruct(key, trv) + } +} + +func (enc *Encoder) eTable(key Key, rv reflect.Value) { + panicIfInvalidKey(key) + if len(key) == 1 { + // Output an extra newline between top-level tables. + // (The newline isn't written if nothing else has been written though.) + enc.newline() + } + if len(key) > 0 { + enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll()) + enc.newline() + } + enc.eMapOrStruct(key, rv) +} + +func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) { + switch rv := eindirect(rv); rv.Kind() { + case reflect.Map: + enc.eMap(key, rv) + case reflect.Struct: + enc.eStruct(key, rv) + default: + panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String()) + } +} + +func (enc *Encoder) eMap(key Key, rv reflect.Value) { + rt := rv.Type() + if rt.Key().Kind() != reflect.String { + encPanic(errNonString) + } + + // Sort keys so that we have deterministic output. And write keys directly + // underneath this key first, before writing sub-structs or sub-maps. + var mapKeysDirect, mapKeysSub []string + for _, mapKey := range rv.MapKeys() { + k := mapKey.String() + if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) { + mapKeysSub = append(mapKeysSub, k) + } else { + mapKeysDirect = append(mapKeysDirect, k) + } + } + + var writeMapKeys = func(mapKeys []string) { + sort.Strings(mapKeys) + for _, mapKey := range mapKeys { + mrv := rv.MapIndex(reflect.ValueOf(mapKey)) + if isNil(mrv) { + // Don't write anything for nil fields. + continue + } + enc.encode(key.add(mapKey), mrv) + } + } + writeMapKeys(mapKeysDirect) + writeMapKeys(mapKeysSub) +} + +func (enc *Encoder) eStruct(key Key, rv reflect.Value) { + // Write keys for fields directly under this key first, because if we write + // a field that creates a new table, then all keys under it will be in that + // table (not the one we're writing here). + rt := rv.Type() + var fieldsDirect, fieldsSub [][]int + var addFields func(rt reflect.Type, rv reflect.Value, start []int) + addFields = func(rt reflect.Type, rv reflect.Value, start []int) { + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + // skip unexported fields + if f.PkgPath != "" && !f.Anonymous { + continue + } + frv := rv.Field(i) + if f.Anonymous { + t := f.Type + switch t.Kind() { + case reflect.Struct: + // Treat anonymous struct fields with + // tag names as though they are not + // anonymous, like encoding/json does. + if getOptions(f.Tag).name == "" { + addFields(t, frv, f.Index) + continue + } + case reflect.Ptr: + if t.Elem().Kind() == reflect.Struct && + getOptions(f.Tag).name == "" { + if !frv.IsNil() { + addFields(t.Elem(), frv.Elem(), f.Index) + } + continue + } + // Fall through to the normal field encoding logic below + // for non-struct anonymous fields. + } + } + + if typeIsHash(tomlTypeOfGo(frv)) { + fieldsSub = append(fieldsSub, append(start, f.Index...)) + } else { + fieldsDirect = append(fieldsDirect, append(start, f.Index...)) + } + } + } + addFields(rt, rv, nil) + + var writeFields = func(fields [][]int) { + for _, fieldIndex := range fields { + sft := rt.FieldByIndex(fieldIndex) + sf := rv.FieldByIndex(fieldIndex) + if isNil(sf) { + // Don't write anything for nil fields. + continue + } + + opts := getOptions(sft.Tag) + if opts.skip { + continue + } + keyName := sft.Name + if opts.name != "" { + keyName = opts.name + } + if opts.omitempty && isEmpty(sf) { + continue + } + if opts.omitzero && isZero(sf) { + continue + } + + enc.encode(key.add(keyName), sf) + } + } + writeFields(fieldsDirect) + writeFields(fieldsSub) +} + +// tomlTypeName returns the TOML type name of the Go value's type. It is +// used to determine whether the types of array elements are mixed (which is +// forbidden). If the Go value is nil, then it is illegal for it to be an array +// element, and valueIsNil is returned as true. + +// Returns the TOML type of a Go value. The type may be `nil`, which means +// no concrete TOML type could be found. +func tomlTypeOfGo(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() { + return nil + } + switch rv.Kind() { + case reflect.Bool: + return tomlBool + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64: + return tomlInteger + case reflect.Float32, reflect.Float64: + return tomlFloat + case reflect.Array, reflect.Slice: + if typeEqual(tomlHash, tomlArrayType(rv)) { + return tomlArrayHash + } + return tomlArray + case reflect.Ptr, reflect.Interface: + return tomlTypeOfGo(rv.Elem()) + case reflect.String: + return tomlString + case reflect.Map: + return tomlHash + case reflect.Struct: + switch rv.Interface().(type) { + case time.Time: + return tomlDatetime + case TextMarshaler: + return tomlString + default: + return tomlHash + } + default: + panic("unexpected reflect.Kind: " + rv.Kind().String()) + } +} + +// tomlArrayType returns the element type of a TOML array. The type returned +// may be nil if it cannot be determined (e.g., a nil slice or a zero length +// slize). This function may also panic if it finds a type that cannot be +// expressed in TOML (such as nil elements, heterogeneous arrays or directly +// nested arrays of tables). +func tomlArrayType(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() || rv.Len() == 0 { + return nil + } + firstType := tomlTypeOfGo(rv.Index(0)) + if firstType == nil { + encPanic(errArrayNilElement) + } + + rvlen := rv.Len() + for i := 1; i < rvlen; i++ { + elem := rv.Index(i) + switch elemType := tomlTypeOfGo(elem); { + case elemType == nil: + encPanic(errArrayNilElement) + case !typeEqual(firstType, elemType): + encPanic(errArrayMixedElementTypes) + } + } + // If we have a nested array, then we must make sure that the nested + // array contains ONLY primitives. + // This checks arbitrarily nested arrays. + if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) { + nest := tomlArrayType(eindirect(rv.Index(0))) + if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) { + encPanic(errArrayNoTable) + } + } + return firstType +} + +type tagOptions struct { + skip bool // "-" + name string + omitempty bool + omitzero bool +} + +func getOptions(tag reflect.StructTag) tagOptions { + t := tag.Get("toml") + if t == "-" { + return tagOptions{skip: true} + } + var opts tagOptions + parts := strings.Split(t, ",") + opts.name = parts[0] + for _, s := range parts[1:] { + switch s { + case "omitempty": + opts.omitempty = true + case "omitzero": + opts.omitzero = true + } + } + return opts +} + +func isZero(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return rv.Uint() == 0 + case reflect.Float32, reflect.Float64: + return rv.Float() == 0.0 + } + return false +} + +func isEmpty(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Array, reflect.Slice, reflect.Map, reflect.String: + return rv.Len() == 0 + case reflect.Bool: + return !rv.Bool() + } + return false +} + +func (enc *Encoder) newline() { + if enc.hasWritten { + enc.wf("\n") + } +} + +func (enc *Encoder) keyEqElement(key Key, val reflect.Value) { + if len(key) == 0 { + encPanic(errNoKey) + } + panicIfInvalidKey(key) + enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1)) + enc.eElement(val) + enc.newline() +} + +func (enc *Encoder) wf(format string, v ...interface{}) { + if _, err := fmt.Fprintf(enc.w, format, v...); err != nil { + encPanic(err) + } + enc.hasWritten = true +} + +func (enc *Encoder) indentStr(key Key) string { + return strings.Repeat(enc.Indent, len(key)-1) +} + +func encPanic(err error) { + panic(tomlEncodeError{err}) +} + +func eindirect(v reflect.Value) reflect.Value { + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + return eindirect(v.Elem()) + default: + return v + } +} + +func isNil(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return rv.IsNil() + default: + return false + } +} + +func panicIfInvalidKey(key Key) { + for _, k := range key { + if len(k) == 0 { + encPanic(e("Key '%s' is not a valid table name. Key names "+ + "cannot be empty.", key.maybeQuotedAll())) + } + } +} + +func isValidKeyName(s string) bool { + return len(s) != 0 +} diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/encoding_types.go b/src/runtime/vendor/github.com/BurntSushi/toml/encoding_types.go new file mode 100644 index 000000000..d36e1dd60 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/encoding_types.go @@ -0,0 +1,19 @@ +// +build go1.2 + +package toml + +// In order to support Go 1.1, we define our own TextMarshaler and +// TextUnmarshaler types. For Go 1.2+, we just alias them with the +// standard library interfaces. + +import ( + "encoding" +) + +// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextMarshaler encoding.TextMarshaler + +// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined +// here so that Go 1.1 can be supported. +type TextUnmarshaler encoding.TextUnmarshaler diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go b/src/runtime/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go new file mode 100644 index 000000000..e8d503d04 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go @@ -0,0 +1,18 @@ +// +build !go1.2 + +package toml + +// These interfaces were introduced in Go 1.2, so we add them manually when +// compiling for Go 1.1. + +// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextMarshaler interface { + MarshalText() (text []byte, err error) +} + +// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined +// here so that Go 1.1 can be supported. +type TextUnmarshaler interface { + UnmarshalText(text []byte) error +} diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/lex.go b/src/runtime/vendor/github.com/BurntSushi/toml/lex.go new file mode 100644 index 000000000..e0a742a88 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/lex.go @@ -0,0 +1,953 @@ +package toml + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +type itemType int + +const ( + itemError itemType = iota + itemNIL // used in the parser to indicate no type + itemEOF + itemText + itemString + itemRawString + itemMultilineString + itemRawMultilineString + itemBool + itemInteger + itemFloat + itemDatetime + itemArray // the start of an array + itemArrayEnd + itemTableStart + itemTableEnd + itemArrayTableStart + itemArrayTableEnd + itemKeyStart + itemCommentStart + itemInlineTableStart + itemInlineTableEnd +) + +const ( + eof = 0 + comma = ',' + tableStart = '[' + tableEnd = ']' + arrayTableStart = '[' + arrayTableEnd = ']' + tableSep = '.' + keySep = '=' + arrayStart = '[' + arrayEnd = ']' + commentStart = '#' + stringStart = '"' + stringEnd = '"' + rawStringStart = '\'' + rawStringEnd = '\'' + inlineTableStart = '{' + inlineTableEnd = '}' +) + +type stateFn func(lx *lexer) stateFn + +type lexer struct { + input string + start int + pos int + line int + state stateFn + items chan item + + // Allow for backing up up to three runes. + // This is necessary because TOML contains 3-rune tokens (""" and '''). + prevWidths [3]int + nprev int // how many of prevWidths are in use + // If we emit an eof, we can still back up, but it is not OK to call + // next again. + atEOF bool + + // A stack of state functions used to maintain context. + // The idea is to reuse parts of the state machine in various places. + // For example, values can appear at the top level or within arbitrarily + // nested arrays. The last state on the stack is used after a value has + // been lexed. Similarly for comments. + stack []stateFn +} + +type item struct { + typ itemType + val string + line int +} + +func (lx *lexer) nextItem() item { + for { + select { + case item := <-lx.items: + return item + default: + lx.state = lx.state(lx) + } + } +} + +func lex(input string) *lexer { + lx := &lexer{ + input: input, + state: lexTop, + line: 1, + items: make(chan item, 10), + stack: make([]stateFn, 0, 10), + } + return lx +} + +func (lx *lexer) push(state stateFn) { + lx.stack = append(lx.stack, state) +} + +func (lx *lexer) pop() stateFn { + if len(lx.stack) == 0 { + return lx.errorf("BUG in lexer: no states to pop") + } + last := lx.stack[len(lx.stack)-1] + lx.stack = lx.stack[0 : len(lx.stack)-1] + return last +} + +func (lx *lexer) current() string { + return lx.input[lx.start:lx.pos] +} + +func (lx *lexer) emit(typ itemType) { + lx.items <- item{typ, lx.current(), lx.line} + lx.start = lx.pos +} + +func (lx *lexer) emitTrim(typ itemType) { + lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line} + lx.start = lx.pos +} + +func (lx *lexer) next() (r rune) { + if lx.atEOF { + panic("next called after EOF") + } + if lx.pos >= len(lx.input) { + lx.atEOF = true + return eof + } + + if lx.input[lx.pos] == '\n' { + lx.line++ + } + lx.prevWidths[2] = lx.prevWidths[1] + lx.prevWidths[1] = lx.prevWidths[0] + if lx.nprev < 3 { + lx.nprev++ + } + r, w := utf8.DecodeRuneInString(lx.input[lx.pos:]) + lx.prevWidths[0] = w + lx.pos += w + return r +} + +// ignore skips over the pending input before this point. +func (lx *lexer) ignore() { + lx.start = lx.pos +} + +// backup steps back one rune. Can be called only twice between calls to next. +func (lx *lexer) backup() { + if lx.atEOF { + lx.atEOF = false + return + } + if lx.nprev < 1 { + panic("backed up too far") + } + w := lx.prevWidths[0] + lx.prevWidths[0] = lx.prevWidths[1] + lx.prevWidths[1] = lx.prevWidths[2] + lx.nprev-- + lx.pos -= w + if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { + lx.line-- + } +} + +// accept consumes the next rune if it's equal to `valid`. +func (lx *lexer) accept(valid rune) bool { + if lx.next() == valid { + return true + } + lx.backup() + return false +} + +// peek returns but does not consume the next rune in the input. +func (lx *lexer) peek() rune { + r := lx.next() + lx.backup() + return r +} + +// skip ignores all input that matches the given predicate. +func (lx *lexer) skip(pred func(rune) bool) { + for { + r := lx.next() + if pred(r) { + continue + } + lx.backup() + lx.ignore() + return + } +} + +// errorf stops all lexing by emitting an error and returning `nil`. +// Note that any value that is a character is escaped if it's a special +// character (newlines, tabs, etc.). +func (lx *lexer) errorf(format string, values ...interface{}) stateFn { + lx.items <- item{ + itemError, + fmt.Sprintf(format, values...), + lx.line, + } + return nil +} + +// lexTop consumes elements at the top level of TOML data. +func lexTop(lx *lexer) stateFn { + r := lx.next() + if isWhitespace(r) || isNL(r) { + return lexSkip(lx, lexTop) + } + switch r { + case commentStart: + lx.push(lexTop) + return lexCommentStart + case tableStart: + return lexTableStart + case eof: + if lx.pos > lx.start { + return lx.errorf("unexpected EOF") + } + lx.emit(itemEOF) + return nil + } + + // At this point, the only valid item can be a key, so we back up + // and let the key lexer do the rest. + lx.backup() + lx.push(lexTopEnd) + return lexKeyStart +} + +// lexTopEnd is entered whenever a top-level item has been consumed. (A value +// or a table.) It must see only whitespace, and will turn back to lexTop +// upon a newline. If it sees EOF, it will quit the lexer successfully. +func lexTopEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case r == commentStart: + // a comment will read to a newline for us. + lx.push(lexTop) + return lexCommentStart + case isWhitespace(r): + return lexTopEnd + case isNL(r): + lx.ignore() + return lexTop + case r == eof: + lx.emit(itemEOF) + return nil + } + return lx.errorf("expected a top-level item to end with a newline, "+ + "comment, or EOF, but got %q instead", r) +} + +// lexTable lexes the beginning of a table. Namely, it makes sure that +// it starts with a character other than '.' and ']'. +// It assumes that '[' has already been consumed. +// It also handles the case that this is an item in an array of tables. +// e.g., '[[name]]'. +func lexTableStart(lx *lexer) stateFn { + if lx.peek() == arrayTableStart { + lx.next() + lx.emit(itemArrayTableStart) + lx.push(lexArrayTableEnd) + } else { + lx.emit(itemTableStart) + lx.push(lexTableEnd) + } + return lexTableNameStart +} + +func lexTableEnd(lx *lexer) stateFn { + lx.emit(itemTableEnd) + return lexTopEnd +} + +func lexArrayTableEnd(lx *lexer) stateFn { + if r := lx.next(); r != arrayTableEnd { + return lx.errorf("expected end of table array name delimiter %q, "+ + "but got %q instead", arrayTableEnd, r) + } + lx.emit(itemArrayTableEnd) + return lexTopEnd +} + +func lexTableNameStart(lx *lexer) stateFn { + lx.skip(isWhitespace) + switch r := lx.peek(); { + case r == tableEnd || r == eof: + return lx.errorf("unexpected end of table name " + + "(table names cannot be empty)") + case r == tableSep: + return lx.errorf("unexpected table separator " + + "(table names cannot be empty)") + case r == stringStart || r == rawStringStart: + lx.ignore() + lx.push(lexTableNameEnd) + return lexValue // reuse string lexing + default: + return lexBareTableName + } +} + +// lexBareTableName lexes the name of a table. It assumes that at least one +// valid character for the table has already been read. +func lexBareTableName(lx *lexer) stateFn { + r := lx.next() + if isBareKeyChar(r) { + return lexBareTableName + } + lx.backup() + lx.emit(itemText) + return lexTableNameEnd +} + +// lexTableNameEnd reads the end of a piece of a table name, optionally +// consuming whitespace. +func lexTableNameEnd(lx *lexer) stateFn { + lx.skip(isWhitespace) + switch r := lx.next(); { + case isWhitespace(r): + return lexTableNameEnd + case r == tableSep: + lx.ignore() + return lexTableNameStart + case r == tableEnd: + return lx.pop() + default: + return lx.errorf("expected '.' or ']' to end table name, "+ + "but got %q instead", r) + } +} + +// lexKeyStart consumes a key name up until the first non-whitespace character. +// lexKeyStart will ignore whitespace. +func lexKeyStart(lx *lexer) stateFn { + r := lx.peek() + switch { + case r == keySep: + return lx.errorf("unexpected key separator %q", keySep) + case isWhitespace(r) || isNL(r): + lx.next() + return lexSkip(lx, lexKeyStart) + case r == stringStart || r == rawStringStart: + lx.ignore() + lx.emit(itemKeyStart) + lx.push(lexKeyEnd) + return lexValue // reuse string lexing + default: + lx.ignore() + lx.emit(itemKeyStart) + return lexBareKey + } +} + +// lexBareKey consumes the text of a bare key. Assumes that the first character +// (which is not whitespace) has not yet been consumed. +func lexBareKey(lx *lexer) stateFn { + switch r := lx.next(); { + case isBareKeyChar(r): + return lexBareKey + case isWhitespace(r): + lx.backup() + lx.emit(itemText) + return lexKeyEnd + case r == keySep: + lx.backup() + lx.emit(itemText) + return lexKeyEnd + default: + return lx.errorf("bare keys cannot contain %q", r) + } +} + +// lexKeyEnd consumes the end of a key and trims whitespace (up to the key +// separator). +func lexKeyEnd(lx *lexer) stateFn { + switch r := lx.next(); { + case r == keySep: + return lexSkip(lx, lexValue) + case isWhitespace(r): + return lexSkip(lx, lexKeyEnd) + default: + return lx.errorf("expected key separator %q, but got %q instead", + keySep, r) + } +} + +// lexValue starts the consumption of a value anywhere a value is expected. +// lexValue will ignore whitespace. +// After a value is lexed, the last state on the next is popped and returned. +func lexValue(lx *lexer) stateFn { + // We allow whitespace to precede a value, but NOT newlines. + // In array syntax, the array states are responsible for ignoring newlines. + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexValue) + case isDigit(r): + lx.backup() // avoid an extra state and use the same as above + return lexNumberOrDateStart + } + switch r { + case arrayStart: + lx.ignore() + lx.emit(itemArray) + return lexArrayValue + case inlineTableStart: + lx.ignore() + lx.emit(itemInlineTableStart) + return lexInlineTableValue + case stringStart: + if lx.accept(stringStart) { + if lx.accept(stringStart) { + lx.ignore() // Ignore """ + return lexMultilineString + } + lx.backup() + } + lx.ignore() // ignore the '"' + return lexString + case rawStringStart: + if lx.accept(rawStringStart) { + if lx.accept(rawStringStart) { + lx.ignore() // Ignore """ + return lexMultilineRawString + } + lx.backup() + } + lx.ignore() // ignore the "'" + return lexRawString + case '+', '-': + return lexNumberStart + case '.': // special error case, be kind to users + return lx.errorf("floats must start with a digit, not '.'") + } + if unicode.IsLetter(r) { + // Be permissive here; lexBool will give a nice error if the + // user wrote something like + // x = foo + // (i.e. not 'true' or 'false' but is something else word-like.) + lx.backup() + return lexBool + } + return lx.errorf("expected value but found %q instead", r) +} + +// lexArrayValue consumes one value in an array. It assumes that '[' or ',' +// have already been consumed. All whitespace and newlines are ignored. +func lexArrayValue(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValue) + case r == commentStart: + lx.push(lexArrayValue) + return lexCommentStart + case r == comma: + return lx.errorf("unexpected comma") + case r == arrayEnd: + // NOTE(caleb): The spec isn't clear about whether you can have + // a trailing comma or not, so we'll allow it. + return lexArrayEnd + } + + lx.backup() + lx.push(lexArrayValueEnd) + return lexValue +} + +// lexArrayValueEnd consumes everything between the end of an array value and +// the next value (or the end of the array): it ignores whitespace and newlines +// and expects either a ',' or a ']'. +func lexArrayValueEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValueEnd) + case r == commentStart: + lx.push(lexArrayValueEnd) + return lexCommentStart + case r == comma: + lx.ignore() + return lexArrayValue // move on to the next value + case r == arrayEnd: + return lexArrayEnd + } + return lx.errorf( + "expected a comma or array terminator %q, but got %q instead", + arrayEnd, r, + ) +} + +// lexArrayEnd finishes the lexing of an array. +// It assumes that a ']' has just been consumed. +func lexArrayEnd(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemArrayEnd) + return lx.pop() +} + +// lexInlineTableValue consumes one key/value pair in an inline table. +// It assumes that '{' or ',' have already been consumed. Whitespace is ignored. +func lexInlineTableValue(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexInlineTableValue) + case isNL(r): + return lx.errorf("newlines not allowed within inline tables") + case r == commentStart: + lx.push(lexInlineTableValue) + return lexCommentStart + case r == comma: + return lx.errorf("unexpected comma") + case r == inlineTableEnd: + return lexInlineTableEnd + } + lx.backup() + lx.push(lexInlineTableValueEnd) + return lexKeyStart +} + +// lexInlineTableValueEnd consumes everything between the end of an inline table +// key/value pair and the next pair (or the end of the table): +// it ignores whitespace and expects either a ',' or a '}'. +func lexInlineTableValueEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexInlineTableValueEnd) + case isNL(r): + return lx.errorf("newlines not allowed within inline tables") + case r == commentStart: + lx.push(lexInlineTableValueEnd) + return lexCommentStart + case r == comma: + lx.ignore() + return lexInlineTableValue + case r == inlineTableEnd: + return lexInlineTableEnd + } + return lx.errorf("expected a comma or an inline table terminator %q, "+ + "but got %q instead", inlineTableEnd, r) +} + +// lexInlineTableEnd finishes the lexing of an inline table. +// It assumes that a '}' has just been consumed. +func lexInlineTableEnd(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemInlineTableEnd) + return lx.pop() +} + +// lexString consumes the inner contents of a string. It assumes that the +// beginning '"' has already been consumed and ignored. +func lexString(lx *lexer) stateFn { + r := lx.next() + switch { + case r == eof: + return lx.errorf("unexpected EOF") + case isNL(r): + return lx.errorf("strings cannot contain newlines") + case r == '\\': + lx.push(lexString) + return lexStringEscape + case r == stringEnd: + lx.backup() + lx.emit(itemString) + lx.next() + lx.ignore() + return lx.pop() + } + return lexString +} + +// lexMultilineString consumes the inner contents of a string. It assumes that +// the beginning '"""' has already been consumed and ignored. +func lexMultilineString(lx *lexer) stateFn { + switch lx.next() { + case eof: + return lx.errorf("unexpected EOF") + case '\\': + return lexMultilineStringEscape + case stringEnd: + if lx.accept(stringEnd) { + if lx.accept(stringEnd) { + lx.backup() + lx.backup() + lx.backup() + lx.emit(itemMultilineString) + lx.next() + lx.next() + lx.next() + lx.ignore() + return lx.pop() + } + lx.backup() + } + } + return lexMultilineString +} + +// lexRawString consumes a raw string. Nothing can be escaped in such a string. +// It assumes that the beginning "'" has already been consumed and ignored. +func lexRawString(lx *lexer) stateFn { + r := lx.next() + switch { + case r == eof: + return lx.errorf("unexpected EOF") + case isNL(r): + return lx.errorf("strings cannot contain newlines") + case r == rawStringEnd: + lx.backup() + lx.emit(itemRawString) + lx.next() + lx.ignore() + return lx.pop() + } + return lexRawString +} + +// lexMultilineRawString consumes a raw string. Nothing can be escaped in such +// a string. It assumes that the beginning "'''" has already been consumed and +// ignored. +func lexMultilineRawString(lx *lexer) stateFn { + switch lx.next() { + case eof: + return lx.errorf("unexpected EOF") + case rawStringEnd: + if lx.accept(rawStringEnd) { + if lx.accept(rawStringEnd) { + lx.backup() + lx.backup() + lx.backup() + lx.emit(itemRawMultilineString) + lx.next() + lx.next() + lx.next() + lx.ignore() + return lx.pop() + } + lx.backup() + } + } + return lexMultilineRawString +} + +// lexMultilineStringEscape consumes an escaped character. It assumes that the +// preceding '\\' has already been consumed. +func lexMultilineStringEscape(lx *lexer) stateFn { + // Handle the special case first: + if isNL(lx.next()) { + return lexMultilineString + } + lx.backup() + lx.push(lexMultilineString) + return lexStringEscape(lx) +} + +func lexStringEscape(lx *lexer) stateFn { + r := lx.next() + switch r { + case 'b': + fallthrough + case 't': + fallthrough + case 'n': + fallthrough + case 'f': + fallthrough + case 'r': + fallthrough + case '"': + fallthrough + case '\\': + return lx.pop() + case 'u': + return lexShortUnicodeEscape + case 'U': + return lexLongUnicodeEscape + } + return lx.errorf("invalid escape character %q; only the following "+ + "escape characters are allowed: "+ + `\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r) +} + +func lexShortUnicodeEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 4; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf(`expected four hexadecimal digits after '\u', `+ + "but got %q instead", lx.current()) + } + } + return lx.pop() +} + +func lexLongUnicodeEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 8; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf(`expected eight hexadecimal digits after '\U', `+ + "but got %q instead", lx.current()) + } + } + return lx.pop() +} + +// lexNumberOrDateStart consumes either an integer, a float, or datetime. +func lexNumberOrDateStart(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexNumberOrDate + } + switch r { + case '_': + return lexNumber + case 'e', 'E': + return lexFloat + case '.': + return lx.errorf("floats must start with a digit, not '.'") + } + return lx.errorf("expected a digit but got %q", r) +} + +// lexNumberOrDate consumes either an integer, float or datetime. +func lexNumberOrDate(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexNumberOrDate + } + switch r { + case '-': + return lexDatetime + case '_': + return lexNumber + case '.', 'e', 'E': + return lexFloat + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexDatetime consumes a Datetime, to a first approximation. +// The parser validates that it matches one of the accepted formats. +func lexDatetime(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexDatetime + } + switch r { + case '-', 'T', ':', '.', 'Z', '+': + return lexDatetime + } + + lx.backup() + lx.emit(itemDatetime) + return lx.pop() +} + +// lexNumberStart consumes either an integer or a float. It assumes that a sign +// has already been read, but that *no* digits have been consumed. +// lexNumberStart will move to the appropriate integer or float states. +func lexNumberStart(lx *lexer) stateFn { + // We MUST see a digit. Even floats have to start with a digit. + r := lx.next() + if !isDigit(r) { + if r == '.' { + return lx.errorf("floats must start with a digit, not '.'") + } + return lx.errorf("expected a digit but got %q", r) + } + return lexNumber +} + +// lexNumber consumes an integer or a float after seeing the first digit. +func lexNumber(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexNumber + } + switch r { + case '_': + return lexNumber + case '.', 'e', 'E': + return lexFloat + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexFloat consumes the elements of a float. It allows any sequence of +// float-like characters, so floats emitted by the lexer are only a first +// approximation and must be validated by the parser. +func lexFloat(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexFloat + } + switch r { + case '_', '.', '-', '+', 'e', 'E': + return lexFloat + } + + lx.backup() + lx.emit(itemFloat) + return lx.pop() +} + +// lexBool consumes a bool string: 'true' or 'false. +func lexBool(lx *lexer) stateFn { + var rs []rune + for { + r := lx.next() + if !unicode.IsLetter(r) { + lx.backup() + break + } + rs = append(rs, r) + } + s := string(rs) + switch s { + case "true", "false": + lx.emit(itemBool) + return lx.pop() + } + return lx.errorf("expected value but found %q instead", s) +} + +// lexCommentStart begins the lexing of a comment. It will emit +// itemCommentStart and consume no characters, passing control to lexComment. +func lexCommentStart(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemCommentStart) + return lexComment +} + +// lexComment lexes an entire comment. It assumes that '#' has been consumed. +// It will consume *up to* the first newline character, and pass control +// back to the last state on the stack. +func lexComment(lx *lexer) stateFn { + r := lx.peek() + if isNL(r) || r == eof { + lx.emit(itemText) + return lx.pop() + } + lx.next() + return lexComment +} + +// lexSkip ignores all slurped input and moves on to the next state. +func lexSkip(lx *lexer, nextState stateFn) stateFn { + return func(lx *lexer) stateFn { + lx.ignore() + return nextState + } +} + +// isWhitespace returns true if `r` is a whitespace character according +// to the spec. +func isWhitespace(r rune) bool { + return r == '\t' || r == ' ' +} + +func isNL(r rune) bool { + return r == '\n' || r == '\r' +} + +func isDigit(r rune) bool { + return r >= '0' && r <= '9' +} + +func isHexadecimal(r rune) bool { + return (r >= '0' && r <= '9') || + (r >= 'a' && r <= 'f') || + (r >= 'A' && r <= 'F') +} + +func isBareKeyChar(r rune) bool { + return (r >= 'A' && r <= 'Z') || + (r >= 'a' && r <= 'z') || + (r >= '0' && r <= '9') || + r == '_' || + r == '-' +} + +func (itype itemType) String() string { + switch itype { + case itemError: + return "Error" + case itemNIL: + return "NIL" + case itemEOF: + return "EOF" + case itemText: + return "Text" + case itemString, itemRawString, itemMultilineString, itemRawMultilineString: + return "String" + case itemBool: + return "Bool" + case itemInteger: + return "Integer" + case itemFloat: + return "Float" + case itemDatetime: + return "DateTime" + case itemTableStart: + return "TableStart" + case itemTableEnd: + return "TableEnd" + case itemKeyStart: + return "KeyStart" + case itemArray: + return "Array" + case itemArrayEnd: + return "ArrayEnd" + case itemCommentStart: + return "CommentStart" + } + panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype))) +} + +func (item item) String() string { + return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val) +} diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/parse.go b/src/runtime/vendor/github.com/BurntSushi/toml/parse.go new file mode 100644 index 000000000..50869ef92 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/parse.go @@ -0,0 +1,592 @@ +package toml + +import ( + "fmt" + "strconv" + "strings" + "time" + "unicode" + "unicode/utf8" +) + +type parser struct { + mapping map[string]interface{} + types map[string]tomlType + lx *lexer + + // A list of keys in the order that they appear in the TOML data. + ordered []Key + + // the full key for the current hash in scope + context Key + + // the base key name for everything except hashes + currentKey string + + // rough approximation of line number + approxLine int + + // A map of 'key.group.names' to whether they were created implicitly. + implicits map[string]bool +} + +type parseError string + +func (pe parseError) Error() string { + return string(pe) +} + +func parse(data string) (p *parser, err error) { + defer func() { + if r := recover(); r != nil { + var ok bool + if err, ok = r.(parseError); ok { + return + } + panic(r) + } + }() + + p = &parser{ + mapping: make(map[string]interface{}), + types: make(map[string]tomlType), + lx: lex(data), + ordered: make([]Key, 0), + implicits: make(map[string]bool), + } + for { + item := p.next() + if item.typ == itemEOF { + break + } + p.topLevel(item) + } + + return p, nil +} + +func (p *parser) panicf(format string, v ...interface{}) { + msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s", + p.approxLine, p.current(), fmt.Sprintf(format, v...)) + panic(parseError(msg)) +} + +func (p *parser) next() item { + it := p.lx.nextItem() + if it.typ == itemError { + p.panicf("%s", it.val) + } + return it +} + +func (p *parser) bug(format string, v ...interface{}) { + panic(fmt.Sprintf("BUG: "+format+"\n\n", v...)) +} + +func (p *parser) expect(typ itemType) item { + it := p.next() + p.assertEqual(typ, it.typ) + return it +} + +func (p *parser) assertEqual(expected, got itemType) { + if expected != got { + p.bug("Expected '%s' but got '%s'.", expected, got) + } +} + +func (p *parser) topLevel(item item) { + switch item.typ { + case itemCommentStart: + p.approxLine = item.line + p.expect(itemText) + case itemTableStart: + kg := p.next() + p.approxLine = kg.line + + var key Key + for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() { + key = append(key, p.keyString(kg)) + } + p.assertEqual(itemTableEnd, kg.typ) + + p.establishContext(key, false) + p.setType("", tomlHash) + p.ordered = append(p.ordered, key) + case itemArrayTableStart: + kg := p.next() + p.approxLine = kg.line + + var key Key + for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() { + key = append(key, p.keyString(kg)) + } + p.assertEqual(itemArrayTableEnd, kg.typ) + + p.establishContext(key, true) + p.setType("", tomlArrayHash) + p.ordered = append(p.ordered, key) + case itemKeyStart: + kname := p.next() + p.approxLine = kname.line + p.currentKey = p.keyString(kname) + + val, typ := p.value(p.next()) + p.setValue(p.currentKey, val) + p.setType(p.currentKey, typ) + p.ordered = append(p.ordered, p.context.add(p.currentKey)) + p.currentKey = "" + default: + p.bug("Unexpected type at top level: %s", item.typ) + } +} + +// Gets a string for a key (or part of a key in a table name). +func (p *parser) keyString(it item) string { + switch it.typ { + case itemText: + return it.val + case itemString, itemMultilineString, + itemRawString, itemRawMultilineString: + s, _ := p.value(it) + return s.(string) + default: + p.bug("Unexpected key type: %s", it.typ) + panic("unreachable") + } +} + +// value translates an expected value from the lexer into a Go value wrapped +// as an empty interface. +func (p *parser) value(it item) (interface{}, tomlType) { + switch it.typ { + case itemString: + return p.replaceEscapes(it.val), p.typeOfPrimitive(it) + case itemMultilineString: + trimmed := stripFirstNewline(stripEscapedWhitespace(it.val)) + return p.replaceEscapes(trimmed), p.typeOfPrimitive(it) + case itemRawString: + return it.val, p.typeOfPrimitive(it) + case itemRawMultilineString: + return stripFirstNewline(it.val), p.typeOfPrimitive(it) + case itemBool: + switch it.val { + case "true": + return true, p.typeOfPrimitive(it) + case "false": + return false, p.typeOfPrimitive(it) + } + p.bug("Expected boolean value, but got '%s'.", it.val) + case itemInteger: + if !numUnderscoresOK(it.val) { + p.panicf("Invalid integer %q: underscores must be surrounded by digits", + it.val) + } + val := strings.Replace(it.val, "_", "", -1) + num, err := strconv.ParseInt(val, 10, 64) + if err != nil { + // Distinguish integer values. Normally, it'd be a bug if the lexer + // provides an invalid integer, but it's possible that the number is + // out of range of valid values (which the lexer cannot determine). + // So mark the former as a bug but the latter as a legitimate user + // error. + if e, ok := err.(*strconv.NumError); ok && + e.Err == strconv.ErrRange { + + p.panicf("Integer '%s' is out of the range of 64-bit "+ + "signed integers.", it.val) + } else { + p.bug("Expected integer value, but got '%s'.", it.val) + } + } + return num, p.typeOfPrimitive(it) + case itemFloat: + parts := strings.FieldsFunc(it.val, func(r rune) bool { + switch r { + case '.', 'e', 'E': + return true + } + return false + }) + for _, part := range parts { + if !numUnderscoresOK(part) { + p.panicf("Invalid float %q: underscores must be "+ + "surrounded by digits", it.val) + } + } + if !numPeriodsOK(it.val) { + // As a special case, numbers like '123.' or '1.e2', + // which are valid as far as Go/strconv are concerned, + // must be rejected because TOML says that a fractional + // part consists of '.' followed by 1+ digits. + p.panicf("Invalid float %q: '.' must be followed "+ + "by one or more digits", it.val) + } + val := strings.Replace(it.val, "_", "", -1) + num, err := strconv.ParseFloat(val, 64) + if err != nil { + if e, ok := err.(*strconv.NumError); ok && + e.Err == strconv.ErrRange { + + p.panicf("Float '%s' is out of the range of 64-bit "+ + "IEEE-754 floating-point numbers.", it.val) + } else { + p.panicf("Invalid float value: %q", it.val) + } + } + return num, p.typeOfPrimitive(it) + case itemDatetime: + var t time.Time + var ok bool + var err error + for _, format := range []string{ + "2006-01-02T15:04:05Z07:00", + "2006-01-02T15:04:05", + "2006-01-02", + } { + t, err = time.ParseInLocation(format, it.val, time.Local) + if err == nil { + ok = true + break + } + } + if !ok { + p.panicf("Invalid TOML Datetime: %q.", it.val) + } + return t, p.typeOfPrimitive(it) + case itemArray: + array := make([]interface{}, 0) + types := make([]tomlType, 0) + + for it = p.next(); it.typ != itemArrayEnd; it = p.next() { + if it.typ == itemCommentStart { + p.expect(itemText) + continue + } + + val, typ := p.value(it) + array = append(array, val) + types = append(types, typ) + } + return array, p.typeOfArray(types) + case itemInlineTableStart: + var ( + hash = make(map[string]interface{}) + outerContext = p.context + outerKey = p.currentKey + ) + + p.context = append(p.context, p.currentKey) + p.currentKey = "" + for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() { + if it.typ != itemKeyStart { + p.bug("Expected key start but instead found %q, around line %d", + it.val, p.approxLine) + } + if it.typ == itemCommentStart { + p.expect(itemText) + continue + } + + // retrieve key + k := p.next() + p.approxLine = k.line + kname := p.keyString(k) + + // retrieve value + p.currentKey = kname + val, typ := p.value(p.next()) + // make sure we keep metadata up to date + p.setType(kname, typ) + p.ordered = append(p.ordered, p.context.add(p.currentKey)) + hash[kname] = val + } + p.context = outerContext + p.currentKey = outerKey + return hash, tomlHash + } + p.bug("Unexpected value type: %s", it.typ) + panic("unreachable") +} + +// numUnderscoresOK checks whether each underscore in s is surrounded by +// characters that are not underscores. +func numUnderscoresOK(s string) bool { + accept := false + for _, r := range s { + if r == '_' { + if !accept { + return false + } + accept = false + continue + } + accept = true + } + return accept +} + +// numPeriodsOK checks whether every period in s is followed by a digit. +func numPeriodsOK(s string) bool { + period := false + for _, r := range s { + if period && !isDigit(r) { + return false + } + period = r == '.' + } + return !period +} + +// establishContext sets the current context of the parser, +// where the context is either a hash or an array of hashes. Which one is +// set depends on the value of the `array` parameter. +// +// Establishing the context also makes sure that the key isn't a duplicate, and +// will create implicit hashes automatically. +func (p *parser) establishContext(key Key, array bool) { + var ok bool + + // Always start at the top level and drill down for our context. + hashContext := p.mapping + keyContext := make(Key, 0) + + // We only need implicit hashes for key[0:-1] + for _, k := range key[0 : len(key)-1] { + _, ok = hashContext[k] + keyContext = append(keyContext, k) + + // No key? Make an implicit hash and move on. + if !ok { + p.addImplicit(keyContext) + hashContext[k] = make(map[string]interface{}) + } + + // If the hash context is actually an array of tables, then set + // the hash context to the last element in that array. + // + // Otherwise, it better be a table, since this MUST be a key group (by + // virtue of it not being the last element in a key). + switch t := hashContext[k].(type) { + case []map[string]interface{}: + hashContext = t[len(t)-1] + case map[string]interface{}: + hashContext = t + default: + p.panicf("Key '%s' was already created as a hash.", keyContext) + } + } + + p.context = keyContext + if array { + // If this is the first element for this array, then allocate a new + // list of tables for it. + k := key[len(key)-1] + if _, ok := hashContext[k]; !ok { + hashContext[k] = make([]map[string]interface{}, 0, 5) + } + + // Add a new table. But make sure the key hasn't already been used + // for something else. + if hash, ok := hashContext[k].([]map[string]interface{}); ok { + hashContext[k] = append(hash, make(map[string]interface{})) + } else { + p.panicf("Key '%s' was already created and cannot be used as "+ + "an array.", keyContext) + } + } else { + p.setValue(key[len(key)-1], make(map[string]interface{})) + } + p.context = append(p.context, key[len(key)-1]) +} + +// setValue sets the given key to the given value in the current context. +// It will make sure that the key hasn't already been defined, account for +// implicit key groups. +func (p *parser) setValue(key string, value interface{}) { + var tmpHash interface{} + var ok bool + + hash := p.mapping + keyContext := make(Key, 0) + for _, k := range p.context { + keyContext = append(keyContext, k) + if tmpHash, ok = hash[k]; !ok { + p.bug("Context for key '%s' has not been established.", keyContext) + } + switch t := tmpHash.(type) { + case []map[string]interface{}: + // The context is a table of hashes. Pick the most recent table + // defined as the current hash. + hash = t[len(t)-1] + case map[string]interface{}: + hash = t + default: + p.bug("Expected hash to have type 'map[string]interface{}', but "+ + "it has '%T' instead.", tmpHash) + } + } + keyContext = append(keyContext, key) + + if _, ok := hash[key]; ok { + // Typically, if the given key has already been set, then we have + // to raise an error since duplicate keys are disallowed. However, + // it's possible that a key was previously defined implicitly. In this + // case, it is allowed to be redefined concretely. (See the + // `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.) + // + // But we have to make sure to stop marking it as an implicit. (So that + // another redefinition provokes an error.) + // + // Note that since it has already been defined (as a hash), we don't + // want to overwrite it. So our business is done. + if p.isImplicit(keyContext) { + p.removeImplicit(keyContext) + return + } + + // Otherwise, we have a concrete key trying to override a previous + // key, which is *always* wrong. + p.panicf("Key '%s' has already been defined.", keyContext) + } + hash[key] = value +} + +// setType sets the type of a particular value at a given key. +// It should be called immediately AFTER setValue. +// +// Note that if `key` is empty, then the type given will be applied to the +// current context (which is either a table or an array of tables). +func (p *parser) setType(key string, typ tomlType) { + keyContext := make(Key, 0, len(p.context)+1) + for _, k := range p.context { + keyContext = append(keyContext, k) + } + if len(key) > 0 { // allow type setting for hashes + keyContext = append(keyContext, key) + } + p.types[keyContext.String()] = typ +} + +// addImplicit sets the given Key as having been created implicitly. +func (p *parser) addImplicit(key Key) { + p.implicits[key.String()] = true +} + +// removeImplicit stops tagging the given key as having been implicitly +// created. +func (p *parser) removeImplicit(key Key) { + p.implicits[key.String()] = false +} + +// isImplicit returns true if the key group pointed to by the key was created +// implicitly. +func (p *parser) isImplicit(key Key) bool { + return p.implicits[key.String()] +} + +// current returns the full key name of the current context. +func (p *parser) current() string { + if len(p.currentKey) == 0 { + return p.context.String() + } + if len(p.context) == 0 { + return p.currentKey + } + return fmt.Sprintf("%s.%s", p.context, p.currentKey) +} + +func stripFirstNewline(s string) string { + if len(s) == 0 || s[0] != '\n' { + return s + } + return s[1:] +} + +func stripEscapedWhitespace(s string) string { + esc := strings.Split(s, "\\\n") + if len(esc) > 1 { + for i := 1; i < len(esc); i++ { + esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace) + } + } + return strings.Join(esc, "") +} + +func (p *parser) replaceEscapes(str string) string { + var replaced []rune + s := []byte(str) + r := 0 + for r < len(s) { + if s[r] != '\\' { + c, size := utf8.DecodeRune(s[r:]) + r += size + replaced = append(replaced, c) + continue + } + r += 1 + if r >= len(s) { + p.bug("Escape sequence at end of string.") + return "" + } + switch s[r] { + default: + p.bug("Expected valid escape code after \\, but got %q.", s[r]) + return "" + case 'b': + replaced = append(replaced, rune(0x0008)) + r += 1 + case 't': + replaced = append(replaced, rune(0x0009)) + r += 1 + case 'n': + replaced = append(replaced, rune(0x000A)) + r += 1 + case 'f': + replaced = append(replaced, rune(0x000C)) + r += 1 + case 'r': + replaced = append(replaced, rune(0x000D)) + r += 1 + case '"': + replaced = append(replaced, rune(0x0022)) + r += 1 + case '\\': + replaced = append(replaced, rune(0x005C)) + r += 1 + case 'u': + // At this point, we know we have a Unicode escape of the form + // `uXXXX` at [r, r+5). (Because the lexer guarantees this + // for us.) + escaped := p.asciiEscapeToUnicode(s[r+1 : r+5]) + replaced = append(replaced, escaped) + r += 5 + case 'U': + // At this point, we know we have a Unicode escape of the form + // `uXXXX` at [r, r+9). (Because the lexer guarantees this + // for us.) + escaped := p.asciiEscapeToUnicode(s[r+1 : r+9]) + replaced = append(replaced, escaped) + r += 9 + } + } + return string(replaced) +} + +func (p *parser) asciiEscapeToUnicode(bs []byte) rune { + s := string(bs) + hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32) + if err != nil { + p.bug("Could not parse '%s' as a hexadecimal number, but the "+ + "lexer claims it's OK: %s", s, err) + } + if !utf8.ValidRune(rune(hex)) { + p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s) + } + return rune(hex) +} + +func isStringType(ty itemType) bool { + return ty == itemString || ty == itemMultilineString || + ty == itemRawString || ty == itemRawMultilineString +} diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/session.vim b/src/runtime/vendor/github.com/BurntSushi/toml/session.vim new file mode 100644 index 000000000..562164be0 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/session.vim @@ -0,0 +1 @@ +au BufWritePost *.go silent!make tags > /dev/null 2>&1 diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/type_check.go b/src/runtime/vendor/github.com/BurntSushi/toml/type_check.go new file mode 100644 index 000000000..c73f8afc1 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/type_check.go @@ -0,0 +1,91 @@ +package toml + +// tomlType represents any Go type that corresponds to a TOML type. +// While the first draft of the TOML spec has a simplistic type system that +// probably doesn't need this level of sophistication, we seem to be militating +// toward adding real composite types. +type tomlType interface { + typeString() string +} + +// typeEqual accepts any two types and returns true if they are equal. +func typeEqual(t1, t2 tomlType) bool { + if t1 == nil || t2 == nil { + return false + } + return t1.typeString() == t2.typeString() +} + +func typeIsHash(t tomlType) bool { + return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) +} + +type tomlBaseType string + +func (btype tomlBaseType) typeString() string { + return string(btype) +} + +func (btype tomlBaseType) String() string { + return btype.typeString() +} + +var ( + tomlInteger tomlBaseType = "Integer" + tomlFloat tomlBaseType = "Float" + tomlDatetime tomlBaseType = "Datetime" + tomlString tomlBaseType = "String" + tomlBool tomlBaseType = "Bool" + tomlArray tomlBaseType = "Array" + tomlHash tomlBaseType = "Hash" + tomlArrayHash tomlBaseType = "ArrayHash" +) + +// typeOfPrimitive returns a tomlType of any primitive value in TOML. +// Primitive values are: Integer, Float, Datetime, String and Bool. +// +// Passing a lexer item other than the following will cause a BUG message +// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. +func (p *parser) typeOfPrimitive(lexItem item) tomlType { + switch lexItem.typ { + case itemInteger: + return tomlInteger + case itemFloat: + return tomlFloat + case itemDatetime: + return tomlDatetime + case itemString: + return tomlString + case itemMultilineString: + return tomlString + case itemRawString: + return tomlString + case itemRawMultilineString: + return tomlString + case itemBool: + return tomlBool + } + p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) + panic("unreachable") +} + +// typeOfArray returns a tomlType for an array given a list of types of its +// values. +// +// In the current spec, if an array is homogeneous, then its type is always +// "Array". If the array is not homogeneous, an error is generated. +func (p *parser) typeOfArray(types []tomlType) tomlType { + // Empty arrays are cool. + if len(types) == 0 { + return tomlArray + } + + theType := types[0] + for _, t := range types[1:] { + if !typeEqual(theType, t) { + p.panicf("Array contains values of type '%s' and '%s', but "+ + "arrays must be homogeneous.", theType, t) + } + } + return tomlArray +} diff --git a/src/runtime/vendor/github.com/BurntSushi/toml/type_fields.go b/src/runtime/vendor/github.com/BurntSushi/toml/type_fields.go new file mode 100644 index 000000000..608997c22 --- /dev/null +++ b/src/runtime/vendor/github.com/BurntSushi/toml/type_fields.go @@ -0,0 +1,242 @@ +package toml + +// Struct field handling is adapted from code in encoding/json: +// +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the Go distribution. + +import ( + "reflect" + "sort" + "sync" +) + +// A field represents a single field found in a struct. +type field struct { + name string // the name of the field (`toml` tag included) + tag bool // whether field has a `toml` tag + index []int // represents the depth of an anonymous field + typ reflect.Type // the type of the field +} + +// byName sorts field by name, breaking ties with depth, +// then breaking ties with "name came from toml tag", then +// breaking ties with index sequence. +type byName []field + +func (x byName) Len() int { return len(x) } + +func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byName) Less(i, j int) bool { + if x[i].name != x[j].name { + return x[i].name < x[j].name + } + if len(x[i].index) != len(x[j].index) { + return len(x[i].index) < len(x[j].index) + } + if x[i].tag != x[j].tag { + return x[i].tag + } + return byIndex(x).Less(i, j) +} + +// byIndex sorts field by index sequence. +type byIndex []field + +func (x byIndex) Len() int { return len(x) } + +func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byIndex) Less(i, j int) bool { + for k, xik := range x[i].index { + if k >= len(x[j].index) { + return false + } + if xik != x[j].index[k] { + return xik < x[j].index[k] + } + } + return len(x[i].index) < len(x[j].index) +} + +// typeFields returns a list of fields that TOML should recognize for the given +// type. The algorithm is breadth-first search over the set of structs to +// include - the top struct and then any reachable anonymous structs. +func typeFields(t reflect.Type) []field { + // Anonymous fields to explore at the current level and the next. + current := []field{} + next := []field{{typ: t}} + + // Count of queued names for current level and the next. + count := map[reflect.Type]int{} + nextCount := map[reflect.Type]int{} + + // Types already visited at an earlier level. + visited := map[reflect.Type]bool{} + + // Fields found. + var fields []field + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, map[reflect.Type]int{} + + for _, f := range current { + if visited[f.typ] { + continue + } + visited[f.typ] = true + + // Scan f.typ for fields to include. + for i := 0; i < f.typ.NumField(); i++ { + sf := f.typ.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { // unexported + continue + } + opts := getOptions(sf.Tag) + if opts.skip { + continue + } + index := make([]int, len(f.index)+1) + copy(index, f.index) + index[len(f.index)] = i + + ft := sf.Type + if ft.Name() == "" && ft.Kind() == reflect.Ptr { + // Follow pointer. + ft = ft.Elem() + } + + // Record found field and index sequence. + if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { + tagged := opts.name != "" + name := opts.name + if name == "" { + name = sf.Name + } + fields = append(fields, field{name, tagged, index, ft}) + if count[f.typ] > 1 { + // If there were multiple instances, add a second, + // so that the annihilation code will see a duplicate. + // It only cares about the distinction between 1 or 2, + // so don't bother generating any more copies. + fields = append(fields, fields[len(fields)-1]) + } + continue + } + + // Record new anonymous struct to explore in next round. + nextCount[ft]++ + if nextCount[ft] == 1 { + f := field{name: ft.Name(), index: index, typ: ft} + next = append(next, f) + } + } + } + } + + sort.Sort(byName(fields)) + + // Delete all fields that are hidden by the Go rules for embedded fields, + // except that fields with TOML tags are promoted. + + // The fields are sorted in primary order of name, secondary order + // of field index length. Loop over names; for each name, delete + // hidden fields by choosing the one dominant field that survives. + out := fields[:0] + for advance, i := 0, 0; i < len(fields); i += advance { + // One iteration per name. + // Find the sequence of fields with the name of this first field. + fi := fields[i] + name := fi.name + for advance = 1; i+advance < len(fields); advance++ { + fj := fields[i+advance] + if fj.name != name { + break + } + } + if advance == 1 { // Only one field with this name + out = append(out, fi) + continue + } + dominant, ok := dominantField(fields[i : i+advance]) + if ok { + out = append(out, dominant) + } + } + + fields = out + sort.Sort(byIndex(fields)) + + return fields +} + +// dominantField looks through the fields, all of which are known to +// have the same name, to find the single field that dominates the +// others using Go's embedding rules, modified by the presence of +// TOML tags. If there are multiple top-level fields, the boolean +// will be false: This condition is an error in Go and we skip all +// the fields. +func dominantField(fields []field) (field, bool) { + // The fields are sorted in increasing index-length order. The winner + // must therefore be one with the shortest index length. Drop all + // longer entries, which is easy: just truncate the slice. + length := len(fields[0].index) + tagged := -1 // Index of first tagged field. + for i, f := range fields { + if len(f.index) > length { + fields = fields[:i] + break + } + if f.tag { + if tagged >= 0 { + // Multiple tagged fields at the same level: conflict. + // Return no field. + return field{}, false + } + tagged = i + } + } + if tagged >= 0 { + return fields[tagged], true + } + // All remaining fields have the same length. If there's more than one, + // we have a conflict (two fields named "X" at the same level) and we + // return no field. + if len(fields) > 1 { + return field{}, false + } + return fields[0], true +} + +var fieldCache struct { + sync.RWMutex + m map[reflect.Type][]field +} + +// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. +func cachedTypeFields(t reflect.Type) []field { + fieldCache.RLock() + f := fieldCache.m[t] + fieldCache.RUnlock() + if f != nil { + return f + } + + // Compute fields without lock. + // Might duplicate effort but won't hold other computations back. + f = typeFields(t) + if f == nil { + f = []field{} + } + + fieldCache.Lock() + if fieldCache.m == nil { + fieldCache.m = map[reflect.Type][]field{} + } + fieldCache.m[t] = f + fieldCache.Unlock() + return f +} diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/.gitignore b/src/runtime/vendor/github.com/Microsoft/go-winio/.gitignore new file mode 100644 index 000000000..b883f1fdc --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/.gitignore @@ -0,0 +1 @@ +*.exe diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/LICENSE b/src/runtime/vendor/github.com/Microsoft/go-winio/LICENSE new file mode 100644 index 000000000..b8b569d77 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/README.md b/src/runtime/vendor/github.com/Microsoft/go-winio/README.md new file mode 100644 index 000000000..568001057 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/README.md @@ -0,0 +1,22 @@ +# go-winio + +This repository contains utilities for efficiently performing Win32 IO operations in +Go. Currently, this is focused on accessing named pipes and other file handles, and +for using named pipes as a net transport. + +This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go +to reuse the thread to schedule another goroutine. This limits support to Windows Vista and +newer operating systems. This is similar to the implementation of network sockets in Go's net +package. + +Please see the LICENSE file for licensing information. + +This project has adopted the [Microsoft Open Source Code of +Conduct](https://opensource.microsoft.com/codeofconduct/). For more information +see the [Code of Conduct +FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact +[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional +questions or comments. + +Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe +for another named pipe implementation. diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/backup.go b/src/runtime/vendor/github.com/Microsoft/go-winio/backup.go new file mode 100644 index 000000000..2be34af43 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/backup.go @@ -0,0 +1,280 @@ +// +build windows + +package winio + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "runtime" + "syscall" + "unicode/utf16" +) + +//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead +//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite + +const ( + BackupData = uint32(iota + 1) + BackupEaData + BackupSecurity + BackupAlternateData + BackupLink + BackupPropertyData + BackupObjectId + BackupReparseData + BackupSparseBlock + BackupTxfsData +) + +const ( + StreamSparseAttributes = uint32(8) +) + +const ( + WRITE_DAC = 0x40000 + WRITE_OWNER = 0x80000 + ACCESS_SYSTEM_SECURITY = 0x1000000 +) + +// BackupHeader represents a backup stream of a file. +type BackupHeader struct { + Id uint32 // The backup stream ID + Attributes uint32 // Stream attributes + Size int64 // The size of the stream in bytes + Name string // The name of the stream (for BackupAlternateData only). + Offset int64 // The offset of the stream in the file (for BackupSparseBlock only). +} + +type win32StreamId struct { + StreamId uint32 + Attributes uint32 + Size uint64 + NameSize uint32 +} + +// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series +// of BackupHeader values. +type BackupStreamReader struct { + r io.Reader + bytesLeft int64 +} + +// NewBackupStreamReader produces a BackupStreamReader from any io.Reader. +func NewBackupStreamReader(r io.Reader) *BackupStreamReader { + return &BackupStreamReader{r, 0} +} + +// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if +// it was not completely read. +func (r *BackupStreamReader) Next() (*BackupHeader, error) { + if r.bytesLeft > 0 { + if s, ok := r.r.(io.Seeker); ok { + // Make sure Seek on io.SeekCurrent sometimes succeeds + // before trying the actual seek. + if _, err := s.Seek(0, io.SeekCurrent); err == nil { + if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil { + return nil, err + } + r.bytesLeft = 0 + } + } + if _, err := io.Copy(ioutil.Discard, r); err != nil { + return nil, err + } + } + var wsi win32StreamId + if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil { + return nil, err + } + hdr := &BackupHeader{ + Id: wsi.StreamId, + Attributes: wsi.Attributes, + Size: int64(wsi.Size), + } + if wsi.NameSize != 0 { + name := make([]uint16, int(wsi.NameSize/2)) + if err := binary.Read(r.r, binary.LittleEndian, name); err != nil { + return nil, err + } + hdr.Name = syscall.UTF16ToString(name) + } + if wsi.StreamId == BackupSparseBlock { + if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil { + return nil, err + } + hdr.Size -= 8 + } + r.bytesLeft = hdr.Size + return hdr, nil +} + +// Read reads from the current backup stream. +func (r *BackupStreamReader) Read(b []byte) (int, error) { + if r.bytesLeft == 0 { + return 0, io.EOF + } + if int64(len(b)) > r.bytesLeft { + b = b[:r.bytesLeft] + } + n, err := r.r.Read(b) + r.bytesLeft -= int64(n) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } else if r.bytesLeft == 0 && err == nil { + err = io.EOF + } + return n, err +} + +// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API. +type BackupStreamWriter struct { + w io.Writer + bytesLeft int64 +} + +// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer. +func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter { + return &BackupStreamWriter{w, 0} +} + +// WriteHeader writes the next backup stream header and prepares for calls to Write(). +func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error { + if w.bytesLeft != 0 { + return fmt.Errorf("missing %d bytes", w.bytesLeft) + } + name := utf16.Encode([]rune(hdr.Name)) + wsi := win32StreamId{ + StreamId: hdr.Id, + Attributes: hdr.Attributes, + Size: uint64(hdr.Size), + NameSize: uint32(len(name) * 2), + } + if hdr.Id == BackupSparseBlock { + // Include space for the int64 block offset + wsi.Size += 8 + } + if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil { + return err + } + if len(name) != 0 { + if err := binary.Write(w.w, binary.LittleEndian, name); err != nil { + return err + } + } + if hdr.Id == BackupSparseBlock { + if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil { + return err + } + } + w.bytesLeft = hdr.Size + return nil +} + +// Write writes to the current backup stream. +func (w *BackupStreamWriter) Write(b []byte) (int, error) { + if w.bytesLeft < int64(len(b)) { + return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft) + } + n, err := w.w.Write(b) + w.bytesLeft -= int64(n) + return n, err +} + +// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API. +type BackupFileReader struct { + f *os.File + includeSecurity bool + ctx uintptr +} + +// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true, +// Read will attempt to read the security descriptor of the file. +func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader { + r := &BackupFileReader{f, includeSecurity, 0} + return r +} + +// Read reads a backup stream from the file by calling the Win32 API BackupRead(). +func (r *BackupFileReader) Read(b []byte) (int, error) { + var bytesRead uint32 + err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx) + if err != nil { + return 0, &os.PathError{"BackupRead", r.f.Name(), err} + } + runtime.KeepAlive(r.f) + if bytesRead == 0 { + return 0, io.EOF + } + return int(bytesRead), nil +} + +// Close frees Win32 resources associated with the BackupFileReader. It does not close +// the underlying file. +func (r *BackupFileReader) Close() error { + if r.ctx != 0 { + backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) + runtime.KeepAlive(r.f) + r.ctx = 0 + } + return nil +} + +// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API. +type BackupFileWriter struct { + f *os.File + includeSecurity bool + ctx uintptr +} + +// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true, +// Write() will attempt to restore the security descriptor from the stream. +func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter { + w := &BackupFileWriter{f, includeSecurity, 0} + return w +} + +// Write restores a portion of the file using the provided backup stream. +func (w *BackupFileWriter) Write(b []byte) (int, error) { + var bytesWritten uint32 + err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx) + if err != nil { + return 0, &os.PathError{"BackupWrite", w.f.Name(), err} + } + runtime.KeepAlive(w.f) + if int(bytesWritten) != len(b) { + return int(bytesWritten), errors.New("not all bytes could be written") + } + return len(b), nil +} + +// Close frees Win32 resources associated with the BackupFileWriter. It does not +// close the underlying file. +func (w *BackupFileWriter) Close() error { + if w.ctx != 0 { + backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) + runtime.KeepAlive(w.f) + w.ctx = 0 + } + return nil +} + +// OpenForBackup opens a file or directory, potentially skipping access checks if the backup +// or restore privileges have been acquired. +// +// If the file opened was a directory, it cannot be used with Readdir(). +func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) { + winPath, err := syscall.UTF16FromString(path) + if err != nil { + return nil, err + } + h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0) + if err != nil { + err = &os.PathError{Op: "open", Path: path, Err: err} + return nil, err + } + return os.NewFile(uintptr(h), path), nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/ea.go b/src/runtime/vendor/github.com/Microsoft/go-winio/ea.go new file mode 100644 index 000000000..4051c1b33 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/ea.go @@ -0,0 +1,137 @@ +package winio + +import ( + "bytes" + "encoding/binary" + "errors" +) + +type fileFullEaInformation struct { + NextEntryOffset uint32 + Flags uint8 + NameLength uint8 + ValueLength uint16 +} + +var ( + fileFullEaInformationSize = binary.Size(&fileFullEaInformation{}) + + errInvalidEaBuffer = errors.New("invalid extended attribute buffer") + errEaNameTooLarge = errors.New("extended attribute name too large") + errEaValueTooLarge = errors.New("extended attribute value too large") +) + +// ExtendedAttribute represents a single Windows EA. +type ExtendedAttribute struct { + Name string + Value []byte + Flags uint8 +} + +func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { + var info fileFullEaInformation + err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info) + if err != nil { + err = errInvalidEaBuffer + return + } + + nameOffset := fileFullEaInformationSize + nameLen := int(info.NameLength) + valueOffset := nameOffset + int(info.NameLength) + 1 + valueLen := int(info.ValueLength) + nextOffset := int(info.NextEntryOffset) + if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) { + err = errInvalidEaBuffer + return + } + + ea.Name = string(b[nameOffset : nameOffset+nameLen]) + ea.Value = b[valueOffset : valueOffset+valueLen] + ea.Flags = info.Flags + if info.NextEntryOffset != 0 { + nb = b[info.NextEntryOffset:] + } + return +} + +// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION +// buffer retrieved from BackupRead, ZwQueryEaFile, etc. +func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) { + for len(b) != 0 { + ea, nb, err := parseEa(b) + if err != nil { + return nil, err + } + + eas = append(eas, ea) + b = nb + } + return +} + +func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error { + if int(uint8(len(ea.Name))) != len(ea.Name) { + return errEaNameTooLarge + } + if int(uint16(len(ea.Value))) != len(ea.Value) { + return errEaValueTooLarge + } + entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value)) + withPadding := (entrySize + 3) &^ 3 + nextOffset := uint32(0) + if !last { + nextOffset = withPadding + } + info := fileFullEaInformation{ + NextEntryOffset: nextOffset, + Flags: ea.Flags, + NameLength: uint8(len(ea.Name)), + ValueLength: uint16(len(ea.Value)), + } + + err := binary.Write(buf, binary.LittleEndian, &info) + if err != nil { + return err + } + + _, err = buf.Write([]byte(ea.Name)) + if err != nil { + return err + } + + err = buf.WriteByte(0) + if err != nil { + return err + } + + _, err = buf.Write(ea.Value) + if err != nil { + return err + } + + _, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize]) + if err != nil { + return err + } + + return nil +} + +// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION +// buffer for use with BackupWrite, ZwSetEaFile, etc. +func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) { + var buf bytes.Buffer + for i := range eas { + last := false + if i == len(eas)-1 { + last = true + } + + err := writeEa(&buf, &eas[i], last) + if err != nil { + return nil, err + } + } + return buf.Bytes(), nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/file.go b/src/runtime/vendor/github.com/Microsoft/go-winio/file.go new file mode 100644 index 000000000..4334ff1cb --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/file.go @@ -0,0 +1,307 @@ +// +build windows + +package winio + +import ( + "errors" + "io" + "runtime" + "sync" + "sync/atomic" + "syscall" + "time" +) + +//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx +//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort +//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus +//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes + +type atomicBool int32 + +func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } +func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } +func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } +func (b *atomicBool) swap(new bool) bool { + var newInt int32 + if new { + newInt = 1 + } + return atomic.SwapInt32((*int32)(b), newInt) == 1 +} + +const ( + cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 + cFILE_SKIP_SET_EVENT_ON_HANDLE = 2 +) + +var ( + ErrFileClosed = errors.New("file has already been closed") + ErrTimeout = &timeoutError{} +) + +type timeoutError struct{} + +func (e *timeoutError) Error() string { return "i/o timeout" } +func (e *timeoutError) Timeout() bool { return true } +func (e *timeoutError) Temporary() bool { return true } + +type timeoutChan chan struct{} + +var ioInitOnce sync.Once +var ioCompletionPort syscall.Handle + +// ioResult contains the result of an asynchronous IO operation +type ioResult struct { + bytes uint32 + err error +} + +// ioOperation represents an outstanding asynchronous Win32 IO +type ioOperation struct { + o syscall.Overlapped + ch chan ioResult +} + +func initIo() { + h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff) + if err != nil { + panic(err) + } + ioCompletionPort = h + go ioCompletionProcessor(h) +} + +// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall. +// It takes ownership of this handle and will close it if it is garbage collected. +type win32File struct { + handle syscall.Handle + wg sync.WaitGroup + wgLock sync.RWMutex + closing atomicBool + readDeadline deadlineHandler + writeDeadline deadlineHandler +} + +type deadlineHandler struct { + setLock sync.Mutex + channel timeoutChan + channelLock sync.RWMutex + timer *time.Timer + timedout atomicBool +} + +// makeWin32File makes a new win32File from an existing file handle +func makeWin32File(h syscall.Handle) (*win32File, error) { + f := &win32File{handle: h} + ioInitOnce.Do(initIo) + _, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff) + if err != nil { + return nil, err + } + err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE) + if err != nil { + return nil, err + } + f.readDeadline.channel = make(timeoutChan) + f.writeDeadline.channel = make(timeoutChan) + return f, nil +} + +func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { + return makeWin32File(h) +} + +// closeHandle closes the resources associated with a Win32 handle +func (f *win32File) closeHandle() { + f.wgLock.Lock() + // Atomically set that we are closing, releasing the resources only once. + if !f.closing.swap(true) { + f.wgLock.Unlock() + // cancel all IO and wait for it to complete + cancelIoEx(f.handle, nil) + f.wg.Wait() + // at this point, no new IO can start + syscall.Close(f.handle) + f.handle = 0 + } else { + f.wgLock.Unlock() + } +} + +// Close closes a win32File. +func (f *win32File) Close() error { + f.closeHandle() + return nil +} + +// prepareIo prepares for a new IO operation. +// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning. +func (f *win32File) prepareIo() (*ioOperation, error) { + f.wgLock.RLock() + if f.closing.isSet() { + f.wgLock.RUnlock() + return nil, ErrFileClosed + } + f.wg.Add(1) + f.wgLock.RUnlock() + c := &ioOperation{} + c.ch = make(chan ioResult) + return c, nil +} + +// ioCompletionProcessor processes completed async IOs forever +func ioCompletionProcessor(h syscall.Handle) { + for { + var bytes uint32 + var key uintptr + var op *ioOperation + err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE) + if op == nil { + panic(err) + } + op.ch <- ioResult{bytes, err} + } +} + +// asyncIo processes the return value from ReadFile or WriteFile, blocking until +// the operation has actually completed. +func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { + if err != syscall.ERROR_IO_PENDING { + return int(bytes), err + } + + if f.closing.isSet() { + cancelIoEx(f.handle, &c.o) + } + + var timeout timeoutChan + if d != nil { + d.channelLock.Lock() + timeout = d.channel + d.channelLock.Unlock() + } + + var r ioResult + select { + case r = <-c.ch: + err = r.err + if err == syscall.ERROR_OPERATION_ABORTED { + if f.closing.isSet() { + err = ErrFileClosed + } + } + case <-timeout: + cancelIoEx(f.handle, &c.o) + r = <-c.ch + err = r.err + if err == syscall.ERROR_OPERATION_ABORTED { + err = ErrTimeout + } + } + + // runtime.KeepAlive is needed, as c is passed via native + // code to ioCompletionProcessor, c must remain alive + // until the channel read is complete. + runtime.KeepAlive(c) + return int(r.bytes), err +} + +// Read reads from a file handle. +func (f *win32File) Read(b []byte) (int, error) { + c, err := f.prepareIo() + if err != nil { + return 0, err + } + defer f.wg.Done() + + if f.readDeadline.timedout.isSet() { + return 0, ErrTimeout + } + + var bytes uint32 + err = syscall.ReadFile(f.handle, b, &bytes, &c.o) + n, err := f.asyncIo(c, &f.readDeadline, bytes, err) + runtime.KeepAlive(b) + + // Handle EOF conditions. + if err == nil && n == 0 && len(b) != 0 { + return 0, io.EOF + } else if err == syscall.ERROR_BROKEN_PIPE { + return 0, io.EOF + } else { + return n, err + } +} + +// Write writes to a file handle. +func (f *win32File) Write(b []byte) (int, error) { + c, err := f.prepareIo() + if err != nil { + return 0, err + } + defer f.wg.Done() + + if f.writeDeadline.timedout.isSet() { + return 0, ErrTimeout + } + + var bytes uint32 + err = syscall.WriteFile(f.handle, b, &bytes, &c.o) + n, err := f.asyncIo(c, &f.writeDeadline, bytes, err) + runtime.KeepAlive(b) + return n, err +} + +func (f *win32File) SetReadDeadline(deadline time.Time) error { + return f.readDeadline.set(deadline) +} + +func (f *win32File) SetWriteDeadline(deadline time.Time) error { + return f.writeDeadline.set(deadline) +} + +func (f *win32File) Flush() error { + return syscall.FlushFileBuffers(f.handle) +} + +func (d *deadlineHandler) set(deadline time.Time) error { + d.setLock.Lock() + defer d.setLock.Unlock() + + if d.timer != nil { + if !d.timer.Stop() { + <-d.channel + } + d.timer = nil + } + d.timedout.setFalse() + + select { + case <-d.channel: + d.channelLock.Lock() + d.channel = make(chan struct{}) + d.channelLock.Unlock() + default: + } + + if deadline.IsZero() { + return nil + } + + timeoutIO := func() { + d.timedout.setTrue() + close(d.channel) + } + + now := time.Now() + duration := deadline.Sub(now) + if deadline.After(now) { + // Deadline is in the future, set a timer to wait + d.timer = time.AfterFunc(duration, timeoutIO) + } else { + // Deadline is in the past. Cancel all pending IO now. + timeoutIO() + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/fileinfo.go b/src/runtime/vendor/github.com/Microsoft/go-winio/fileinfo.go new file mode 100644 index 000000000..ada2fbab6 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/fileinfo.go @@ -0,0 +1,61 @@ +// +build windows + +package winio + +import ( + "os" + "runtime" + "syscall" + "unsafe" +) + +//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx +//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle + +const ( + fileBasicInfo = 0 + fileIDInfo = 0x12 +) + +// FileBasicInfo contains file access time and file attributes information. +type FileBasicInfo struct { + CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime + FileAttributes uint32 + pad uint32 // padding +} + +// GetFileBasicInfo retrieves times and attributes for a file. +func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { + bi := &FileBasicInfo{} + if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { + return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} + } + runtime.KeepAlive(f) + return bi, nil +} + +// SetFileBasicInfo sets times and attributes for a file. +func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { + if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { + return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} + } + runtime.KeepAlive(f) + return nil +} + +// FileIDInfo contains the volume serial number and file ID for a file. This pair should be +// unique on a system. +type FileIDInfo struct { + VolumeSerialNumber uint64 + FileID [16]byte +} + +// GetFileID retrieves the unique (volume, file ID) pair for a file. +func GetFileID(f *os.File) (*FileIDInfo, error) { + fileID := &FileIDInfo{} + if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { + return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} + } + runtime.KeepAlive(f) + return fileID, nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/pipe.go b/src/runtime/vendor/github.com/Microsoft/go-winio/pipe.go new file mode 100644 index 000000000..d99eedb64 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/pipe.go @@ -0,0 +1,421 @@ +// +build windows + +package winio + +import ( + "errors" + "io" + "net" + "os" + "syscall" + "time" + "unsafe" +) + +//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe +//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW +//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW +//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo +//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW +//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc + +const ( + cERROR_PIPE_BUSY = syscall.Errno(231) + cERROR_NO_DATA = syscall.Errno(232) + cERROR_PIPE_CONNECTED = syscall.Errno(535) + cERROR_SEM_TIMEOUT = syscall.Errno(121) + + cPIPE_ACCESS_DUPLEX = 0x3 + cFILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000 + cSECURITY_SQOS_PRESENT = 0x100000 + cSECURITY_ANONYMOUS = 0 + + cPIPE_REJECT_REMOTE_CLIENTS = 0x8 + + cPIPE_UNLIMITED_INSTANCES = 255 + + cNMPWAIT_USE_DEFAULT_WAIT = 0 + cNMPWAIT_NOWAIT = 1 + + cPIPE_TYPE_MESSAGE = 4 + + cPIPE_READMODE_MESSAGE = 2 +) + +var ( + // ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed. + // This error should match net.errClosing since docker takes a dependency on its text. + ErrPipeListenerClosed = errors.New("use of closed network connection") + + errPipeWriteClosed = errors.New("pipe has been closed for write") +) + +type win32Pipe struct { + *win32File + path string +} + +type win32MessageBytePipe struct { + win32Pipe + writeClosed bool + readEOF bool +} + +type pipeAddress string + +func (f *win32Pipe) LocalAddr() net.Addr { + return pipeAddress(f.path) +} + +func (f *win32Pipe) RemoteAddr() net.Addr { + return pipeAddress(f.path) +} + +func (f *win32Pipe) SetDeadline(t time.Time) error { + f.SetReadDeadline(t) + f.SetWriteDeadline(t) + return nil +} + +// CloseWrite closes the write side of a message pipe in byte mode. +func (f *win32MessageBytePipe) CloseWrite() error { + if f.writeClosed { + return errPipeWriteClosed + } + err := f.win32File.Flush() + if err != nil { + return err + } + _, err = f.win32File.Write(nil) + if err != nil { + return err + } + f.writeClosed = true + return nil +} + +// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since +// they are used to implement CloseWrite(). +func (f *win32MessageBytePipe) Write(b []byte) (int, error) { + if f.writeClosed { + return 0, errPipeWriteClosed + } + if len(b) == 0 { + return 0, nil + } + return f.win32File.Write(b) +} + +// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message +// mode pipe will return io.EOF, as will all subsequent reads. +func (f *win32MessageBytePipe) Read(b []byte) (int, error) { + if f.readEOF { + return 0, io.EOF + } + n, err := f.win32File.Read(b) + if err == io.EOF { + // If this was the result of a zero-byte read, then + // it is possible that the read was due to a zero-size + // message. Since we are simulating CloseWrite with a + // zero-byte message, ensure that all future Read() calls + // also return EOF. + f.readEOF = true + } else if err == syscall.ERROR_MORE_DATA { + // ERROR_MORE_DATA indicates that the pipe's read mode is message mode + // and the message still has more bytes. Treat this as a success, since + // this package presents all named pipes as byte streams. + err = nil + } + return n, err +} + +func (s pipeAddress) Network() string { + return "pipe" +} + +func (s pipeAddress) String() string { + return string(s) +} + +// DialPipe connects to a named pipe by path, timing out if the connection +// takes longer than the specified duration. If timeout is nil, then we use +// a default timeout of 5 seconds. (We do not use WaitNamedPipe.) +func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { + var absTimeout time.Time + if timeout != nil { + absTimeout = time.Now().Add(*timeout) + } else { + absTimeout = time.Now().Add(time.Second * 2) + } + var err error + var h syscall.Handle + for { + h, err = createFile(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) + if err != cERROR_PIPE_BUSY { + break + } + if time.Now().After(absTimeout) { + return nil, ErrTimeout + } + + // Wait 10 msec and try again. This is a rather simplistic + // view, as we always try each 10 milliseconds. + time.Sleep(time.Millisecond * 10) + } + if err != nil { + return nil, &os.PathError{Op: "open", Path: path, Err: err} + } + + var flags uint32 + err = getNamedPipeInfo(h, &flags, nil, nil, nil) + if err != nil { + return nil, err + } + + f, err := makeWin32File(h) + if err != nil { + syscall.Close(h) + return nil, err + } + + // If the pipe is in message mode, return a message byte pipe, which + // supports CloseWrite(). + if flags&cPIPE_TYPE_MESSAGE != 0 { + return &win32MessageBytePipe{ + win32Pipe: win32Pipe{win32File: f, path: path}, + }, nil + } + return &win32Pipe{win32File: f, path: path}, nil +} + +type acceptResponse struct { + f *win32File + err error +} + +type win32PipeListener struct { + firstHandle syscall.Handle + path string + securityDescriptor []byte + config PipeConfig + acceptCh chan (chan acceptResponse) + closeCh chan int + doneCh chan int +} + +func makeServerPipeHandle(path string, securityDescriptor []byte, c *PipeConfig, first bool) (syscall.Handle, error) { + var flags uint32 = cPIPE_ACCESS_DUPLEX | syscall.FILE_FLAG_OVERLAPPED + if first { + flags |= cFILE_FLAG_FIRST_PIPE_INSTANCE + } + + var mode uint32 = cPIPE_REJECT_REMOTE_CLIENTS + if c.MessageMode { + mode |= cPIPE_TYPE_MESSAGE + } + + sa := &syscall.SecurityAttributes{} + sa.Length = uint32(unsafe.Sizeof(*sa)) + if securityDescriptor != nil { + len := uint32(len(securityDescriptor)) + sa.SecurityDescriptor = localAlloc(0, len) + defer localFree(sa.SecurityDescriptor) + copy((*[0xffff]byte)(unsafe.Pointer(sa.SecurityDescriptor))[:], securityDescriptor) + } + h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 0, sa) + if err != nil { + return 0, &os.PathError{Op: "open", Path: path, Err: err} + } + return h, nil +} + +func (l *win32PipeListener) makeServerPipe() (*win32File, error) { + h, err := makeServerPipeHandle(l.path, l.securityDescriptor, &l.config, false) + if err != nil { + return nil, err + } + f, err := makeWin32File(h) + if err != nil { + syscall.Close(h) + return nil, err + } + return f, nil +} + +func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) { + p, err := l.makeServerPipe() + if err != nil { + return nil, err + } + + // Wait for the client to connect. + ch := make(chan error) + go func(p *win32File) { + ch <- connectPipe(p) + }(p) + + select { + case err = <-ch: + if err != nil { + p.Close() + p = nil + } + case <-l.closeCh: + // Abort the connect request by closing the handle. + p.Close() + p = nil + err = <-ch + if err == nil || err == ErrFileClosed { + err = ErrPipeListenerClosed + } + } + return p, err +} + +func (l *win32PipeListener) listenerRoutine() { + closed := false + for !closed { + select { + case <-l.closeCh: + closed = true + case responseCh := <-l.acceptCh: + var ( + p *win32File + err error + ) + for { + p, err = l.makeConnectedServerPipe() + // If the connection was immediately closed by the client, try + // again. + if err != cERROR_NO_DATA { + break + } + } + responseCh <- acceptResponse{p, err} + closed = err == ErrPipeListenerClosed + } + } + syscall.Close(l.firstHandle) + l.firstHandle = 0 + // Notify Close() and Accept() callers that the handle has been closed. + close(l.doneCh) +} + +// PipeConfig contain configuration for the pipe listener. +type PipeConfig struct { + // SecurityDescriptor contains a Windows security descriptor in SDDL format. + SecurityDescriptor string + + // MessageMode determines whether the pipe is in byte or message mode. In either + // case the pipe is read in byte mode by default. The only practical difference in + // this implementation is that CloseWrite() is only supported for message mode pipes; + // CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only + // transferred to the reader (and returned as io.EOF in this implementation) + // when the pipe is in message mode. + MessageMode bool + + // InputBufferSize specifies the size the input buffer, in bytes. + InputBufferSize int32 + + // OutputBufferSize specifies the size the input buffer, in bytes. + OutputBufferSize int32 +} + +// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe. +// The pipe must not already exist. +func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { + var ( + sd []byte + err error + ) + if c == nil { + c = &PipeConfig{} + } + if c.SecurityDescriptor != "" { + sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor) + if err != nil { + return nil, err + } + } + h, err := makeServerPipeHandle(path, sd, c, true) + if err != nil { + return nil, err + } + // Create a client handle and connect it. This results in the pipe + // instance always existing, so that clients see ERROR_PIPE_BUSY + // rather than ERROR_FILE_NOT_FOUND. This ties the first instance + // up so that no other instances can be used. This would have been + // cleaner if the Win32 API matched CreateFile with ConnectNamedPipe + // instead of CreateNamedPipe. (Apparently created named pipes are + // considered to be in listening state regardless of whether any + // active calls to ConnectNamedPipe are outstanding.) + h2, err := createFile(path, 0, 0, nil, syscall.OPEN_EXISTING, cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) + if err != nil { + syscall.Close(h) + return nil, err + } + // Close the client handle. The server side of the instance will + // still be busy, leading to ERROR_PIPE_BUSY instead of + // ERROR_NOT_FOUND, as long as we don't close the server handle, + // or disconnect the client with DisconnectNamedPipe. + syscall.Close(h2) + l := &win32PipeListener{ + firstHandle: h, + path: path, + securityDescriptor: sd, + config: *c, + acceptCh: make(chan (chan acceptResponse)), + closeCh: make(chan int), + doneCh: make(chan int), + } + go l.listenerRoutine() + return l, nil +} + +func connectPipe(p *win32File) error { + c, err := p.prepareIo() + if err != nil { + return err + } + defer p.wg.Done() + + err = connectNamedPipe(p.handle, &c.o) + _, err = p.asyncIo(c, nil, 0, err) + if err != nil && err != cERROR_PIPE_CONNECTED { + return err + } + return nil +} + +func (l *win32PipeListener) Accept() (net.Conn, error) { + ch := make(chan acceptResponse) + select { + case l.acceptCh <- ch: + response := <-ch + err := response.err + if err != nil { + return nil, err + } + if l.config.MessageMode { + return &win32MessageBytePipe{ + win32Pipe: win32Pipe{win32File: response.f, path: l.path}, + }, nil + } + return &win32Pipe{win32File: response.f, path: l.path}, nil + case <-l.doneCh: + return nil, ErrPipeListenerClosed + } +} + +func (l *win32PipeListener) Close() error { + select { + case l.closeCh <- 1: + <-l.doneCh + case <-l.doneCh: + } + return nil +} + +func (l *win32PipeListener) Addr() net.Addr { + return pipeAddress(l.path) +} diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/privilege.go b/src/runtime/vendor/github.com/Microsoft/go-winio/privilege.go new file mode 100644 index 000000000..9c83d36fe --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/privilege.go @@ -0,0 +1,202 @@ +// +build windows + +package winio + +import ( + "bytes" + "encoding/binary" + "fmt" + "runtime" + "sync" + "syscall" + "unicode/utf16" + + "golang.org/x/sys/windows" +) + +//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges +//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf +//sys revertToSelf() (err error) = advapi32.RevertToSelf +//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken +//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread +//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW +//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW +//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW + +const ( + SE_PRIVILEGE_ENABLED = 2 + + ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300 + + SeBackupPrivilege = "SeBackupPrivilege" + SeRestorePrivilege = "SeRestorePrivilege" +) + +const ( + securityAnonymous = iota + securityIdentification + securityImpersonation + securityDelegation +) + +var ( + privNames = make(map[string]uint64) + privNameMutex sync.Mutex +) + +// PrivilegeError represents an error enabling privileges. +type PrivilegeError struct { + privileges []uint64 +} + +func (e *PrivilegeError) Error() string { + s := "" + if len(e.privileges) > 1 { + s = "Could not enable privileges " + } else { + s = "Could not enable privilege " + } + for i, p := range e.privileges { + if i != 0 { + s += ", " + } + s += `"` + s += getPrivilegeName(p) + s += `"` + } + return s +} + +// RunWithPrivilege enables a single privilege for a function call. +func RunWithPrivilege(name string, fn func() error) error { + return RunWithPrivileges([]string{name}, fn) +} + +// RunWithPrivileges enables privileges for a function call. +func RunWithPrivileges(names []string, fn func() error) error { + privileges, err := mapPrivileges(names) + if err != nil { + return err + } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + token, err := newThreadToken() + if err != nil { + return err + } + defer releaseThreadToken(token) + err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED) + if err != nil { + return err + } + return fn() +} + +func mapPrivileges(names []string) ([]uint64, error) { + var privileges []uint64 + privNameMutex.Lock() + defer privNameMutex.Unlock() + for _, name := range names { + p, ok := privNames[name] + if !ok { + err := lookupPrivilegeValue("", name, &p) + if err != nil { + return nil, err + } + privNames[name] = p + } + privileges = append(privileges, p) + } + return privileges, nil +} + +// EnableProcessPrivileges enables privileges globally for the process. +func EnableProcessPrivileges(names []string) error { + return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED) +} + +// DisableProcessPrivileges disables privileges globally for the process. +func DisableProcessPrivileges(names []string) error { + return enableDisableProcessPrivilege(names, 0) +} + +func enableDisableProcessPrivilege(names []string, action uint32) error { + privileges, err := mapPrivileges(names) + if err != nil { + return err + } + + p, _ := windows.GetCurrentProcess() + var token windows.Token + err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token) + if err != nil { + return err + } + + defer token.Close() + return adjustPrivileges(token, privileges, action) +} + +func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error { + var b bytes.Buffer + binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) + for _, p := range privileges { + binary.Write(&b, binary.LittleEndian, p) + binary.Write(&b, binary.LittleEndian, action) + } + prevState := make([]byte, b.Len()) + reqSize := uint32(0) + success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize) + if !success { + return err + } + if err == ERROR_NOT_ALL_ASSIGNED { + return &PrivilegeError{privileges} + } + return nil +} + +func getPrivilegeName(luid uint64) string { + var nameBuffer [256]uint16 + bufSize := uint32(len(nameBuffer)) + err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize) + if err != nil { + return fmt.Sprintf("", luid) + } + + var displayNameBuffer [256]uint16 + displayBufSize := uint32(len(displayNameBuffer)) + var langID uint32 + err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID) + if err != nil { + return fmt.Sprintf("", string(utf16.Decode(nameBuffer[:bufSize]))) + } + + return string(utf16.Decode(displayNameBuffer[:displayBufSize])) +} + +func newThreadToken() (windows.Token, error) { + err := impersonateSelf(securityImpersonation) + if err != nil { + return 0, err + } + + var token windows.Token + err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token) + if err != nil { + rerr := revertToSelf() + if rerr != nil { + panic(rerr) + } + return 0, err + } + return token, nil +} + +func releaseThreadToken(h windows.Token) { + err := revertToSelf() + if err != nil { + panic(err) + } + h.Close() +} diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/reparse.go b/src/runtime/vendor/github.com/Microsoft/go-winio/reparse.go new file mode 100644 index 000000000..fc1ee4d3a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/reparse.go @@ -0,0 +1,128 @@ +package winio + +import ( + "bytes" + "encoding/binary" + "fmt" + "strings" + "unicode/utf16" + "unsafe" +) + +const ( + reparseTagMountPoint = 0xA0000003 + reparseTagSymlink = 0xA000000C +) + +type reparseDataBuffer struct { + ReparseTag uint32 + ReparseDataLength uint16 + Reserved uint16 + SubstituteNameOffset uint16 + SubstituteNameLength uint16 + PrintNameOffset uint16 + PrintNameLength uint16 +} + +// ReparsePoint describes a Win32 symlink or mount point. +type ReparsePoint struct { + Target string + IsMountPoint bool +} + +// UnsupportedReparsePointError is returned when trying to decode a non-symlink or +// mount point reparse point. +type UnsupportedReparsePointError struct { + Tag uint32 +} + +func (e *UnsupportedReparsePointError) Error() string { + return fmt.Sprintf("unsupported reparse point %x", e.Tag) +} + +// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink +// or a mount point. +func DecodeReparsePoint(b []byte) (*ReparsePoint, error) { + tag := binary.LittleEndian.Uint32(b[0:4]) + return DecodeReparsePointData(tag, b[8:]) +} + +func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) { + isMountPoint := false + switch tag { + case reparseTagMountPoint: + isMountPoint = true + case reparseTagSymlink: + default: + return nil, &UnsupportedReparsePointError{tag} + } + nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6]) + if !isMountPoint { + nameOffset += 4 + } + nameLength := binary.LittleEndian.Uint16(b[6:8]) + name := make([]uint16, nameLength/2) + err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name) + if err != nil { + return nil, err + } + return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil +} + +func isDriveLetter(c byte) bool { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') +} + +// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or +// mount point. +func EncodeReparsePoint(rp *ReparsePoint) []byte { + // Generate an NT path and determine if this is a relative path. + var ntTarget string + relative := false + if strings.HasPrefix(rp.Target, `\\?\`) { + ntTarget = `\??\` + rp.Target[4:] + } else if strings.HasPrefix(rp.Target, `\\`) { + ntTarget = `\??\UNC\` + rp.Target[2:] + } else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' { + ntTarget = `\??\` + rp.Target + } else { + ntTarget = rp.Target + relative = true + } + + // The paths must be NUL-terminated even though they are counted strings. + target16 := utf16.Encode([]rune(rp.Target + "\x00")) + ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00")) + + size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8 + size += len(ntTarget16)*2 + len(target16)*2 + + tag := uint32(reparseTagMountPoint) + if !rp.IsMountPoint { + tag = reparseTagSymlink + size += 4 // Add room for symlink flags + } + + data := reparseDataBuffer{ + ReparseTag: tag, + ReparseDataLength: uint16(size), + SubstituteNameOffset: 0, + SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2), + PrintNameOffset: uint16(len(ntTarget16) * 2), + PrintNameLength: uint16((len(target16) - 1) * 2), + } + + var b bytes.Buffer + binary.Write(&b, binary.LittleEndian, &data) + if !rp.IsMountPoint { + flags := uint32(0) + if relative { + flags |= 1 + } + binary.Write(&b, binary.LittleEndian, flags) + } + + binary.Write(&b, binary.LittleEndian, ntTarget16) + binary.Write(&b, binary.LittleEndian, target16) + return b.Bytes() +} diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/sd.go b/src/runtime/vendor/github.com/Microsoft/go-winio/sd.go new file mode 100644 index 000000000..db1b370a1 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/sd.go @@ -0,0 +1,98 @@ +// +build windows + +package winio + +import ( + "syscall" + "unsafe" +) + +//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW +//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW +//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW +//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW +//sys localFree(mem uintptr) = LocalFree +//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength + +const ( + cERROR_NONE_MAPPED = syscall.Errno(1332) +) + +type AccountLookupError struct { + Name string + Err error +} + +func (e *AccountLookupError) Error() string { + if e.Name == "" { + return "lookup account: empty account name specified" + } + var s string + switch e.Err { + case cERROR_NONE_MAPPED: + s = "not found" + default: + s = e.Err.Error() + } + return "lookup account " + e.Name + ": " + s +} + +type SddlConversionError struct { + Sddl string + Err error +} + +func (e *SddlConversionError) Error() string { + return "convert " + e.Sddl + ": " + e.Err.Error() +} + +// LookupSidByName looks up the SID of an account by name +func LookupSidByName(name string) (sid string, err error) { + if name == "" { + return "", &AccountLookupError{name, cERROR_NONE_MAPPED} + } + + var sidSize, sidNameUse, refDomainSize uint32 + err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse) + if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { + return "", &AccountLookupError{name, err} + } + sidBuffer := make([]byte, sidSize) + refDomainBuffer := make([]uint16, refDomainSize) + err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) + if err != nil { + return "", &AccountLookupError{name, err} + } + var strBuffer *uint16 + err = convertSidToStringSid(&sidBuffer[0], &strBuffer) + if err != nil { + return "", &AccountLookupError{name, err} + } + sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:]) + localFree(uintptr(unsafe.Pointer(strBuffer))) + return sid, nil +} + +func SddlToSecurityDescriptor(sddl string) ([]byte, error) { + var sdBuffer uintptr + err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil) + if err != nil { + return nil, &SddlConversionError{sddl, err} + } + defer localFree(sdBuffer) + sd := make([]byte, getSecurityDescriptorLength(sdBuffer)) + copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)]) + return sd, nil +} + +func SecurityDescriptorToSddl(sd []byte) (string, error) { + var sddl *uint16 + // The returned string length seems to including an aribtrary number of terminating NULs. + // Don't use it. + err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil) + if err != nil { + return "", err + } + defer localFree(uintptr(unsafe.Pointer(sddl))) + return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/syscall.go b/src/runtime/vendor/github.com/Microsoft/go-winio/syscall.go new file mode 100644 index 000000000..20d64cf41 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/syscall.go @@ -0,0 +1,3 @@ +package winio + +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go diff --git a/src/runtime/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go b/src/runtime/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go new file mode 100644 index 000000000..3f527639a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go @@ -0,0 +1,520 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package winio + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + + procCancelIoEx = modkernel32.NewProc("CancelIoEx") + procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") + procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") + procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") + procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") + procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") + procCreateFileW = modkernel32.NewProc("CreateFileW") + procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") + procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") + procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") + procLocalAlloc = modkernel32.NewProc("LocalAlloc") + procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") + procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") + procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") + procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW") + procLocalFree = modkernel32.NewProc("LocalFree") + procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") + procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") + procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle") + procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") + procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") + procRevertToSelf = modadvapi32.NewProc("RevertToSelf") + procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") + procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") + procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") + procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW") + procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") + procBackupRead = modkernel32.NewProc("BackupRead") + procBackupWrite = modkernel32.NewProc("BackupWrite") +) + +func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0) + newport = syscall.Handle(r0) + if newport == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) { + r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(name) + if err != nil { + return + } + return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa) +} + +func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(name) + if err != nil { + return + } + return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile) +} + +func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func waitNamedPipe(name string, timeout uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(name) + if err != nil { + return + } + return _waitNamedPipe(_p0, timeout) +} + +func _waitNamedPipe(name *uint16, timeout uint32) (err error) { + r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { + r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func localAlloc(uFlags uint32, length uint32) (ptr uintptr) { + r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0) + ptr = uintptr(r0) + return +} + +func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(accountName) + if err != nil { + return + } + return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse) +} + +func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { + r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func convertSidToStringSid(sid *byte, str **uint16) (err error) { + r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(str) + if err != nil { + return + } + return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size) +} + +func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func localFree(mem uintptr) { + syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0) + return +} + +func getSecurityDescriptorLength(sd uintptr) (len uint32) { + r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) + len = uint32(r0) + return +} + +func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { + var _p0 uint32 + if releaseAll { + _p0 = 1 + } else { + _p0 = 0 + } + r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize))) + success = r0 != 0 + if true { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func impersonateSelf(level uint32) (err error) { + r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func revertToSelf() (err error) { + r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) { + var _p0 uint32 + if openAsSelf { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getCurrentThread() (h syscall.Handle) { + r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0) + h = syscall.Handle(r0) + return +} + +func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(systemName) + if err != nil { + return + } + var _p1 *uint16 + _p1, err = syscall.UTF16PtrFromString(name) + if err != nil { + return + } + return _lookupPrivilegeValue(_p0, _p1, luid) +} + +func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) { + r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(systemName) + if err != nil { + return + } + return _lookupPrivilegeName(_p0, luid, buffer, size) +} + +func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(systemName) + if err != nil { + return + } + return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId) +} + +func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { + var _p0 *byte + if len(b) > 0 { + _p0 = &b[0] + } + var _p1 uint32 + if abort { + _p1 = 1 + } else { + _p1 = 0 + } + var _p2 uint32 + if processSecurity { + _p2 = 1 + } else { + _p2 = 0 + } + r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { + var _p0 *byte + if len(b) > 0 { + _p0 = &b[0] + } + var _p1 uint32 + if abort { + _p1 = 1 + } else { + _p1 = 0 + } + var _p2 uint32 + if processSecurity { + _p2 = 1 + } else { + _p2 = 0 + } + r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/.gitignore b/src/runtime/vendor/github.com/Microsoft/hcsshim/.gitignore new file mode 100644 index 000000000..b883f1fdc --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/.gitignore @@ -0,0 +1 @@ +*.exe diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/.gometalinter.json b/src/runtime/vendor/github.com/Microsoft/hcsshim/.gometalinter.json new file mode 100644 index 000000000..00e9a6e2e --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/.gometalinter.json @@ -0,0 +1,17 @@ +{ + "Vendor": true, + "Deadline": "2m", + "Sort": [ + "linter", + "severity", + "path", + "line" + ], + "Skip": [ + "internal\\schema2" + ], + "EnableGC": true, + "Enable": [ + "gofmt" + ] +} \ No newline at end of file diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/LICENSE b/src/runtime/vendor/github.com/Microsoft/hcsshim/LICENSE new file mode 100644 index 000000000..49d21669a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/README.md b/src/runtime/vendor/github.com/Microsoft/hcsshim/README.md new file mode 100644 index 000000000..15b39181a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/README.md @@ -0,0 +1,41 @@ +# hcsshim + +[![Build status](https://ci.appveyor.com/api/projects/status/nbcw28mnkqml0loa/branch/master?svg=true)](https://ci.appveyor.com/project/WindowsVirtualization/hcsshim/branch/master) + +This package contains the Golang interface for using the Windows [Host Compute Service](https://blogs.technet.microsoft.com/virtualization/2017/01/27/introducing-the-host-compute-service-hcs/) (HCS) to launch and manage [Windows Containers](https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/). It also contains other helpers and functions for managing Windows Containers such as the Golang interface for the Host Network Service (HNS). + +It is primarily used in the [Moby Project](https://github.com/moby/moby), but it can be freely used by other projects as well. + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit https://cla.microsoft.com. + +When you submit a pull request, a CLA-bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Dependencies + +This project requires Golang 1.9 or newer to build. + +For system requirements to run this project, see the Microsoft docs on [Windows Container requirements](https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/system-requirements). + +## Reporting Security Issues + +Security issues and bugs should be reported privately, via email, to the Microsoft Security +Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should +receive a response within 24 hours. If for some reason you do not, please follow up via +email to ensure we received your original message. Further information, including the +[MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in +the [Security TechCenter](https://technet.microsoft.com/en-us/security/default). + +For additional details, see [Report a Computer Security Vulnerability](https://technet.microsoft.com/en-us/security/ff852094.aspx) on Technet + +--------------- +Copyright (c) 2018 Microsoft Corp. All rights reserved. diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/appveyor.yml b/src/runtime/vendor/github.com/Microsoft/hcsshim/appveyor.yml new file mode 100644 index 000000000..a8ec5a593 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/appveyor.yml @@ -0,0 +1,29 @@ +version: 0.1.{build} + +image: Visual Studio 2017 + +clone_folder: c:\gopath\src\github.com\Microsoft\hcsshim + +environment: + GOPATH: c:\gopath + PATH: C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin;%GOPATH%\bin;C:\gometalinter-2.0.12-windows-amd64;%PATH% + +stack: go 1.11 + +build_script: + - appveyor DownloadFile https://github.com/alecthomas/gometalinter/releases/download/v2.0.12/gometalinter-2.0.12-windows-amd64.zip + - 7z x gometalinter-2.0.12-windows-amd64.zip -y -oC:\ > NUL + - gometalinter.exe --config .gometalinter.json ./... + - go build ./cmd/wclayer + - go build ./cmd/runhcs + - go build ./cmd/tar2ext4 + - go test -v ./... -tags admin + - go test -c ./test/functional/ -tags functional + - go test -c ./test/runhcs/ -tags integration + +artifacts: + - path: 'wclayer.exe' + - path: 'runhcs.exe' + - path: 'tar2ext4.exe' + - path: 'functional.test.exe' + - path: 'runhcs.test.exe' \ No newline at end of file diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/container.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/container.go new file mode 100644 index 000000000..e142c3154 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/container.go @@ -0,0 +1,192 @@ +package hcsshim + +import ( + "fmt" + "os" + "time" + + "github.com/Microsoft/hcsshim/internal/hcs" + "github.com/Microsoft/hcsshim/internal/mergemaps" + "github.com/Microsoft/hcsshim/internal/schema1" +) + +// ContainerProperties holds the properties for a container and the processes running in that container +type ContainerProperties = schema1.ContainerProperties + +// MemoryStats holds the memory statistics for a container +type MemoryStats = schema1.MemoryStats + +// ProcessorStats holds the processor statistics for a container +type ProcessorStats = schema1.ProcessorStats + +// StorageStats holds the storage statistics for a container +type StorageStats = schema1.StorageStats + +// NetworkStats holds the network statistics for a container +type NetworkStats = schema1.NetworkStats + +// Statistics is the structure returned by a statistics call on a container +type Statistics = schema1.Statistics + +// ProcessList is the structure of an item returned by a ProcessList call on a container +type ProcessListItem = schema1.ProcessListItem + +// MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container +type MappedVirtualDiskController = schema1.MappedVirtualDiskController + +// Type of Request Support in ModifySystem +type RequestType = schema1.RequestType + +// Type of Resource Support in ModifySystem +type ResourceType = schema1.ResourceType + +// RequestType const +const ( + Add = schema1.Add + Remove = schema1.Remove + Network = schema1.Network +) + +// ResourceModificationRequestResponse is the structure used to send request to the container to modify the system +// Supported resource types are Network and Request Types are Add/Remove +type ResourceModificationRequestResponse = schema1.ResourceModificationRequestResponse + +type container struct { + system *hcs.System +} + +// createComputeSystemAdditionalJSON is read from the environment at initialisation +// time. It allows an environment variable to define additional JSON which +// is merged in the CreateComputeSystem call to HCS. +var createContainerAdditionalJSON []byte + +func init() { + createContainerAdditionalJSON = ([]byte)(os.Getenv("HCSSHIM_CREATECONTAINER_ADDITIONALJSON")) +} + +// CreateContainer creates a new container with the given configuration but does not start it. +func CreateContainer(id string, c *ContainerConfig) (Container, error) { + fullConfig, err := mergemaps.MergeJSON(c, createContainerAdditionalJSON) + if err != nil { + return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", createContainerAdditionalJSON, err) + } + + system, err := hcs.CreateComputeSystem(id, fullConfig) + if err != nil { + return nil, err + } + return &container{system}, err +} + +// OpenContainer opens an existing container by ID. +func OpenContainer(id string) (Container, error) { + system, err := hcs.OpenComputeSystem(id) + if err != nil { + return nil, err + } + return &container{system}, err +} + +// GetContainers gets a list of the containers on the system that match the query +func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) { + return hcs.GetComputeSystems(q) +} + +// Start synchronously starts the container. +func (container *container) Start() error { + return convertSystemError(container.system.Start(), container) +} + +// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds. +func (container *container) Shutdown() error { + return convertSystemError(container.system.Shutdown(), container) +} + +// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds. +func (container *container) Terminate() error { + return convertSystemError(container.system.Terminate(), container) +} + +// Waits synchronously waits for the container to shutdown or terminate. +func (container *container) Wait() error { + return convertSystemError(container.system.Wait(), container) +} + +// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It +// returns false if timeout occurs. +func (container *container) WaitTimeout(t time.Duration) error { + return convertSystemError(container.system.WaitTimeout(t), container) +} + +// Pause pauses the execution of a container. +func (container *container) Pause() error { + return convertSystemError(container.system.Pause(), container) +} + +// Resume resumes the execution of a container. +func (container *container) Resume() error { + return convertSystemError(container.system.Resume(), container) +} + +// HasPendingUpdates returns true if the container has updates pending to install +func (container *container) HasPendingUpdates() (bool, error) { + return false, nil +} + +// Statistics returns statistics for the container. This is a legacy v1 call +func (container *container) Statistics() (Statistics, error) { + properties, err := container.system.Properties(schema1.PropertyTypeStatistics) + if err != nil { + return Statistics{}, convertSystemError(err, container) + } + + return properties.Statistics, nil +} + +// ProcessList returns an array of ProcessListItems for the container. This is a legacy v1 call +func (container *container) ProcessList() ([]ProcessListItem, error) { + properties, err := container.system.Properties(schema1.PropertyTypeProcessList) + if err != nil { + return nil, convertSystemError(err, container) + } + + return properties.ProcessList, nil +} + +// This is a legacy v1 call +func (container *container) MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) { + properties, err := container.system.Properties(schema1.PropertyTypeMappedVirtualDisk) + if err != nil { + return nil, convertSystemError(err, container) + } + + return properties.MappedVirtualDiskControllers, nil +} + +// CreateProcess launches a new process within the container. +func (container *container) CreateProcess(c *ProcessConfig) (Process, error) { + p, err := container.system.CreateProcess(c) + if err != nil { + return nil, convertSystemError(err, container) + } + return &process{p}, nil +} + +// OpenProcess gets an interface to an existing process within the container. +func (container *container) OpenProcess(pid int) (Process, error) { + p, err := container.system.OpenProcess(pid) + if err != nil { + return nil, convertSystemError(err, container) + } + return &process{p}, nil +} + +// Close cleans up any state associated with the container but does not terminate or wait for it. +func (container *container) Close() error { + return convertSystemError(container.system.Close(), container) +} + +// Modify the System +func (container *container) Modify(config *ResourceModificationRequestResponse) error { + return convertSystemError(container.system.Modify(config), container) +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/errors.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/errors.go new file mode 100644 index 000000000..63efa23c7 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/errors.go @@ -0,0 +1,257 @@ +package hcsshim + +import ( + "fmt" + "syscall" + + "github.com/Microsoft/hcsshim/internal/hns" + + "github.com/Microsoft/hcsshim/internal/hcs" + "github.com/Microsoft/hcsshim/internal/hcserror" +) + +var ( + // ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists = hcs.exist + ErrComputeSystemDoesNotExist = hcs.ErrComputeSystemDoesNotExist + + // ErrElementNotFound is an error encountered when the object being referenced does not exist + ErrElementNotFound = hcs.ErrElementNotFound + + // ErrElementNotFound is an error encountered when the object being referenced does not exist + ErrNotSupported = hcs.ErrNotSupported + + // ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported + // decimal -2147024883 / hex 0x8007000d + ErrInvalidData = hcs.ErrInvalidData + + // ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed + ErrHandleClose = hcs.ErrHandleClose + + // ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method + ErrAlreadyClosed = hcs.ErrAlreadyClosed + + // ErrInvalidNotificationType is an error encountered when an invalid notification type is used + ErrInvalidNotificationType = hcs.ErrInvalidNotificationType + + // ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation + ErrInvalidProcessState = hcs.ErrInvalidProcessState + + // ErrTimeout is an error encountered when waiting on a notification times out + ErrTimeout = hcs.ErrTimeout + + // ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for + // a different expected notification + ErrUnexpectedContainerExit = hcs.ErrUnexpectedContainerExit + + // ErrUnexpectedProcessAbort is the error encountered when communication with the compute service + // is lost while waiting for a notification + ErrUnexpectedProcessAbort = hcs.ErrUnexpectedProcessAbort + + // ErrUnexpectedValue is an error encountered when hcs returns an invalid value + ErrUnexpectedValue = hcs.ErrUnexpectedValue + + // ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container + ErrVmcomputeAlreadyStopped = hcs.ErrVmcomputeAlreadyStopped + + // ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously + ErrVmcomputeOperationPending = hcs.ErrVmcomputeOperationPending + + // ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation + ErrVmcomputeOperationInvalidState = hcs.ErrVmcomputeOperationInvalidState + + // ErrProcNotFound is an error encountered when the the process cannot be found + ErrProcNotFound = hcs.ErrProcNotFound + + // ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2 + // builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3. + ErrVmcomputeOperationAccessIsDenied = hcs.ErrVmcomputeOperationAccessIsDenied + + // ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management + ErrVmcomputeInvalidJSON = hcs.ErrVmcomputeInvalidJSON + + // ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message + ErrVmcomputeUnknownMessage = hcs.ErrVmcomputeUnknownMessage + + // ErrNotSupported is an error encountered when hcs doesn't support the request + ErrPlatformNotSupported = hcs.ErrPlatformNotSupported +) + +type EndpointNotFoundError = hns.EndpointNotFoundError +type NetworkNotFoundError = hns.NetworkNotFoundError + +// ProcessError is an error encountered in HCS during an operation on a Process object +type ProcessError struct { + Process *process + Operation string + ExtraInfo string + Err error + Events []hcs.ErrorEvent +} + +// ContainerError is an error encountered in HCS during an operation on a Container object +type ContainerError struct { + Container *container + Operation string + ExtraInfo string + Err error + Events []hcs.ErrorEvent +} + +func (e *ContainerError) Error() string { + if e == nil { + return "" + } + + if e.Container == nil { + return "unexpected nil container for error: " + e.Err.Error() + } + + s := "container " + e.Container.system.ID() + + if e.Operation != "" { + s += " encountered an error during " + e.Operation + } + + switch e.Err.(type) { + case nil: + break + case syscall.Errno: + s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err)) + default: + s += fmt.Sprintf(": %s", e.Err.Error()) + } + + for _, ev := range e.Events { + s += "\n" + ev.String() + } + + if e.ExtraInfo != "" { + s += " extra info: " + e.ExtraInfo + } + + return s +} + +func makeContainerError(container *container, operation string, extraInfo string, err error) error { + // Don't double wrap errors + if _, ok := err.(*ContainerError); ok { + return err + } + containerError := &ContainerError{Container: container, Operation: operation, ExtraInfo: extraInfo, Err: err} + return containerError +} + +func (e *ProcessError) Error() string { + if e == nil { + return "" + } + + if e.Process == nil { + return "Unexpected nil process for error: " + e.Err.Error() + } + + s := fmt.Sprintf("process %d in container %s", e.Process.p.Pid(), e.Process.p.SystemID()) + if e.Operation != "" { + s += " encountered an error during " + e.Operation + } + + switch e.Err.(type) { + case nil: + break + case syscall.Errno: + s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err)) + default: + s += fmt.Sprintf(": %s", e.Err.Error()) + } + + for _, ev := range e.Events { + s += "\n" + ev.String() + } + + return s +} + +func makeProcessError(process *process, operation string, extraInfo string, err error) error { + // Don't double wrap errors + if _, ok := err.(*ProcessError); ok { + return err + } + processError := &ProcessError{Process: process, Operation: operation, ExtraInfo: extraInfo, Err: err} + return processError +} + +// IsNotExist checks if an error is caused by the Container or Process not existing. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +func IsNotExist(err error) bool { + if _, ok := err.(EndpointNotFoundError); ok { + return true + } + if _, ok := err.(NetworkNotFoundError); ok { + return true + } + return hcs.IsNotExist(getInnerError(err)) +} + +// IsAlreadyClosed checks if an error is caused by the Container or Process having been +// already closed by a call to the Close() method. +func IsAlreadyClosed(err error) bool { + return hcs.IsAlreadyClosed(getInnerError(err)) +} + +// IsPending returns a boolean indicating whether the error is that +// the requested operation is being completed in the background. +func IsPending(err error) bool { + return hcs.IsPending(getInnerError(err)) +} + +// IsTimeout returns a boolean indicating whether the error is caused by +// a timeout waiting for the operation to complete. +func IsTimeout(err error) bool { + return hcs.IsTimeout(getInnerError(err)) +} + +// IsAlreadyStopped returns a boolean indicating whether the error is caused by +// a Container or Process being already stopped. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +func IsAlreadyStopped(err error) bool { + return hcs.IsAlreadyStopped(getInnerError(err)) +} + +// IsNotSupported returns a boolean indicating whether the error is caused by +// unsupported platform requests +// Note: Currently Unsupported platform requests can be mean either +// ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage +// is thrown from the Platform +func IsNotSupported(err error) bool { + return hcs.IsNotSupported(getInnerError(err)) +} + +func getInnerError(err error) error { + switch pe := err.(type) { + case nil: + return nil + case *ContainerError: + err = pe.Err + case *ProcessError: + err = pe.Err + } + return err +} + +func convertSystemError(err error, c *container) error { + if serr, ok := err.(*hcs.SystemError); ok { + return &ContainerError{Container: c, Operation: serr.Op, ExtraInfo: serr.Extra, Err: serr.Err, Events: serr.Events} + } + return err +} + +func convertProcessError(err error, p *process) error { + if perr, ok := err.(*hcs.ProcessError); ok { + return &ProcessError{Process: p, Operation: perr.Op, Err: perr.Err, Events: perr.Events} + } + return err +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/functional_tests.ps1 b/src/runtime/vendor/github.com/Microsoft/hcsshim/functional_tests.ps1 new file mode 100644 index 000000000..ce6edbcf3 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/functional_tests.ps1 @@ -0,0 +1,12 @@ +# Requirements so far: +# dockerd running +# - image microsoft/nanoserver (matching host base image) docker load -i c:\baseimages\nanoserver.tar +# - image alpine (linux) docker pull --platform=linux alpine + + +# TODO: Add this a parameter for debugging. ie "functional-tests -debug=$true" +#$env:HCSSHIM_FUNCTIONAL_TESTS_DEBUG="yes please" + +#pushd uvm +go test -v -tags "functional uvmcreate uvmscratch uvmscsi uvmvpmem uvmvsmb uvmp9" ./... +#popd \ No newline at end of file diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/hcsshim.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/hcsshim.go new file mode 100644 index 000000000..ceb3ac85e --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/hcsshim.go @@ -0,0 +1,28 @@ +// Shim for the Host Compute Service (HCS) to manage Windows Server +// containers and Hyper-V containers. + +package hcsshim + +import ( + "syscall" + + "github.com/Microsoft/hcsshim/internal/hcserror" +) + +//go:generate go run mksyscall_windows.go -output zsyscall_windows.go hcsshim.go + +//sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId + +const ( + // Specific user-visible exit codes + WaitErrExecFailed = 32767 + + ERROR_GEN_FAILURE = hcserror.ERROR_GEN_FAILURE + ERROR_SHUTDOWN_IN_PROGRESS = syscall.Errno(1115) + WSAEINVAL = syscall.Errno(10022) + + // Timeout on wait calls + TimeoutInfinite = 0xFFFFFFFF +) + +type HcsError = hcserror.HcsError diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go new file mode 100644 index 000000000..eb013d2c4 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go @@ -0,0 +1,94 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +// HNSEndpoint represents a network endpoint in HNS +type HNSEndpoint = hns.HNSEndpoint + +// Namespace represents a Compartment. +type Namespace = hns.Namespace + +//SystemType represents the type of the system on which actions are done +type SystemType string + +// SystemType const +const ( + ContainerType SystemType = "Container" + VirtualMachineType SystemType = "VirtualMachine" + HostType SystemType = "Host" +) + +// EndpointAttachDetachRequest is the structure used to send request to the container to modify the system +// Supported resource types are Network and Request Types are Add/Remove +type EndpointAttachDetachRequest = hns.EndpointAttachDetachRequest + +// EndpointResquestResponse is object to get the endpoint request response +type EndpointResquestResponse = hns.EndpointResquestResponse + +// HNSEndpointRequest makes a HNS call to modify/query a network endpoint +func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) { + return hns.HNSEndpointRequest(method, path, request) +} + +// HNSListEndpointRequest makes a HNS call to query the list of available endpoints +func HNSListEndpointRequest() ([]HNSEndpoint, error) { + return hns.HNSListEndpointRequest() +} + +// HotAttachEndpoint makes a HCS Call to attach the endpoint to the container +func HotAttachEndpoint(containerID string, endpointID string) error { + return modifyNetworkEndpoint(containerID, endpointID, Add) +} + +// HotDetachEndpoint makes a HCS Call to detach the endpoint from the container +func HotDetachEndpoint(containerID string, endpointID string) error { + return modifyNetworkEndpoint(containerID, endpointID, Remove) +} + +// ModifyContainer corresponding to the container id, by sending a request +func modifyContainer(id string, request *ResourceModificationRequestResponse) error { + container, err := OpenContainer(id) + if err != nil { + if IsNotExist(err) { + return ErrComputeSystemDoesNotExist + } + return getInnerError(err) + } + defer container.Close() + err = container.Modify(request) + if err != nil { + if IsNotSupported(err) { + return ErrPlatformNotSupported + } + return getInnerError(err) + } + + return nil +} + +func modifyNetworkEndpoint(containerID string, endpointID string, request RequestType) error { + requestMessage := &ResourceModificationRequestResponse{ + Resource: Network, + Request: request, + Data: endpointID, + } + err := modifyContainer(containerID, requestMessage) + + if err != nil { + return err + } + + return nil +} + +// GetHNSEndpointByID get the Endpoint by ID +func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) { + return hns.GetHNSEndpointByID(endpointID) +} + +// GetHNSEndpointByName gets the endpoint filtered by Name +func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { + return hns.GetHNSEndpointByName(endpointName) +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/hnsglobals.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnsglobals.go new file mode 100644 index 000000000..2b5381904 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnsglobals.go @@ -0,0 +1,16 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +type HNSGlobals = hns.HNSGlobals +type HNSVersion = hns.HNSVersion + +var ( + HNSVersion1803 = hns.HNSVersion1803 +) + +func GetHNSGlobals() (*HNSGlobals, error) { + return hns.GetHNSGlobals() +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go new file mode 100644 index 000000000..f775fa1d0 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go @@ -0,0 +1,36 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +// Subnet is assoicated with a network and represents a list +// of subnets available to the network +type Subnet = hns.Subnet + +// MacPool is assoicated with a network and represents a list +// of macaddresses available to the network +type MacPool = hns.MacPool + +// HNSNetwork represents a network in HNS +type HNSNetwork = hns.HNSNetwork + +// HNSNetworkRequest makes a call into HNS to update/query a single network +func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) { + return hns.HNSNetworkRequest(method, path, request) +} + +// HNSListNetworkRequest makes a HNS call to query the list of available networks +func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) { + return hns.HNSListNetworkRequest(method, path, request) +} + +// GetHNSNetworkByID +func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) { + return hns.GetHNSNetworkByID(networkID) +} + +// GetHNSNetworkName filtered by Name +func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) { + return hns.GetHNSNetworkByName(networkName) +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/hnspolicy.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnspolicy.go new file mode 100644 index 000000000..a3e03ff8f --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnspolicy.go @@ -0,0 +1,57 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +// Type of Request Support in ModifySystem +type PolicyType = hns.PolicyType + +// RequestType const +const ( + Nat = hns.Nat + ACL = hns.ACL + PA = hns.PA + VLAN = hns.VLAN + VSID = hns.VSID + VNet = hns.VNet + L2Driver = hns.L2Driver + Isolation = hns.Isolation + QOS = hns.QOS + OutboundNat = hns.OutboundNat + ExternalLoadBalancer = hns.ExternalLoadBalancer + Route = hns.Route +) + +type NatPolicy = hns.NatPolicy + +type QosPolicy = hns.QosPolicy + +type IsolationPolicy = hns.IsolationPolicy + +type VlanPolicy = hns.VlanPolicy + +type VsidPolicy = hns.VsidPolicy + +type PaPolicy = hns.PaPolicy + +type OutboundNatPolicy = hns.OutboundNatPolicy + +type ActionType = hns.ActionType +type DirectionType = hns.DirectionType +type RuleType = hns.RuleType + +const ( + Allow = hns.Allow + Block = hns.Block + + In = hns.In + Out = hns.Out + + Host = hns.Host + Switch = hns.Switch +) + +type ACLPolicy = hns.ACLPolicy + +type Policy = hns.Policy diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go new file mode 100644 index 000000000..55aaa4a50 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go @@ -0,0 +1,47 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +// RoutePolicy is a structure defining schema for Route based Policy +type RoutePolicy = hns.RoutePolicy + +// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy +type ELBPolicy = hns.ELBPolicy + +// LBPolicy is a structure defining schema for LoadBalancing based Policy +type LBPolicy = hns.LBPolicy + +// PolicyList is a structure defining schema for Policy list request +type PolicyList = hns.PolicyList + +// HNSPolicyListRequest makes a call into HNS to update/query a single network +func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) { + return hns.HNSPolicyListRequest(method, path, request) +} + +// HNSListPolicyListRequest gets all the policy list +func HNSListPolicyListRequest() ([]PolicyList, error) { + return hns.HNSListPolicyListRequest() +} + +// PolicyListRequest makes a HNS call to modify/query a network policy list +func PolicyListRequest(method, path, request string) (*PolicyList, error) { + return hns.PolicyListRequest(method, path, request) +} + +// GetPolicyListByID get the policy list by ID +func GetPolicyListByID(policyListID string) (*PolicyList, error) { + return hns.GetPolicyListByID(policyListID) +} + +// AddLoadBalancer policy list for the specified endpoints +func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { + return hns.AddLoadBalancer(endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort) +} + +// AddRoute adds route policy list for the specified endpoints +func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) { + return hns.AddRoute(endpoints, destinationPrefix, nextHop, encapEnabled) +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/hnssupport.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnssupport.go new file mode 100644 index 000000000..69405244b --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/hnssupport.go @@ -0,0 +1,13 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +type HNSSupportedFeatures = hns.HNSSupportedFeatures + +type HNSAclFeatures = hns.HNSAclFeatures + +func GetHNSSupportedFeatures() HNSSupportedFeatures { + return hns.GetHNSSupportedFeatures() +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/interface.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/interface.go new file mode 100644 index 000000000..5b91e0cc5 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/interface.go @@ -0,0 +1,114 @@ +package hcsshim + +import ( + "io" + "time" + + "github.com/Microsoft/hcsshim/internal/schema1" +) + +// ProcessConfig is used as both the input of Container.CreateProcess +// and to convert the parameters to JSON for passing onto the HCS +type ProcessConfig = schema1.ProcessConfig + +type Layer = schema1.Layer +type MappedDir = schema1.MappedDir +type MappedPipe = schema1.MappedPipe +type HvRuntime = schema1.HvRuntime +type MappedVirtualDisk = schema1.MappedVirtualDisk + +// AssignedDevice represents a device that has been directly assigned to a container +// +// NOTE: Support added in RS5 +type AssignedDevice = schema1.AssignedDevice + +// ContainerConfig is used as both the input of CreateContainer +// and to convert the parameters to JSON for passing onto the HCS +type ContainerConfig = schema1.ContainerConfig + +type ComputeSystemQuery = schema1.ComputeSystemQuery + +// Container represents a created (but not necessarily running) container. +type Container interface { + // Start synchronously starts the container. + Start() error + + // Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds. + Shutdown() error + + // Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds. + Terminate() error + + // Waits synchronously waits for the container to shutdown or terminate. + Wait() error + + // WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It + // returns false if timeout occurs. + WaitTimeout(time.Duration) error + + // Pause pauses the execution of a container. + Pause() error + + // Resume resumes the execution of a container. + Resume() error + + // HasPendingUpdates returns true if the container has updates pending to install. + HasPendingUpdates() (bool, error) + + // Statistics returns statistics for a container. + Statistics() (Statistics, error) + + // ProcessList returns details for the processes in a container. + ProcessList() ([]ProcessListItem, error) + + // MappedVirtualDisks returns virtual disks mapped to a utility VM, indexed by controller + MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) + + // CreateProcess launches a new process within the container. + CreateProcess(c *ProcessConfig) (Process, error) + + // OpenProcess gets an interface to an existing process within the container. + OpenProcess(pid int) (Process, error) + + // Close cleans up any state associated with the container but does not terminate or wait for it. + Close() error + + // Modify the System + Modify(config *ResourceModificationRequestResponse) error +} + +// Process represents a running or exited process. +type Process interface { + // Pid returns the process ID of the process within the container. + Pid() int + + // Kill signals the process to terminate but does not wait for it to finish terminating. + Kill() error + + // Wait waits for the process to exit. + Wait() error + + // WaitTimeout waits for the process to exit or the duration to elapse. It returns + // false if timeout occurs. + WaitTimeout(time.Duration) error + + // ExitCode returns the exit code of the process. The process must have + // already terminated. + ExitCode() (int, error) + + // ResizeConsole resizes the console of the process. + ResizeConsole(width, height uint16) error + + // Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing + // these pipes does not close the underlying pipes; it should be possible to + // call this multiple times to get multiple interfaces. + Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) + + // CloseStdin closes the write side of the stdin pipe so that the process is + // notified on the read side that there is no more data in stdin. + CloseStdin() error + + // Close cleans up any state associated with the process but does not kill + // or wait on it. + Close() error +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/guestrequest/types.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/guestrequest/types.go new file mode 100644 index 000000000..5d3d0dfef --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/guestrequest/types.go @@ -0,0 +1,100 @@ +package guestrequest + +import ( + "github.com/Microsoft/hcsshim/internal/schema2" +) + +// Arguably, many of these (at least CombinedLayers) should have been generated +// by swagger. +// +// This will also change package name due to an inbound breaking change. + +// This class is used by a modify request to add or remove a combined layers +// structure in the guest. For windows, the GCS applies a filter in ContainerRootPath +// using the specified layers as the parent content. Ignores property ScratchPath +// since the container path is already the scratch path. For linux, the GCS unions +// the specified layers and ScratchPath together, placing the resulting union +// filesystem at ContainerRootPath. +type CombinedLayers struct { + ContainerRootPath string `json:"ContainerRootPath,omitempty"` + Layers []hcsschema.Layer `json:"Layers,omitempty"` + ScratchPath string `json:"ScratchPath,omitempty"` +} + +// Defines the schema for hosted settings passed to GCS and/or OpenGCS + +// SCSI. Scratch space for remote file-system commands, or R/W layer for containers +type LCOWMappedVirtualDisk struct { + MountPath string `json:"MountPath,omitempty"` // /tmp/scratch for an LCOW utility VM being used as a service VM + Lun uint8 `json:"Lun,omitempty"` + Controller uint8 `json:"Controller,omitempty"` + ReadOnly bool `json:"ReadOnly,omitempty"` +} + +type WCOWMappedVirtualDisk struct { + ContainerPath string `json:"ContainerPath,omitempty"` + Lun int32 `json:"Lun,omitempty"` +} + +type LCOWMappedDirectory struct { + MountPath string `json:"MountPath,omitempty"` + Port int32 `json:"Port,omitempty"` + ShareName string `json:"ShareName,omitempty"` // If empty not using ANames (not currently supported) + ReadOnly bool `json:"ReadOnly,omitempty"` +} + +// Read-only layers over VPMem +type LCOWMappedVPMemDevice struct { + DeviceNumber uint32 `json:"DeviceNumber,omitempty"` + MountPath string `json:"MountPath,omitempty"` // /tmp/pN +} + +type LCOWNetworkAdapter struct { + NamespaceID string `json:",omitempty"` + ID string `json:",omitempty"` + MacAddress string `json:",omitempty"` + IPAddress string `json:",omitempty"` + PrefixLength uint8 `json:",omitempty"` + GatewayAddress string `json:",omitempty"` + DNSSuffix string `json:",omitempty"` + DNSServerList string `json:",omitempty"` + EnableLowMetric bool `json:",omitempty"` + EncapOverhead uint16 `json:",omitempty"` +} + +type ResourceType string + +const ( + // These are constants for v2 schema modify guest requests. + ResourceTypeMappedDirectory ResourceType = "MappedDirectory" + ResourceTypeMappedVirtualDisk ResourceType = "MappedVirtualDisk" + ResourceTypeNetwork ResourceType = "Network" + ResourceTypeNetworkNamespace ResourceType = "NetworkNamespace" + ResourceTypeCombinedLayers ResourceType = "CombinedLayers" + ResourceTypeVPMemDevice ResourceType = "VPMemDevice" +) + +// GuestRequest is for modify commands passed to the guest. +type GuestRequest struct { + RequestType string `json:"RequestType,omitempty"` + ResourceType ResourceType `json:"ResourceType,omitempty"` + Settings interface{} `json:"Settings,omitempty"` +} + +type NetworkModifyRequest struct { + AdapterId string `json:"AdapterId,omitempty"` + RequestType string `json:"RequestType,omitempty"` + Settings interface{} `json:"Settings,omitempty"` +} + +type RS4NetworkModifyRequest struct { + AdapterInstanceId string `json:"AdapterInstanceId,omitempty"` + RequestType string `json:"RequestType,omitempty"` + Settings interface{} `json:"Settings,omitempty"` +} + +// SignalProcessOptions is the options passed to either WCOW or LCOW +// to signal a given process. +type SignalProcessOptions struct { + Signal int `json:,omitempty` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/guid/guid.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/guid/guid.go new file mode 100644 index 000000000..e9e45c030 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/guid/guid.go @@ -0,0 +1,69 @@ +package guid + +import ( + "crypto/rand" + "encoding/json" + "fmt" + "io" + "strconv" + "strings" +) + +var _ = (json.Marshaler)(&GUID{}) +var _ = (json.Unmarshaler)(&GUID{}) + +type GUID [16]byte + +func New() GUID { + g := GUID{} + _, err := io.ReadFull(rand.Reader, g[:]) + if err != nil { + panic(err) + } + return g +} + +func (g GUID) String() string { + return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x-%02x", g[3], g[2], g[1], g[0], g[5], g[4], g[7], g[6], g[8:10], g[10:]) +} + +func FromString(s string) GUID { + if len(s) != 36 { + panic(fmt.Sprintf("invalid GUID length: %d", len(s))) + } + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + panic("invalid GUID format") + } + indexOrder := [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34, + } + byteOrder := [16]int{ + 3, 2, 1, 0, + 5, 4, + 7, 6, + 8, 9, + 10, 11, 12, 13, 14, 15, + } + var g GUID + for i, x := range indexOrder { + b, err := strconv.ParseInt(s[x:x+2], 16, 16) + if err != nil { + panic(err) + } + g[byteOrder[i]] = byte(b) + } + return g +} + +func (g GUID) MarshalJSON() ([]byte, error) { + return json.Marshal(g.String()) +} + +func (g *GUID) UnmarshalJSON(data []byte) error { + *g = FromString(strings.Trim(string(data), "\"")) + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go new file mode 100644 index 000000000..f9a922a4b --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go @@ -0,0 +1,104 @@ +package hcs + +import ( + "sync" + "syscall" + + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/sirupsen/logrus" +) + +var ( + nextCallback uintptr + callbackMap = map[uintptr]*notifcationWatcherContext{} + callbackMapLock = sync.RWMutex{} + + notificationWatcherCallback = syscall.NewCallback(notificationWatcher) + + // Notifications for HCS_SYSTEM handles + hcsNotificationSystemExited hcsNotification = 0x00000001 + hcsNotificationSystemCreateCompleted hcsNotification = 0x00000002 + hcsNotificationSystemStartCompleted hcsNotification = 0x00000003 + hcsNotificationSystemPauseCompleted hcsNotification = 0x00000004 + hcsNotificationSystemResumeCompleted hcsNotification = 0x00000005 + hcsNotificationSystemCrashReport hcsNotification = 0x00000006 + hcsNotificationSystemSiloJobCreated hcsNotification = 0x00000007 + hcsNotificationSystemSaveCompleted hcsNotification = 0x00000008 + hcsNotificationSystemRdpEnhancedModeStateChanged hcsNotification = 0x00000009 + hcsNotificationSystemShutdownFailed hcsNotification = 0x0000000A + hcsNotificationSystemGetPropertiesCompleted hcsNotification = 0x0000000B + hcsNotificationSystemModifyCompleted hcsNotification = 0x0000000C + hcsNotificationSystemCrashInitiated hcsNotification = 0x0000000D + hcsNotificationSystemGuestConnectionClosed hcsNotification = 0x0000000E + + // Notifications for HCS_PROCESS handles + hcsNotificationProcessExited hcsNotification = 0x00010000 + + // Common notifications + hcsNotificationInvalid hcsNotification = 0x00000000 + hcsNotificationServiceDisconnect hcsNotification = 0x01000000 +) + +type hcsNotification uint32 +type notificationChannel chan error + +type notifcationWatcherContext struct { + channels notificationChannels + handle hcsCallback +} + +type notificationChannels map[hcsNotification]notificationChannel + +func newChannels() notificationChannels { + channels := make(notificationChannels) + + channels[hcsNotificationSystemExited] = make(notificationChannel, 1) + channels[hcsNotificationSystemCreateCompleted] = make(notificationChannel, 1) + channels[hcsNotificationSystemStartCompleted] = make(notificationChannel, 1) + channels[hcsNotificationSystemPauseCompleted] = make(notificationChannel, 1) + channels[hcsNotificationSystemResumeCompleted] = make(notificationChannel, 1) + channels[hcsNotificationProcessExited] = make(notificationChannel, 1) + channels[hcsNotificationServiceDisconnect] = make(notificationChannel, 1) + channels[hcsNotificationSystemCrashReport] = make(notificationChannel, 1) + channels[hcsNotificationSystemSiloJobCreated] = make(notificationChannel, 1) + channels[hcsNotificationSystemSaveCompleted] = make(notificationChannel, 1) + channels[hcsNotificationSystemRdpEnhancedModeStateChanged] = make(notificationChannel, 1) + channels[hcsNotificationSystemShutdownFailed] = make(notificationChannel, 1) + channels[hcsNotificationSystemGetPropertiesCompleted] = make(notificationChannel, 1) + channels[hcsNotificationSystemModifyCompleted] = make(notificationChannel, 1) + channels[hcsNotificationSystemCrashInitiated] = make(notificationChannel, 1) + channels[hcsNotificationSystemGuestConnectionClosed] = make(notificationChannel, 1) + + return channels +} + +func closeChannels(channels notificationChannels) { + for _, c := range channels { + close(c) + } +} + +func notificationWatcher(notificationType hcsNotification, callbackNumber uintptr, notificationStatus uintptr, notificationData *uint16) uintptr { + var result error + if int32(notificationStatus) < 0 { + result = interop.Win32FromHresult(notificationStatus) + } + + callbackMapLock.RLock() + context := callbackMap[callbackNumber] + callbackMapLock.RUnlock() + + if context == nil { + return 0 + } + + if channel, ok := context.channels[notificationType]; ok { + channel <- result + } else { + logrus.WithFields(logrus.Fields{ + "notification-type": notificationType, + }).Warn("Received a callback of an unsupported type") + } + + return 0 +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/cgo.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/cgo.go new file mode 100644 index 000000000..3669c34aa --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/cgo.go @@ -0,0 +1,7 @@ +package hcs + +import "C" + +// This import is needed to make the library compile as CGO because HCSSHIM +// only works with CGO due to callbacks from HCS comming back from a C thread +// which is not supported without CGO. See https://github.com/golang/go/issues/10973 diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go new file mode 100644 index 000000000..079b56535 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go @@ -0,0 +1,287 @@ +package hcs + +import ( + "encoding/json" + "errors" + "fmt" + "syscall" + + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/logfields" + "github.com/sirupsen/logrus" +) + +var ( + // ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists + ErrComputeSystemDoesNotExist = syscall.Errno(0xc037010e) + + // ErrElementNotFound is an error encountered when the object being referenced does not exist + ErrElementNotFound = syscall.Errno(0x490) + + // ErrElementNotFound is an error encountered when the object being referenced does not exist + ErrNotSupported = syscall.Errno(0x32) + + // ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported + // decimal -2147024883 / hex 0x8007000d + ErrInvalidData = syscall.Errno(0xd) + + // ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed + ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed") + + // ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method + ErrAlreadyClosed = errors.New("hcsshim: the handle has already been closed") + + // ErrInvalidNotificationType is an error encountered when an invalid notification type is used + ErrInvalidNotificationType = errors.New("hcsshim: invalid notification type") + + // ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation + ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation") + + // ErrTimeout is an error encountered when waiting on a notification times out + ErrTimeout = errors.New("hcsshim: timeout waiting for notification") + + // ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for + // a different expected notification + ErrUnexpectedContainerExit = errors.New("unexpected container exit") + + // ErrUnexpectedProcessAbort is the error encountered when communication with the compute service + // is lost while waiting for a notification + ErrUnexpectedProcessAbort = errors.New("lost communication with compute service") + + // ErrUnexpectedValue is an error encountered when hcs returns an invalid value + ErrUnexpectedValue = errors.New("unexpected value returned from hcs") + + // ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container + ErrVmcomputeAlreadyStopped = syscall.Errno(0xc0370110) + + // ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously + ErrVmcomputeOperationPending = syscall.Errno(0xC0370103) + + // ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation + ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105) + + // ErrProcNotFound is an error encountered when the the process cannot be found + ErrProcNotFound = syscall.Errno(0x7f) + + // ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2 + // builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3. + ErrVmcomputeOperationAccessIsDenied = syscall.Errno(0x5) + + // ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management + ErrVmcomputeInvalidJSON = syscall.Errno(0xc037010d) + + // ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message + ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b) + + // ErrVmcomputeUnexpectedExit is an error encountered when the compute system terminates unexpectedly + ErrVmcomputeUnexpectedExit = syscall.Errno(0xC0370106) + + // ErrNotSupported is an error encountered when hcs doesn't support the request + ErrPlatformNotSupported = errors.New("unsupported platform request") +) + +type ErrorEvent struct { + Message string `json:"Message,omitempty"` // Fully formated error message + StackTrace string `json:"StackTrace,omitempty"` // Stack trace in string form + Provider string `json:"Provider,omitempty"` + EventID uint16 `json:"EventId,omitempty"` + Flags uint32 `json:"Flags,omitempty"` + Source string `json:"Source,omitempty"` + //Data []EventData `json:"Data,omitempty"` // Omit this as HCS doesn't encode this well. It's more confusing to include. It is however logged in debug mode (see processHcsResult function) +} + +type hcsResult struct { + Error int32 + ErrorMessage string + ErrorEvents []ErrorEvent `json:"ErrorEvents,omitempty"` +} + +func (ev *ErrorEvent) String() string { + evs := "[Event Detail: " + ev.Message + if ev.StackTrace != "" { + evs += " Stack Trace: " + ev.StackTrace + } + if ev.Provider != "" { + evs += " Provider: " + ev.Provider + } + if ev.EventID != 0 { + evs = fmt.Sprintf("%s EventID: %d", evs, ev.EventID) + } + if ev.Flags != 0 { + evs = fmt.Sprintf("%s flags: %d", evs, ev.Flags) + } + if ev.Source != "" { + evs += " Source: " + ev.Source + } + evs += "]" + return evs +} + +func processHcsResult(resultp *uint16) []ErrorEvent { + if resultp != nil { + resultj := interop.ConvertAndFreeCoTaskMemString(resultp) + logrus.WithField(logfields.JSON, resultj). + Debug("HCS Result") + result := &hcsResult{} + if err := json.Unmarshal([]byte(resultj), result); err != nil { + logrus.WithFields(logrus.Fields{ + logfields.JSON: resultj, + logrus.ErrorKey: err, + }).Warning("Could not unmarshal HCS result") + return nil + } + return result.ErrorEvents + } + return nil +} + +type HcsError struct { + Op string + Err error + Events []ErrorEvent +} + +func (e *HcsError) Error() string { + s := e.Op + ": " + e.Err.Error() + for _, ev := range e.Events { + s += "\n" + ev.String() + } + return s +} + +// ProcessError is an error encountered in HCS during an operation on a Process object +type ProcessError struct { + SystemID string + Pid int + Op string + Err error + Events []ErrorEvent +} + +// SystemError is an error encountered in HCS during an operation on a Container object +type SystemError struct { + ID string + Op string + Err error + Extra string + Events []ErrorEvent +} + +func (e *SystemError) Error() string { + s := e.Op + " " + e.ID + ": " + e.Err.Error() + for _, ev := range e.Events { + s += "\n" + ev.String() + } + if e.Extra != "" { + s += "\n(extra info: " + e.Extra + ")" + } + return s +} + +func makeSystemError(system *System, op string, extra string, err error, events []ErrorEvent) error { + // Don't double wrap errors + if _, ok := err.(*SystemError); ok { + return err + } + return &SystemError{ + ID: system.ID(), + Op: op, + Extra: extra, + Err: err, + Events: events, + } +} + +func (e *ProcessError) Error() string { + s := fmt.Sprintf("%s %s:%d: %s", e.Op, e.SystemID, e.Pid, e.Err.Error()) + for _, ev := range e.Events { + s += "\n" + ev.String() + } + return s +} + +func makeProcessError(process *Process, op string, err error, events []ErrorEvent) error { + // Don't double wrap errors + if _, ok := err.(*ProcessError); ok { + return err + } + return &ProcessError{ + Pid: process.Pid(), + SystemID: process.SystemID(), + Op: op, + Err: err, + Events: events, + } +} + +// IsNotExist checks if an error is caused by the Container or Process not existing. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +func IsNotExist(err error) bool { + err = getInnerError(err) + return err == ErrComputeSystemDoesNotExist || + err == ErrElementNotFound || + err == ErrProcNotFound +} + +// IsAlreadyClosed checks if an error is caused by the Container or Process having been +// already closed by a call to the Close() method. +func IsAlreadyClosed(err error) bool { + err = getInnerError(err) + return err == ErrAlreadyClosed +} + +// IsPending returns a boolean indicating whether the error is that +// the requested operation is being completed in the background. +func IsPending(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeOperationPending +} + +// IsTimeout returns a boolean indicating whether the error is caused by +// a timeout waiting for the operation to complete. +func IsTimeout(err error) bool { + err = getInnerError(err) + return err == ErrTimeout +} + +// IsAlreadyStopped returns a boolean indicating whether the error is caused by +// a Container or Process being already stopped. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +func IsAlreadyStopped(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeAlreadyStopped || + err == ErrElementNotFound || + err == ErrProcNotFound +} + +// IsNotSupported returns a boolean indicating whether the error is caused by +// unsupported platform requests +// Note: Currently Unsupported platform requests can be mean either +// ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage +// is thrown from the Platform +func IsNotSupported(err error) bool { + err = getInnerError(err) + // If Platform doesn't recognize or support the request sent, below errors are seen + return err == ErrVmcomputeInvalidJSON || + err == ErrInvalidData || + err == ErrNotSupported || + err == ErrVmcomputeUnknownMessage +} + +func getInnerError(err error) error { + switch pe := err.(type) { + case nil: + return nil + case *HcsError: + err = pe.Err + case *SystemError: + err = pe.Err + case *ProcessError: + err = pe.Err + } + return err +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go new file mode 100644 index 000000000..b0d49cbcf --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go @@ -0,0 +1,48 @@ +// Shim for the Host Compute Service (HCS) to manage Windows Server +// containers and Hyper-V containers. + +package hcs + +import ( + "syscall" +) + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go hcs.go + +//sys hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) = vmcompute.HcsEnumerateComputeSystems? +//sys hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystem? +//sys hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsOpenComputeSystem? +//sys hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) = vmcompute.HcsCloseComputeSystem? +//sys hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsStartComputeSystem? +//sys hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsShutdownComputeSystem? +//sys hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsTerminateComputeSystem? +//sys hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsPauseComputeSystem? +//sys hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsResumeComputeSystem? +//sys hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetComputeSystemProperties? +//sys hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) = vmcompute.HcsModifyComputeSystem? +//sys hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterComputeSystemCallback? +//sys hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterComputeSystemCallback? + +//sys hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsCreateProcess? +//sys hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsOpenProcess? +//sys hcsCloseProcess(process hcsProcess) (hr error) = vmcompute.HcsCloseProcess? +//sys hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) = vmcompute.HcsTerminateProcess? +//sys hcsSignalProcess(process hcsProcess, options string, result **uint16) (hr error) = vmcompute.HcsTerminateProcess? +//sys hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) = vmcompute.HcsGetProcessInfo? +//sys hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) = vmcompute.HcsGetProcessProperties? +//sys hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) = vmcompute.HcsModifyProcess? +//sys hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetServiceProperties? +//sys hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterProcessCallback? +//sys hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterProcessCallback? + +type hcsSystem syscall.Handle +type hcsProcess syscall.Handle +type hcsCallback syscall.Handle + +type hcsProcessInformation struct { + ProcessId uint32 + Reserved uint32 + StdInput syscall.Handle + StdOutput syscall.Handle + StdError syscall.Handle +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/log.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/log.go new file mode 100644 index 000000000..6d03b17a2 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/log.go @@ -0,0 +1,20 @@ +package hcs + +import "github.com/sirupsen/logrus" + +func logOperationBegin(ctx logrus.Fields, msg string) { + logrus.WithFields(ctx).Debug(msg) +} + +func logOperationEnd(ctx logrus.Fields, msg string, err error) { + // Copy the log and fields first. + log := logrus.WithFields(ctx) + if err == nil { + log.Debug(msg) + } else { + // Edit only the copied field data to avoid race conditions on the + // write. + log.Data[logrus.ErrorKey] = err + log.Error(msg) + } +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go new file mode 100644 index 000000000..41e20bbf9 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go @@ -0,0 +1,459 @@ +package hcs + +import ( + "encoding/json" + "io" + "sync" + "syscall" + "time" + + "github.com/Microsoft/hcsshim/internal/guestrequest" + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/logfields" + "github.com/sirupsen/logrus" +) + +// ContainerError is an error encountered in HCS +type Process struct { + handleLock sync.RWMutex + handle hcsProcess + processID int + system *System + cachedPipes *cachedPipes + callbackNumber uintptr + + logctx logrus.Fields +} + +func newProcess(process hcsProcess, processID int, computeSystem *System) *Process { + return &Process{ + handle: process, + processID: processID, + system: computeSystem, + logctx: logrus.Fields{ + logfields.ContainerID: computeSystem.ID(), + logfields.ProcessID: processID, + }, + } +} + +type cachedPipes struct { + stdIn syscall.Handle + stdOut syscall.Handle + stdErr syscall.Handle +} + +type processModifyRequest struct { + Operation string + ConsoleSize *consoleSize `json:",omitempty"` + CloseHandle *closeHandle `json:",omitempty"` +} + +type consoleSize struct { + Height uint16 + Width uint16 +} + +type closeHandle struct { + Handle string +} + +type ProcessStatus struct { + ProcessID uint32 + Exited bool + ExitCode uint32 + LastWaitResult int32 +} + +const ( + stdIn string = "StdIn" + stdOut string = "StdOut" + stdErr string = "StdErr" +) + +const ( + modifyConsoleSize string = "ConsoleSize" + modifyCloseHandle string = "CloseHandle" +) + +// Pid returns the process ID of the process within the container. +func (process *Process) Pid() int { + return process.processID +} + +// SystemID returns the ID of the process's compute system. +func (process *Process) SystemID() string { + return process.system.ID() +} + +func (process *Process) logOperationBegin(operation string) { + logOperationBegin( + process.logctx, + operation+" - Begin Operation") +} + +func (process *Process) logOperationEnd(operation string, err error) { + var result string + if err == nil { + result = "Success" + } else { + result = "Error" + } + + logOperationEnd( + process.logctx, + operation+" - End Operation - "+result, + err) +} + +// Signal signals the process with `options`. +func (process *Process) Signal(options guestrequest.SignalProcessOptions) (err error) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + operation := "hcsshim::Process::Signal" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(operation, err) }() + + if process.handle == 0 { + return makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + optionsb, err := json.Marshal(options) + if err != nil { + return err + } + + optionsStr := string(optionsb) + + var resultp *uint16 + syscallWatcher(process.logctx, func() { + err = hcsSignalProcess(process.handle, optionsStr, &resultp) + }) + events := processHcsResult(resultp) + if err != nil { + return makeProcessError(process, operation, err, events) + } + + return nil +} + +// Kill signals the process to terminate but does not wait for it to finish terminating. +func (process *Process) Kill() (err error) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + operation := "hcsshim::Process::Kill" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(operation, err) }() + + if process.handle == 0 { + return makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + var resultp *uint16 + syscallWatcher(process.logctx, func() { + err = hcsTerminateProcess(process.handle, &resultp) + }) + events := processHcsResult(resultp) + if err != nil { + return makeProcessError(process, operation, err, events) + } + + return nil +} + +// Wait waits for the process to exit. +func (process *Process) Wait() (err error) { + operation := "hcsshim::Process::Wait" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(operation, err) }() + + err = waitForNotification(process.callbackNumber, hcsNotificationProcessExited, nil) + if err != nil { + return makeProcessError(process, operation, err, nil) + } + + return nil +} + +// WaitTimeout waits for the process to exit or the duration to elapse. It returns +// false if timeout occurs. +func (process *Process) WaitTimeout(timeout time.Duration) (err error) { + operation := "hcssshim::Process::WaitTimeout" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(operation, err) }() + + err = waitForNotification(process.callbackNumber, hcsNotificationProcessExited, &timeout) + if err != nil { + return makeProcessError(process, operation, err, nil) + } + + return nil +} + +// ResizeConsole resizes the console of the process. +func (process *Process) ResizeConsole(width, height uint16) (err error) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + operation := "hcsshim::Process::ResizeConsole" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(operation, err) }() + + if process.handle == 0 { + return makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + modifyRequest := processModifyRequest{ + Operation: modifyConsoleSize, + ConsoleSize: &consoleSize{ + Height: height, + Width: width, + }, + } + + modifyRequestb, err := json.Marshal(modifyRequest) + if err != nil { + return err + } + + modifyRequestStr := string(modifyRequestb) + + var resultp *uint16 + err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp) + events := processHcsResult(resultp) + if err != nil { + return makeProcessError(process, operation, err, events) + } + + return nil +} + +func (process *Process) Properties() (_ *ProcessStatus, err error) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + operation := "hcsshim::Process::Properties" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(operation, err) }() + + if process.handle == 0 { + return nil, makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + var ( + resultp *uint16 + propertiesp *uint16 + ) + syscallWatcher(process.logctx, func() { + err = hcsGetProcessProperties(process.handle, &propertiesp, &resultp) + }) + events := processHcsResult(resultp) + if err != nil { + return nil, makeProcessError(process, operation, err, events) + } + + if propertiesp == nil { + return nil, ErrUnexpectedValue + } + propertiesRaw := interop.ConvertAndFreeCoTaskMemBytes(propertiesp) + + properties := &ProcessStatus{} + if err := json.Unmarshal(propertiesRaw, properties); err != nil { + return nil, makeProcessError(process, operation, err, nil) + } + + return properties, nil +} + +// ExitCode returns the exit code of the process. The process must have +// already terminated. +func (process *Process) ExitCode() (_ int, err error) { + operation := "hcsshim::Process::ExitCode" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(operation, err) }() + + properties, err := process.Properties() + if err != nil { + return 0, makeProcessError(process, operation, err, nil) + } + + if properties.Exited == false { + return 0, makeProcessError(process, operation, ErrInvalidProcessState, nil) + } + + if properties.LastWaitResult != 0 { + return 0, makeProcessError(process, operation, syscall.Errno(properties.LastWaitResult), nil) + } + + return int(properties.ExitCode), nil +} + +// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing +// these pipes does not close the underlying pipes; it should be possible to +// call this multiple times to get multiple interfaces. +func (process *Process) Stdio() (_ io.WriteCloser, _ io.ReadCloser, _ io.ReadCloser, err error) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + operation := "hcsshim::Process::Stdio" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(operation, err) }() + + if process.handle == 0 { + return nil, nil, nil, makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + var stdIn, stdOut, stdErr syscall.Handle + + if process.cachedPipes == nil { + var ( + processInfo hcsProcessInformation + resultp *uint16 + ) + err = hcsGetProcessInfo(process.handle, &processInfo, &resultp) + events := processHcsResult(resultp) + if err != nil { + return nil, nil, nil, makeProcessError(process, operation, err, events) + } + + stdIn, stdOut, stdErr = processInfo.StdInput, processInfo.StdOutput, processInfo.StdError + } else { + // Use cached pipes + stdIn, stdOut, stdErr = process.cachedPipes.stdIn, process.cachedPipes.stdOut, process.cachedPipes.stdErr + + // Invalidate the cache + process.cachedPipes = nil + } + + pipes, err := makeOpenFiles([]syscall.Handle{stdIn, stdOut, stdErr}) + if err != nil { + return nil, nil, nil, makeProcessError(process, operation, err, nil) + } + + return pipes[0], pipes[1], pipes[2], nil +} + +// CloseStdin closes the write side of the stdin pipe so that the process is +// notified on the read side that there is no more data in stdin. +func (process *Process) CloseStdin() (err error) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + operation := "hcsshim::Process::CloseStdin" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(operation, err) }() + + if process.handle == 0 { + return makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + modifyRequest := processModifyRequest{ + Operation: modifyCloseHandle, + CloseHandle: &closeHandle{ + Handle: stdIn, + }, + } + + modifyRequestb, err := json.Marshal(modifyRequest) + if err != nil { + return err + } + + modifyRequestStr := string(modifyRequestb) + + var resultp *uint16 + err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp) + events := processHcsResult(resultp) + if err != nil { + return makeProcessError(process, operation, err, events) + } + + return nil +} + +// Close cleans up any state associated with the process but does not kill +// or wait on it. +func (process *Process) Close() (err error) { + process.handleLock.Lock() + defer process.handleLock.Unlock() + + operation := "hcsshim::Process::Close" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(operation, err) }() + + // Don't double free this + if process.handle == 0 { + return nil + } + + if err = process.unregisterCallback(); err != nil { + return makeProcessError(process, operation, err, nil) + } + + if err = hcsCloseProcess(process.handle); err != nil { + return makeProcessError(process, operation, err, nil) + } + + process.handle = 0 + + return nil +} + +func (process *Process) registerCallback() error { + context := ¬ifcationWatcherContext{ + channels: newChannels(), + } + + callbackMapLock.Lock() + callbackNumber := nextCallback + nextCallback++ + callbackMap[callbackNumber] = context + callbackMapLock.Unlock() + + var callbackHandle hcsCallback + err := hcsRegisterProcessCallback(process.handle, notificationWatcherCallback, callbackNumber, &callbackHandle) + if err != nil { + return err + } + context.handle = callbackHandle + process.callbackNumber = callbackNumber + + return nil +} + +func (process *Process) unregisterCallback() error { + callbackNumber := process.callbackNumber + + callbackMapLock.RLock() + context := callbackMap[callbackNumber] + callbackMapLock.RUnlock() + + if context == nil { + return nil + } + + handle := context.handle + + if handle == 0 { + return nil + } + + // hcsUnregisterProcessCallback has its own syncronization + // to wait for all callbacks to complete. We must NOT hold the callbackMapLock. + err := hcsUnregisterProcessCallback(handle) + if err != nil { + return err + } + + closeChannels(context.channels) + + callbackMapLock.Lock() + callbackMap[callbackNumber] = nil + callbackMapLock.Unlock() + + handle = 0 + + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go new file mode 100644 index 000000000..20b242524 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go @@ -0,0 +1,685 @@ +package hcs + +import ( + "encoding/json" + "os" + "strconv" + "sync" + "syscall" + "time" + + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/logfields" + "github.com/Microsoft/hcsshim/internal/schema1" + "github.com/Microsoft/hcsshim/internal/timeout" + "github.com/sirupsen/logrus" +) + +// currentContainerStarts is used to limit the number of concurrent container +// starts. +var currentContainerStarts containerStarts + +type containerStarts struct { + maxParallel int + inProgress int + sync.Mutex +} + +func init() { + mpsS := os.Getenv("HCSSHIM_MAX_PARALLEL_START") + if len(mpsS) > 0 { + mpsI, err := strconv.Atoi(mpsS) + if err != nil || mpsI < 0 { + return + } + currentContainerStarts.maxParallel = mpsI + } +} + +type System struct { + handleLock sync.RWMutex + handle hcsSystem + id string + callbackNumber uintptr + + logctx logrus.Fields +} + +func newSystem(id string) *System { + return &System{ + id: id, + logctx: logrus.Fields{ + logfields.ContainerID: id, + }, + } +} + +func (computeSystem *System) logOperationBegin(operation string) { + logOperationBegin( + computeSystem.logctx, + operation+" - Begin Operation") +} + +func (computeSystem *System) logOperationEnd(operation string, err error) { + var result string + if err == nil { + result = "Success" + } else { + result = "Error" + } + + logOperationEnd( + computeSystem.logctx, + operation+" - End Operation - "+result, + err) +} + +// CreateComputeSystem creates a new compute system with the given configuration but does not start it. +func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (_ *System, err error) { + operation := "hcsshim::CreateComputeSystem" + + computeSystem := newSystem(id) + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + hcsDocumentB, err := json.Marshal(hcsDocumentInterface) + if err != nil { + return nil, err + } + + hcsDocument := string(hcsDocumentB) + + logrus.WithFields(computeSystem.logctx). + WithField(logfields.JSON, hcsDocument). + Debug("HCS ComputeSystem Document") + + var ( + resultp *uint16 + identity syscall.Handle + createError error + ) + syscallWatcher(computeSystem.logctx, func() { + createError = hcsCreateComputeSystem(id, hcsDocument, identity, &computeSystem.handle, &resultp) + }) + + if createError == nil || IsPending(createError) { + if err = computeSystem.registerCallback(); err != nil { + // Terminate the compute system if it still exists. We're okay to + // ignore a failure here. + computeSystem.Terminate() + return nil, makeSystemError(computeSystem, operation, "", err, nil) + } + } + + events, err := processAsyncHcsResult(createError, resultp, computeSystem.callbackNumber, hcsNotificationSystemCreateCompleted, &timeout.SystemCreate) + if err != nil { + if err == ErrTimeout { + // Terminate the compute system if it still exists. We're okay to + // ignore a failure here. + computeSystem.Terminate() + } + return nil, makeSystemError(computeSystem, operation, hcsDocument, err, events) + } + + return computeSystem, nil +} + +// OpenComputeSystem opens an existing compute system by ID. +func OpenComputeSystem(id string) (_ *System, err error) { + operation := "hcsshim::OpenComputeSystem" + + computeSystem := newSystem(id) + computeSystem.logOperationBegin(operation) + defer func() { + if IsNotExist(err) { + computeSystem.logOperationEnd(operation, nil) + } else { + computeSystem.logOperationEnd(operation, err) + } + }() + + var ( + handle hcsSystem + resultp *uint16 + ) + err = hcsOpenComputeSystem(id, &handle, &resultp) + events := processHcsResult(resultp) + if err != nil { + return nil, makeSystemError(computeSystem, operation, "", err, events) + } + + computeSystem.handle = handle + + if err = computeSystem.registerCallback(); err != nil { + return nil, makeSystemError(computeSystem, operation, "", err, nil) + } + + return computeSystem, nil +} + +// GetComputeSystems gets a list of the compute systems on the system that match the query +func GetComputeSystems(q schema1.ComputeSystemQuery) (_ []schema1.ContainerProperties, err error) { + operation := "hcsshim::GetComputeSystems" + fields := logrus.Fields{} + logOperationBegin( + fields, + operation+" - Begin Operation") + + defer func() { + var result string + if err == nil { + result = "Success" + } else { + result = "Error" + } + + logOperationEnd( + fields, + operation+" - End Operation - "+result, + err) + }() + + queryb, err := json.Marshal(q) + if err != nil { + return nil, err + } + + query := string(queryb) + + logrus.WithFields(fields). + WithField(logfields.JSON, query). + Debug("HCS ComputeSystem Query") + + var ( + resultp *uint16 + computeSystemsp *uint16 + ) + + syscallWatcher(fields, func() { + err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp) + }) + events := processHcsResult(resultp) + if err != nil { + return nil, &HcsError{Op: operation, Err: err, Events: events} + } + + if computeSystemsp == nil { + return nil, ErrUnexpectedValue + } + computeSystemsRaw := interop.ConvertAndFreeCoTaskMemBytes(computeSystemsp) + computeSystems := []schema1.ContainerProperties{} + if err = json.Unmarshal(computeSystemsRaw, &computeSystems); err != nil { + return nil, err + } + + return computeSystems, nil +} + +// Start synchronously starts the computeSystem. +func (computeSystem *System) Start() (err error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcsshim::ComputeSystem::Start" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Start", "", ErrAlreadyClosed, nil) + } + + // This is a very simple backoff-retry loop to limit the number + // of parallel container starts if environment variable + // HCSSHIM_MAX_PARALLEL_START is set to a positive integer. + // It should generally only be used as a workaround to various + // platform issues that exist between RS1 and RS4 as of Aug 2018 + if currentContainerStarts.maxParallel > 0 { + for { + currentContainerStarts.Lock() + if currentContainerStarts.inProgress < currentContainerStarts.maxParallel { + currentContainerStarts.inProgress++ + currentContainerStarts.Unlock() + break + } + if currentContainerStarts.inProgress == currentContainerStarts.maxParallel { + currentContainerStarts.Unlock() + time.Sleep(100 * time.Millisecond) + } + } + // Make sure we decrement the count when we are done. + defer func() { + currentContainerStarts.Lock() + currentContainerStarts.inProgress-- + currentContainerStarts.Unlock() + }() + } + + var resultp *uint16 + syscallWatcher(computeSystem.logctx, func() { + err = hcsStartComputeSystem(computeSystem.handle, "", &resultp) + }) + events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.SystemStart) + if err != nil { + return makeSystemError(computeSystem, "Start", "", err, events) + } + + return nil +} + +// ID returns the compute system's identifier. +func (computeSystem *System) ID() string { + return computeSystem.id +} + +// Shutdown requests a compute system shutdown, if IsPending() on the error returned is true, +// it may not actually be shut down until Wait() succeeds. +func (computeSystem *System) Shutdown() (err error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcsshim::ComputeSystem::Shutdown" + computeSystem.logOperationBegin(operation) + defer func() { + if IsAlreadyStopped(err) { + computeSystem.logOperationEnd(operation, nil) + } else { + computeSystem.logOperationEnd(operation, err) + } + }() + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Shutdown", "", ErrAlreadyClosed, nil) + } + + var resultp *uint16 + syscallWatcher(computeSystem.logctx, func() { + err = hcsShutdownComputeSystem(computeSystem.handle, "", &resultp) + }) + events := processHcsResult(resultp) + if err != nil { + return makeSystemError(computeSystem, "Shutdown", "", err, events) + } + + return nil +} + +// Terminate requests a compute system terminate, if IsPending() on the error returned is true, +// it may not actually be shut down until Wait() succeeds. +func (computeSystem *System) Terminate() (err error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcsshim::ComputeSystem::Terminate" + computeSystem.logOperationBegin(operation) + defer func() { + if IsPending(err) { + computeSystem.logOperationEnd(operation, nil) + } else { + computeSystem.logOperationEnd(operation, err) + } + }() + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Terminate", "", ErrAlreadyClosed, nil) + } + + var resultp *uint16 + syscallWatcher(computeSystem.logctx, func() { + err = hcsTerminateComputeSystem(computeSystem.handle, "", &resultp) + }) + events := processHcsResult(resultp) + if err != nil && err != ErrVmcomputeAlreadyStopped { + return makeSystemError(computeSystem, "Terminate", "", err, events) + } + + return nil +} + +// Wait synchronously waits for the compute system to shutdown or terminate. +func (computeSystem *System) Wait() (err error) { + operation := "hcsshim::ComputeSystem::Wait" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + err = waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, nil) + if err != nil { + return makeSystemError(computeSystem, "Wait", "", err, nil) + } + + return nil +} + +// WaitExpectedError synchronously waits for the compute system to shutdown or +// terminate, and ignores the passed error if it occurs. +func (computeSystem *System) WaitExpectedError(expected error) (err error) { + operation := "hcsshim::ComputeSystem::WaitExpectedError" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + err = waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, nil) + if err != nil && getInnerError(err) != expected { + return makeSystemError(computeSystem, "WaitExpectedError", "", err, nil) + } + + return nil +} + +// WaitTimeout synchronously waits for the compute system to terminate or the duration to elapse. +// If the timeout expires, IsTimeout(err) == true +func (computeSystem *System) WaitTimeout(timeout time.Duration) (err error) { + operation := "hcsshim::ComputeSystem::WaitTimeout" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + err = waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, &timeout) + if err != nil { + return makeSystemError(computeSystem, "WaitTimeout", "", err, nil) + } + + return nil +} + +func (computeSystem *System) Properties(types ...schema1.PropertyType) (_ *schema1.ContainerProperties, err error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcsshim::ComputeSystem::Properties" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + queryj, err := json.Marshal(schema1.PropertyQuery{types}) + if err != nil { + return nil, makeSystemError(computeSystem, "Properties", "", err, nil) + } + + logrus.WithFields(computeSystem.logctx). + WithField(logfields.JSON, queryj). + Debug("HCS ComputeSystem Properties Query") + + var resultp, propertiesp *uint16 + syscallWatcher(computeSystem.logctx, func() { + err = hcsGetComputeSystemProperties(computeSystem.handle, string(queryj), &propertiesp, &resultp) + }) + events := processHcsResult(resultp) + if err != nil { + return nil, makeSystemError(computeSystem, "Properties", "", err, events) + } + + if propertiesp == nil { + return nil, ErrUnexpectedValue + } + propertiesRaw := interop.ConvertAndFreeCoTaskMemBytes(propertiesp) + properties := &schema1.ContainerProperties{} + if err := json.Unmarshal(propertiesRaw, properties); err != nil { + return nil, makeSystemError(computeSystem, "Properties", "", err, nil) + } + + return properties, nil +} + +// Pause pauses the execution of the computeSystem. This feature is not enabled in TP5. +func (computeSystem *System) Pause() (err error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcsshim::ComputeSystem::Pause" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Pause", "", ErrAlreadyClosed, nil) + } + + var resultp *uint16 + syscallWatcher(computeSystem.logctx, func() { + err = hcsPauseComputeSystem(computeSystem.handle, "", &resultp) + }) + events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.SystemPause) + if err != nil { + return makeSystemError(computeSystem, "Pause", "", err, events) + } + + return nil +} + +// Resume resumes the execution of the computeSystem. This feature is not enabled in TP5. +func (computeSystem *System) Resume() (err error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcsshim::ComputeSystem::Resume" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Resume", "", ErrAlreadyClosed, nil) + } + + var resultp *uint16 + syscallWatcher(computeSystem.logctx, func() { + err = hcsResumeComputeSystem(computeSystem.handle, "", &resultp) + }) + events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.SystemResume) + if err != nil { + return makeSystemError(computeSystem, "Resume", "", err, events) + } + + return nil +} + +// CreateProcess launches a new process within the computeSystem. +func (computeSystem *System) CreateProcess(c interface{}) (_ *Process, err error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcsshim::ComputeSystem::CreateProcess" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + var ( + processInfo hcsProcessInformation + processHandle hcsProcess + resultp *uint16 + ) + + if computeSystem.handle == 0 { + return nil, makeSystemError(computeSystem, "CreateProcess", "", ErrAlreadyClosed, nil) + } + + configurationb, err := json.Marshal(c) + if err != nil { + return nil, makeSystemError(computeSystem, "CreateProcess", "", err, nil) + } + + configuration := string(configurationb) + + logrus.WithFields(computeSystem.logctx). + WithField(logfields.JSON, configuration). + Debug("HCS ComputeSystem Process Document") + + syscallWatcher(computeSystem.logctx, func() { + err = hcsCreateProcess(computeSystem.handle, configuration, &processInfo, &processHandle, &resultp) + }) + events := processHcsResult(resultp) + if err != nil { + return nil, makeSystemError(computeSystem, "CreateProcess", configuration, err, events) + } + + logrus.WithFields(computeSystem.logctx). + WithField(logfields.ProcessID, processInfo.ProcessId). + Debug("HCS ComputeSystem CreateProcess PID") + + process := newProcess(processHandle, int(processInfo.ProcessId), computeSystem) + process.cachedPipes = &cachedPipes{ + stdIn: processInfo.StdInput, + stdOut: processInfo.StdOutput, + stdErr: processInfo.StdError, + } + + if err = process.registerCallback(); err != nil { + return nil, makeSystemError(computeSystem, "CreateProcess", "", err, nil) + } + + return process, nil +} + +// OpenProcess gets an interface to an existing process within the computeSystem. +func (computeSystem *System) OpenProcess(pid int) (_ *Process, err error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + // Add PID for the context of this operation + computeSystem.logctx[logfields.ProcessID] = pid + defer delete(computeSystem.logctx, logfields.ProcessID) + + operation := "hcsshim::ComputeSystem::OpenProcess" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + var ( + processHandle hcsProcess + resultp *uint16 + ) + + if computeSystem.handle == 0 { + return nil, makeSystemError(computeSystem, "OpenProcess", "", ErrAlreadyClosed, nil) + } + + syscallWatcher(computeSystem.logctx, func() { + err = hcsOpenProcess(computeSystem.handle, uint32(pid), &processHandle, &resultp) + }) + events := processHcsResult(resultp) + if err != nil { + return nil, makeSystemError(computeSystem, "OpenProcess", "", err, events) + } + + process := newProcess(processHandle, pid, computeSystem) + if err = process.registerCallback(); err != nil { + return nil, makeSystemError(computeSystem, "OpenProcess", "", err, nil) + } + + return process, nil +} + +// Close cleans up any state associated with the compute system but does not terminate or wait for it. +func (computeSystem *System) Close() (err error) { + computeSystem.handleLock.Lock() + defer computeSystem.handleLock.Unlock() + + operation := "hcsshim::ComputeSystem::Close" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + // Don't double free this + if computeSystem.handle == 0 { + return nil + } + + if err = computeSystem.unregisterCallback(); err != nil { + return makeSystemError(computeSystem, "Close", "", err, nil) + } + + syscallWatcher(computeSystem.logctx, func() { + err = hcsCloseComputeSystem(computeSystem.handle) + }) + if err != nil { + return makeSystemError(computeSystem, "Close", "", err, nil) + } + + computeSystem.handle = 0 + + return nil +} + +func (computeSystem *System) registerCallback() error { + context := ¬ifcationWatcherContext{ + channels: newChannels(), + } + + callbackMapLock.Lock() + callbackNumber := nextCallback + nextCallback++ + callbackMap[callbackNumber] = context + callbackMapLock.Unlock() + + var callbackHandle hcsCallback + err := hcsRegisterComputeSystemCallback(computeSystem.handle, notificationWatcherCallback, callbackNumber, &callbackHandle) + if err != nil { + return err + } + context.handle = callbackHandle + computeSystem.callbackNumber = callbackNumber + + return nil +} + +func (computeSystem *System) unregisterCallback() error { + callbackNumber := computeSystem.callbackNumber + + callbackMapLock.RLock() + context := callbackMap[callbackNumber] + callbackMapLock.RUnlock() + + if context == nil { + return nil + } + + handle := context.handle + + if handle == 0 { + return nil + } + + // hcsUnregisterComputeSystemCallback has its own syncronization + // to wait for all callbacks to complete. We must NOT hold the callbackMapLock. + err := hcsUnregisterComputeSystemCallback(handle) + if err != nil { + return err + } + + closeChannels(context.channels) + + callbackMapLock.Lock() + callbackMap[callbackNumber] = nil + callbackMapLock.Unlock() + + handle = 0 + + return nil +} + +// Modify the System by sending a request to HCS +func (computeSystem *System) Modify(config interface{}) (err error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcsshim::ComputeSystem::Modify" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(operation, err) }() + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Modify", "", ErrAlreadyClosed, nil) + } + + requestJSON, err := json.Marshal(config) + if err != nil { + return err + } + + requestString := string(requestJSON) + + logrus.WithFields(computeSystem.logctx). + WithField(logfields.JSON, requestString). + Debug("HCS ComputeSystem Modify Document") + + var resultp *uint16 + syscallWatcher(computeSystem.logctx, func() { + err = hcsModifyComputeSystem(computeSystem.handle, requestString, &resultp) + }) + events := processHcsResult(resultp) + if err != nil { + return makeSystemError(computeSystem, "Modify", requestString, err, events) + } + + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go new file mode 100644 index 000000000..a638677ed --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go @@ -0,0 +1,33 @@ +package hcs + +import ( + "io" + "syscall" + + "github.com/Microsoft/go-winio" +) + +// makeOpenFiles calls winio.MakeOpenFile for each handle in a slice but closes all the handles +// if there is an error. +func makeOpenFiles(hs []syscall.Handle) (_ []io.ReadWriteCloser, err error) { + fs := make([]io.ReadWriteCloser, len(hs)) + for i, h := range hs { + if h != syscall.Handle(0) { + if err == nil { + fs[i], err = winio.MakeOpenFile(h) + } + if err != nil { + syscall.Close(h) + } + } + } + if err != nil { + for _, f := range fs { + if f != nil { + f.Close() + } + } + return nil, err + } + return fs, nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go new file mode 100644 index 000000000..91e212c57 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go @@ -0,0 +1,63 @@ +package hcs + +import ( + "time" + + "github.com/sirupsen/logrus" +) + +func processAsyncHcsResult(err error, resultp *uint16, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) ([]ErrorEvent, error) { + events := processHcsResult(resultp) + if IsPending(err) { + return nil, waitForNotification(callbackNumber, expectedNotification, timeout) + } + + return events, err +} + +func waitForNotification(callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error { + callbackMapLock.RLock() + channels := callbackMap[callbackNumber].channels + callbackMapLock.RUnlock() + + expectedChannel := channels[expectedNotification] + if expectedChannel == nil { + logrus.Errorf("unknown notification type in waitForNotification %x", expectedNotification) + return ErrInvalidNotificationType + } + + var c <-chan time.Time + if timeout != nil { + timer := time.NewTimer(*timeout) + c = timer.C + defer timer.Stop() + } + + select { + case err, ok := <-expectedChannel: + if !ok { + return ErrHandleClose + } + return err + case err, ok := <-channels[hcsNotificationSystemExited]: + if !ok { + return ErrHandleClose + } + // If the expected notification is hcsNotificationSystemExited which of the two selects + // chosen is random. Return the raw error if hcsNotificationSystemExited is expected + if channels[hcsNotificationSystemExited] == expectedChannel { + return err + } + return ErrUnexpectedContainerExit + case _, ok := <-channels[hcsNotificationServiceDisconnect]: + if !ok { + return ErrHandleClose + } + // hcsNotificationServiceDisconnect should never be an expected notification + // it does not need the same handling as hcsNotificationSystemExited + return ErrUnexpectedProcessAbort + case <-c: + return ErrTimeout + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go new file mode 100644 index 000000000..f85ed3187 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go @@ -0,0 +1,41 @@ +package hcs + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/logfields" + "github.com/Microsoft/hcsshim/internal/timeout" + "github.com/sirupsen/logrus" +) + +// syscallWatcher is used as a very simple goroutine around calls into +// the platform. In some cases, we have seen HCS APIs not returning due to +// various bugs, and the goroutine making the syscall ends up not returning, +// prior to its async callback. By spinning up a syscallWatcher, it allows +// us to at least log a warning if a syscall doesn't complete in a reasonable +// amount of time. +// +// Usage is: +// +// syscallWatcher(logContext, func() { +// err = (args...) +// }) +// + +func syscallWatcher(logContext logrus.Fields, syscallLambda func()) { + ctx, cancel := context.WithTimeout(context.Background(), timeout.SyscallWatcher) + defer cancel() + go watchFunc(ctx, logContext) + syscallLambda() +} + +func watchFunc(ctx context.Context, logContext logrus.Fields) { + select { + case <-ctx.Done(): + if ctx.Err() != context.Canceled { + logrus.WithFields(logContext). + WithField(logfields.Timeout, timeout.SyscallWatcher). + Warning("Syscall did not complete within operation timeout. This may indicate a platform issue. If it appears to be making no forward progress, obtain the stacks and see if there is a syscall stuck in the platform API for a significant length of time.") + } + } +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go new file mode 100644 index 000000000..fcd5cdc87 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go @@ -0,0 +1,533 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package hcs + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") + + procHcsEnumerateComputeSystems = modvmcompute.NewProc("HcsEnumerateComputeSystems") + procHcsCreateComputeSystem = modvmcompute.NewProc("HcsCreateComputeSystem") + procHcsOpenComputeSystem = modvmcompute.NewProc("HcsOpenComputeSystem") + procHcsCloseComputeSystem = modvmcompute.NewProc("HcsCloseComputeSystem") + procHcsStartComputeSystem = modvmcompute.NewProc("HcsStartComputeSystem") + procHcsShutdownComputeSystem = modvmcompute.NewProc("HcsShutdownComputeSystem") + procHcsTerminateComputeSystem = modvmcompute.NewProc("HcsTerminateComputeSystem") + procHcsPauseComputeSystem = modvmcompute.NewProc("HcsPauseComputeSystem") + procHcsResumeComputeSystem = modvmcompute.NewProc("HcsResumeComputeSystem") + procHcsGetComputeSystemProperties = modvmcompute.NewProc("HcsGetComputeSystemProperties") + procHcsModifyComputeSystem = modvmcompute.NewProc("HcsModifyComputeSystem") + procHcsRegisterComputeSystemCallback = modvmcompute.NewProc("HcsRegisterComputeSystemCallback") + procHcsUnregisterComputeSystemCallback = modvmcompute.NewProc("HcsUnregisterComputeSystemCallback") + procHcsCreateProcess = modvmcompute.NewProc("HcsCreateProcess") + procHcsOpenProcess = modvmcompute.NewProc("HcsOpenProcess") + procHcsCloseProcess = modvmcompute.NewProc("HcsCloseProcess") + procHcsTerminateProcess = modvmcompute.NewProc("HcsTerminateProcess") + + procHcsGetProcessInfo = modvmcompute.NewProc("HcsGetProcessInfo") + procHcsGetProcessProperties = modvmcompute.NewProc("HcsGetProcessProperties") + procHcsModifyProcess = modvmcompute.NewProc("HcsModifyProcess") + procHcsGetServiceProperties = modvmcompute.NewProc("HcsGetServiceProperties") + procHcsRegisterProcessCallback = modvmcompute.NewProc("HcsRegisterProcessCallback") + procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback") +) + +func hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(query) + if hr != nil { + return + } + return _hcsEnumerateComputeSystems(_p0, computeSystems, result) +} + +func _hcsEnumerateComputeSystems(query *uint16, computeSystems **uint16, result **uint16) (hr error) { + if hr = procHcsEnumerateComputeSystems.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsEnumerateComputeSystems.Addr(), 3, uintptr(unsafe.Pointer(query)), uintptr(unsafe.Pointer(computeSystems)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(configuration) + if hr != nil { + return + } + return _hcsCreateComputeSystem(_p0, _p1, identity, computeSystem, result) +} + +func _hcsCreateComputeSystem(id *uint16, configuration *uint16, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) { + if hr = procHcsCreateComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsCreateComputeSystem.Addr(), 5, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(configuration)), uintptr(identity), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _hcsOpenComputeSystem(_p0, computeSystem, result) +} + +func _hcsOpenComputeSystem(id *uint16, computeSystem *hcsSystem, result **uint16) (hr error) { + if hr = procHcsOpenComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsOpenComputeSystem.Addr(), 3, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) { + if hr = procHcsCloseComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsCloseComputeSystem.Addr(), 1, uintptr(computeSystem), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsStartComputeSystem(computeSystem, _p0, result) +} + +func _hcsStartComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsStartComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsStartComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsShutdownComputeSystem(computeSystem, _p0, result) +} + +func _hcsShutdownComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsShutdownComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsShutdownComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsTerminateComputeSystem(computeSystem, _p0, result) +} + +func _hcsTerminateComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsTerminateComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsTerminateComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsPauseComputeSystem(computeSystem, _p0, result) +} + +func _hcsPauseComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsPauseComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsPauseComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsResumeComputeSystem(computeSystem, _p0, result) +} + +func _hcsResumeComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsResumeComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsResumeComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(propertyQuery) + if hr != nil { + return + } + return _hcsGetComputeSystemProperties(computeSystem, _p0, properties, result) +} + +func _hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { + if hr = procHcsGetComputeSystemProperties.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsGetComputeSystemProperties.Addr(), 4, uintptr(computeSystem), uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(configuration) + if hr != nil { + return + } + return _hcsModifyComputeSystem(computeSystem, _p0, result) +} + +func _hcsModifyComputeSystem(computeSystem hcsSystem, configuration *uint16, result **uint16) (hr error) { + if hr = procHcsModifyComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsModifyComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(configuration)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) { + if hr = procHcsRegisterComputeSystemCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsRegisterComputeSystemCallback.Addr(), 4, uintptr(computeSystem), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) { + if hr = procHcsUnregisterComputeSystemCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsUnregisterComputeSystemCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(processParameters) + if hr != nil { + return + } + return _hcsCreateProcess(computeSystem, _p0, processInformation, process, result) +} + +func _hcsCreateProcess(computeSystem hcsSystem, processParameters *uint16, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) { + if hr = procHcsCreateProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsCreateProcess.Addr(), 5, uintptr(computeSystem), uintptr(unsafe.Pointer(processParameters)), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) { + if hr = procHcsOpenProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsOpenProcess.Addr(), 4, uintptr(computeSystem), uintptr(pid), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCloseProcess(process hcsProcess) (hr error) { + if hr = procHcsCloseProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsCloseProcess.Addr(), 1, uintptr(process), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) { + if hr = procHcsTerminateProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 2, uintptr(process), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsSignalProcess(process hcsProcess, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsSignalProcess(process, _p0, result) +} + +func _hcsSignalProcess(process hcsProcess, options *uint16, result **uint16) (hr error) { + if hr = procHcsTerminateProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) { + if hr = procHcsGetProcessInfo.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetProcessInfo.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) { + if hr = procHcsGetProcessProperties.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetProcessProperties.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processProperties)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(settings) + if hr != nil { + return + } + return _hcsModifyProcess(process, _p0, result) +} + +func _hcsModifyProcess(process hcsProcess, settings *uint16, result **uint16) (hr error) { + if hr = procHcsModifyProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsModifyProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(propertyQuery) + if hr != nil { + return + } + return _hcsGetServiceProperties(_p0, properties, result) +} + +func _hcsGetServiceProperties(propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { + if hr = procHcsGetServiceProperties.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetServiceProperties.Addr(), 3, uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) { + if hr = procHcsRegisterProcessCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsRegisterProcessCallback.Addr(), 4, uintptr(process), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) { + if hr = procHcsUnregisterProcessCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsUnregisterProcessCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go new file mode 100644 index 000000000..921c2c855 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go @@ -0,0 +1,47 @@ +package hcserror + +import ( + "fmt" + "syscall" +) + +const ERROR_GEN_FAILURE = syscall.Errno(31) + +type HcsError struct { + title string + rest string + Err error +} + +func (e *HcsError) Error() string { + s := e.title + if len(s) > 0 && s[len(s)-1] != ' ' { + s += " " + } + s += fmt.Sprintf("failed in Win32: %s (0x%x)", e.Err, Win32FromError(e.Err)) + if e.rest != "" { + if e.rest[0] != ' ' { + s += " " + } + s += e.rest + } + return s +} + +func New(err error, title, rest string) error { + // Pass through DLL errors directly since they do not originate from HCS. + if _, ok := err.(*syscall.DLLError); ok { + return err + } + return &HcsError{title, rest, err} +} + +func Win32FromError(err error) uint32 { + if herr, ok := err.(*HcsError); ok { + return Win32FromError(herr.Err) + } + if code, ok := err.(syscall.Errno); ok { + return uint32(code) + } + return uint32(ERROR_GEN_FAILURE) +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go new file mode 100644 index 000000000..b2e475f53 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go @@ -0,0 +1,23 @@ +package hns + +import "fmt" + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go hns.go + +//sys _hnsCall(method string, path string, object string, response **uint16) (hr error) = vmcompute.HNSCall? + +type EndpointNotFoundError struct { + EndpointName string +} + +func (e EndpointNotFoundError) Error() string { + return fmt.Sprintf("Endpoint %s not found", e.EndpointName) +} + +type NetworkNotFoundError struct { + NetworkName string +} + +func (e NetworkNotFoundError) Error() string { + return fmt.Sprintf("Network %s not found", e.NetworkName) +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go new file mode 100644 index 000000000..59ec7004c --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go @@ -0,0 +1,262 @@ +package hns + +import ( + "encoding/json" + "net" + + "github.com/sirupsen/logrus" +) + +// HNSEndpoint represents a network endpoint in HNS +type HNSEndpoint struct { + Id string `json:"ID,omitempty"` + Name string `json:",omitempty"` + VirtualNetwork string `json:",omitempty"` + VirtualNetworkName string `json:",omitempty"` + Policies []json.RawMessage `json:",omitempty"` + MacAddress string `json:",omitempty"` + IPAddress net.IP `json:",omitempty"` + DNSSuffix string `json:",omitempty"` + DNSServerList string `json:",omitempty"` + GatewayAddress string `json:",omitempty"` + EnableInternalDNS bool `json:",omitempty"` + DisableICC bool `json:",omitempty"` + PrefixLength uint8 `json:",omitempty"` + IsRemoteEndpoint bool `json:",omitempty"` + EnableLowMetric bool `json:",omitempty"` + Namespace *Namespace `json:",omitempty"` + EncapOverhead uint16 `json:",omitempty"` +} + +//SystemType represents the type of the system on which actions are done +type SystemType string + +// SystemType const +const ( + ContainerType SystemType = "Container" + VirtualMachineType SystemType = "VirtualMachine" + HostType SystemType = "Host" +) + +// EndpointAttachDetachRequest is the structure used to send request to the container to modify the system +// Supported resource types are Network and Request Types are Add/Remove +type EndpointAttachDetachRequest struct { + ContainerID string `json:"ContainerId,omitempty"` + SystemType SystemType `json:"SystemType"` + CompartmentID uint16 `json:"CompartmentId,omitempty"` + VirtualNICName string `json:"VirtualNicName,omitempty"` +} + +// EndpointResquestResponse is object to get the endpoint request response +type EndpointResquestResponse struct { + Success bool + Error string +} + +// HNSEndpointRequest makes a HNS call to modify/query a network endpoint +func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) { + endpoint := &HNSEndpoint{} + err := hnsCall(method, "/endpoints/"+path, request, &endpoint) + if err != nil { + return nil, err + } + + return endpoint, nil +} + +// HNSListEndpointRequest makes a HNS call to query the list of available endpoints +func HNSListEndpointRequest() ([]HNSEndpoint, error) { + var endpoint []HNSEndpoint + err := hnsCall("GET", "/endpoints/", "", &endpoint) + if err != nil { + return nil, err + } + + return endpoint, nil +} + +// GetHNSEndpointByID get the Endpoint by ID +func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) { + return HNSEndpointRequest("GET", endpointID, "") +} + +// GetHNSEndpointByName gets the endpoint filtered by Name +func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { + hnsResponse, err := HNSListEndpointRequest() + if err != nil { + return nil, err + } + for _, hnsEndpoint := range hnsResponse { + if hnsEndpoint.Name == endpointName { + return &hnsEndpoint, nil + } + } + return nil, EndpointNotFoundError{EndpointName: endpointName} +} + +// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods +func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) { + operation := "Create" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + jsonString, err := json.Marshal(endpoint) + if err != nil { + return nil, err + } + return HNSEndpointRequest("POST", "", string(jsonString)) +} + +// Delete Endpoint by sending EndpointRequest to HNS +func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) { + operation := "Delete" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + return HNSEndpointRequest("DELETE", endpoint.Id, "") +} + +// Update Endpoint +func (endpoint *HNSEndpoint) Update() (*HNSEndpoint, error) { + operation := "Update" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + jsonString, err := json.Marshal(endpoint) + if err != nil { + return nil, err + } + err = hnsCall("POST", "/endpoints/"+endpoint.Id, string(jsonString), &endpoint) + + return endpoint, err +} + +// ApplyACLPolicy applies a set of ACL Policies on the Endpoint +func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error { + operation := "ApplyACLPolicy" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + for _, policy := range policies { + if policy == nil { + continue + } + jsonString, err := json.Marshal(policy) + if err != nil { + return err + } + endpoint.Policies = append(endpoint.Policies, jsonString) + } + + _, err := endpoint.Update() + return err +} + +// ContainerAttach attaches an endpoint to container +func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error { + operation := "ContainerAttach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + requestMessage := &EndpointAttachDetachRequest{ + ContainerID: containerID, + CompartmentID: compartmentID, + SystemType: ContainerType, + } + response := &EndpointResquestResponse{} + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) +} + +// ContainerDetach detaches an endpoint from container +func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error { + operation := "ContainerDetach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + requestMessage := &EndpointAttachDetachRequest{ + ContainerID: containerID, + SystemType: ContainerType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) +} + +// HostAttach attaches a nic on the host +func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error { + operation := "HostAttach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + requestMessage := &EndpointAttachDetachRequest{ + CompartmentID: compartmentID, + SystemType: HostType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) + +} + +// HostDetach detaches a nic on the host +func (endpoint *HNSEndpoint) HostDetach() error { + operation := "HostDetach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + requestMessage := &EndpointAttachDetachRequest{ + SystemType: HostType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) +} + +// VirtualMachineNICAttach attaches a endpoint to a virtual machine +func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error { + operation := "VirtualMachineNicAttach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + requestMessage := &EndpointAttachDetachRequest{ + VirtualNICName: virtualMachineNICName, + SystemType: VirtualMachineType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) +} + +// VirtualMachineNICDetach detaches a endpoint from a virtual machine +func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error { + operation := "VirtualMachineNicDetach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + requestMessage := &EndpointAttachDetachRequest{ + SystemType: VirtualMachineType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go new file mode 100644 index 000000000..969d1b263 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go @@ -0,0 +1,42 @@ +package hns + +import ( + "encoding/json" + "fmt" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/sirupsen/logrus" +) + +func hnsCall(method, path, request string, returnResponse interface{}) error { + var responseBuffer *uint16 + logrus.Debugf("[%s]=>[%s] Request : %s", method, path, request) + + err := _hnsCall(method, path, request, &responseBuffer) + if err != nil { + return hcserror.New(err, "hnsCall ", "") + } + response := interop.ConvertAndFreeCoTaskMemString(responseBuffer) + + hnsresponse := &hnsResponse{} + if err = json.Unmarshal([]byte(response), &hnsresponse); err != nil { + return err + } + + if !hnsresponse.Success { + return fmt.Errorf("HNS failed with error : %s", hnsresponse.Error) + } + + if len(hnsresponse.Output) == 0 { + return nil + } + + logrus.Debugf("Network Response : %s", hnsresponse.Output) + err = json.Unmarshal(hnsresponse.Output, returnResponse) + if err != nil { + return err + } + + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go new file mode 100644 index 000000000..a8d8cc56a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go @@ -0,0 +1,28 @@ +package hns + +type HNSGlobals struct { + Version HNSVersion `json:"Version"` +} + +type HNSVersion struct { + Major int `json:"Major"` + Minor int `json:"Minor"` +} + +var ( + HNSVersion1803 = HNSVersion{Major: 7, Minor: 2} +) + +func GetHNSGlobals() (*HNSGlobals, error) { + var version HNSVersion + err := hnsCall("GET", "/globals/version", "", &version) + if err != nil { + return nil, err + } + + globals := &HNSGlobals{ + Version: version, + } + + return globals, nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go new file mode 100644 index 000000000..7e859de91 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go @@ -0,0 +1,141 @@ +package hns + +import ( + "encoding/json" + "net" + + "github.com/sirupsen/logrus" +) + +// Subnet is assoicated with a network and represents a list +// of subnets available to the network +type Subnet struct { + AddressPrefix string `json:",omitempty"` + GatewayAddress string `json:",omitempty"` + Policies []json.RawMessage `json:",omitempty"` +} + +// MacPool is assoicated with a network and represents a list +// of macaddresses available to the network +type MacPool struct { + StartMacAddress string `json:",omitempty"` + EndMacAddress string `json:",omitempty"` +} + +// HNSNetwork represents a network in HNS +type HNSNetwork struct { + Id string `json:"ID,omitempty"` + Name string `json:",omitempty"` + Type string `json:",omitempty"` + NetworkAdapterName string `json:",omitempty"` + SourceMac string `json:",omitempty"` + Policies []json.RawMessage `json:",omitempty"` + MacPools []MacPool `json:",omitempty"` + Subnets []Subnet `json:",omitempty"` + DNSSuffix string `json:",omitempty"` + DNSServerList string `json:",omitempty"` + DNSServerCompartment uint32 `json:",omitempty"` + ManagementIP string `json:",omitempty"` + AutomaticDNS bool `json:",omitempty"` +} + +type hnsNetworkResponse struct { + Success bool + Error string + Output HNSNetwork +} + +type hnsResponse struct { + Success bool + Error string + Output json.RawMessage +} + +// HNSNetworkRequest makes a call into HNS to update/query a single network +func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) { + var network HNSNetwork + err := hnsCall(method, "/networks/"+path, request, &network) + if err != nil { + return nil, err + } + + return &network, nil +} + +// HNSListNetworkRequest makes a HNS call to query the list of available networks +func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) { + var network []HNSNetwork + err := hnsCall(method, "/networks/"+path, request, &network) + if err != nil { + return nil, err + } + + return network, nil +} + +// GetHNSNetworkByID +func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) { + return HNSNetworkRequest("GET", networkID, "") +} + +// GetHNSNetworkName filtered by Name +func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) { + hsnnetworks, err := HNSListNetworkRequest("GET", "", "") + if err != nil { + return nil, err + } + for _, hnsnetwork := range hsnnetworks { + if hnsnetwork.Name == networkName { + return &hnsnetwork, nil + } + } + return nil, NetworkNotFoundError{NetworkName: networkName} +} + +// Create Network by sending NetworkRequest to HNS. +func (network *HNSNetwork) Create() (*HNSNetwork, error) { + operation := "Create" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s", network.Id) + + jsonString, err := json.Marshal(network) + if err != nil { + return nil, err + } + return HNSNetworkRequest("POST", "", string(jsonString)) +} + +// Delete Network by sending NetworkRequest to HNS +func (network *HNSNetwork) Delete() (*HNSNetwork, error) { + operation := "Delete" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s", network.Id) + + return HNSNetworkRequest("DELETE", network.Id, "") +} + +// Creates an endpoint on the Network. +func (network *HNSNetwork) NewEndpoint(ipAddress net.IP, macAddress net.HardwareAddr) *HNSEndpoint { + return &HNSEndpoint{ + VirtualNetwork: network.Id, + IPAddress: ipAddress, + MacAddress: string(macAddress), + } +} + +func (network *HNSNetwork) CreateEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) { + operation := "CreateEndpoint" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s, endpointId=%s", network.Id, endpoint.Id) + + endpoint.VirtualNetwork = network.Id + return endpoint.Create() +} + +func (network *HNSNetwork) CreateRemoteEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) { + operation := "CreateRemoteEndpoint" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s", network.Id) + endpoint.IsRemoteEndpoint = true + return network.CreateEndpoint(endpoint) +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go new file mode 100644 index 000000000..2318a4fce --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go @@ -0,0 +1,98 @@ +package hns + +// Type of Request Support in ModifySystem +type PolicyType string + +// RequestType const +const ( + Nat PolicyType = "NAT" + ACL PolicyType = "ACL" + PA PolicyType = "PA" + VLAN PolicyType = "VLAN" + VSID PolicyType = "VSID" + VNet PolicyType = "VNET" + L2Driver PolicyType = "L2Driver" + Isolation PolicyType = "Isolation" + QOS PolicyType = "QOS" + OutboundNat PolicyType = "OutBoundNAT" + ExternalLoadBalancer PolicyType = "ELB" + Route PolicyType = "ROUTE" +) + +type NatPolicy struct { + Type PolicyType `json:"Type"` + Protocol string + InternalPort uint16 + ExternalPort uint16 +} + +type QosPolicy struct { + Type PolicyType `json:"Type"` + MaximumOutgoingBandwidthInBytes uint64 +} + +type IsolationPolicy struct { + Type PolicyType `json:"Type"` + VLAN uint + VSID uint + InDefaultIsolation bool +} + +type VlanPolicy struct { + Type PolicyType `json:"Type"` + VLAN uint +} + +type VsidPolicy struct { + Type PolicyType `json:"Type"` + VSID uint +} + +type PaPolicy struct { + Type PolicyType `json:"Type"` + PA string `json:"PA"` +} + +type OutboundNatPolicy struct { + Policy + VIP string `json:"VIP,omitempty"` + Exceptions []string `json:"ExceptionList,omitempty"` +} + +type ActionType string +type DirectionType string +type RuleType string + +const ( + Allow ActionType = "Allow" + Block ActionType = "Block" + + In DirectionType = "In" + Out DirectionType = "Out" + + Host RuleType = "Host" + Switch RuleType = "Switch" +) + +type ACLPolicy struct { + Type PolicyType `json:"Type"` + Id string `json:"Id,omitempty"` + Protocol uint16 + Protocols string `json:"Protocols,omitempty"` + InternalPort uint16 + Action ActionType + Direction DirectionType + LocalAddresses string + RemoteAddresses string + LocalPorts string `json:"LocalPorts,omitempty"` + LocalPort uint16 + RemotePorts string `json:"RemotePorts,omitempty"` + RemotePort uint16 + RuleType RuleType `json:"RuleType,omitempty"` + Priority uint16 + ServiceName string +} + +type Policy struct { + Type PolicyType `json:"Type"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go new file mode 100644 index 000000000..31322a681 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go @@ -0,0 +1,201 @@ +package hns + +import ( + "encoding/json" + + "github.com/sirupsen/logrus" +) + +// RoutePolicy is a structure defining schema for Route based Policy +type RoutePolicy struct { + Policy + DestinationPrefix string `json:"DestinationPrefix,omitempty"` + NextHop string `json:"NextHop,omitempty"` + EncapEnabled bool `json:"NeedEncap,omitempty"` +} + +// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy +type ELBPolicy struct { + LBPolicy + SourceVIP string `json:"SourceVIP,omitempty"` + VIPs []string `json:"VIPs,omitempty"` + ILB bool `json:"ILB,omitempty"` + DSR bool `json:"IsDSR,omitempty"` +} + +// LBPolicy is a structure defining schema for LoadBalancing based Policy +type LBPolicy struct { + Policy + Protocol uint16 `json:"Protocol,omitempty"` + InternalPort uint16 + ExternalPort uint16 +} + +// PolicyList is a structure defining schema for Policy list request +type PolicyList struct { + ID string `json:"ID,omitempty"` + EndpointReferences []string `json:"References,omitempty"` + Policies []json.RawMessage `json:"Policies,omitempty"` +} + +// HNSPolicyListRequest makes a call into HNS to update/query a single network +func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) { + var policy PolicyList + err := hnsCall(method, "/policylists/"+path, request, &policy) + if err != nil { + return nil, err + } + + return &policy, nil +} + +// HNSListPolicyListRequest gets all the policy list +func HNSListPolicyListRequest() ([]PolicyList, error) { + var plist []PolicyList + err := hnsCall("GET", "/policylists/", "", &plist) + if err != nil { + return nil, err + } + + return plist, nil +} + +// PolicyListRequest makes a HNS call to modify/query a network policy list +func PolicyListRequest(method, path, request string) (*PolicyList, error) { + policylist := &PolicyList{} + err := hnsCall(method, "/policylists/"+path, request, &policylist) + if err != nil { + return nil, err + } + + return policylist, nil +} + +// GetPolicyListByID get the policy list by ID +func GetPolicyListByID(policyListID string) (*PolicyList, error) { + return PolicyListRequest("GET", policyListID, "") +} + +// Create PolicyList by sending PolicyListRequest to HNS. +func (policylist *PolicyList) Create() (*PolicyList, error) { + operation := "Create" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s", policylist.ID) + jsonString, err := json.Marshal(policylist) + if err != nil { + return nil, err + } + return PolicyListRequest("POST", "", string(jsonString)) +} + +// Delete deletes PolicyList +func (policylist *PolicyList) Delete() (*PolicyList, error) { + operation := "Delete" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s", policylist.ID) + + return PolicyListRequest("DELETE", policylist.ID, "") +} + +// AddEndpoint add an endpoint to a Policy List +func (policylist *PolicyList) AddEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) { + operation := "AddEndpoint" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id) + + _, err := policylist.Delete() + if err != nil { + return nil, err + } + + // Add Endpoint to the Existing List + policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) + + return policylist.Create() +} + +// RemoveEndpoint removes an endpoint from the Policy List +func (policylist *PolicyList) RemoveEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) { + operation := "RemoveEndpoint" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id) + + _, err := policylist.Delete() + if err != nil { + return nil, err + } + + elementToRemove := "/endpoints/" + endpoint.Id + + var references []string + + for _, endpointReference := range policylist.EndpointReferences { + if endpointReference == elementToRemove { + continue + } + references = append(references, endpointReference) + } + policylist.EndpointReferences = references + return policylist.Create() +} + +// AddLoadBalancer policy list for the specified endpoints +func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { + operation := "AddLoadBalancer" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" endpointId=%v, isILB=%v, sourceVIP=%s, vip=%s, protocol=%v, internalPort=%v, externalPort=%v", endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort) + + policylist := &PolicyList{} + + elbPolicy := &ELBPolicy{ + SourceVIP: sourceVIP, + ILB: isILB, + } + + if len(vip) > 0 { + elbPolicy.VIPs = []string{vip} + } + elbPolicy.Type = ExternalLoadBalancer + elbPolicy.Protocol = protocol + elbPolicy.InternalPort = internalPort + elbPolicy.ExternalPort = externalPort + + for _, endpoint := range endpoints { + policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) + } + + jsonString, err := json.Marshal(elbPolicy) + if err != nil { + return nil, err + } + policylist.Policies = append(policylist.Policies, jsonString) + return policylist.Create() +} + +// AddRoute adds route policy list for the specified endpoints +func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) { + operation := "AddRoute" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" destinationPrefix:%s", destinationPrefix) + + policylist := &PolicyList{} + + rPolicy := &RoutePolicy{ + DestinationPrefix: destinationPrefix, + NextHop: nextHop, + EncapEnabled: encapEnabled, + } + rPolicy.Type = Route + + for _, endpoint := range endpoints { + policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) + } + + jsonString, err := json.Marshal(rPolicy) + if err != nil { + return nil, err + } + + policylist.Policies = append(policylist.Policies, jsonString) + return policylist.Create() +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go new file mode 100644 index 000000000..d5efba7f2 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go @@ -0,0 +1,49 @@ +package hns + +import ( + "github.com/sirupsen/logrus" +) + +type HNSSupportedFeatures struct { + Acl HNSAclFeatures `json:"ACL"` +} + +type HNSAclFeatures struct { + AclAddressLists bool `json:"AclAddressLists"` + AclNoHostRulePriority bool `json:"AclHostRulePriority"` + AclPortRanges bool `json:"AclPortRanges"` + AclRuleId bool `json:"AclRuleId"` +} + +func GetHNSSupportedFeatures() HNSSupportedFeatures { + var hnsFeatures HNSSupportedFeatures + + globals, err := GetHNSGlobals() + if err != nil { + // Expected on pre-1803 builds, all features will be false/unsupported + logrus.Debugf("Unable to obtain HNS globals: %s", err) + return hnsFeatures + } + + hnsFeatures.Acl = HNSAclFeatures{ + AclAddressLists: isHNSFeatureSupported(globals.Version, HNSVersion1803), + AclNoHostRulePriority: isHNSFeatureSupported(globals.Version, HNSVersion1803), + AclPortRanges: isHNSFeatureSupported(globals.Version, HNSVersion1803), + AclRuleId: isHNSFeatureSupported(globals.Version, HNSVersion1803), + } + + return hnsFeatures +} + +func isHNSFeatureSupported(currentVersion HNSVersion, minVersionSupported HNSVersion) bool { + if currentVersion.Major < minVersionSupported.Major { + return false + } + if currentVersion.Major > minVersionSupported.Major { + return true + } + if currentVersion.Minor < minVersionSupported.Minor { + return false + } + return true +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go new file mode 100644 index 000000000..45e2281b0 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go @@ -0,0 +1,110 @@ +package hns + +import ( + "encoding/json" + "fmt" + "os" + "path" + "strings" +) + +type namespaceRequest struct { + IsDefault bool `json:",omitempty"` +} + +type namespaceEndpointRequest struct { + ID string `json:"Id"` +} + +type NamespaceResource struct { + Type string + Data json.RawMessage +} + +type namespaceResourceRequest struct { + Type string + Data interface{} +} + +type Namespace struct { + ID string + IsDefault bool `json:",omitempty"` + ResourceList []NamespaceResource `json:",omitempty"` +} + +func issueNamespaceRequest(id *string, method, subpath string, request interface{}) (*Namespace, error) { + var err error + hnspath := "/namespaces/" + if id != nil { + hnspath = path.Join(hnspath, *id) + } + if subpath != "" { + hnspath = path.Join(hnspath, subpath) + } + var reqJSON []byte + if request != nil { + if reqJSON, err = json.Marshal(request); err != nil { + return nil, err + } + } + var ns Namespace + err = hnsCall(method, hnspath, string(reqJSON), &ns) + if err != nil { + if strings.Contains(err.Error(), "Element not found.") { + return nil, os.ErrNotExist + } + return nil, fmt.Errorf("%s %s: %s", method, hnspath, err) + } + return &ns, err +} + +func CreateNamespace() (string, error) { + req := namespaceRequest{} + ns, err := issueNamespaceRequest(nil, "POST", "", &req) + if err != nil { + return "", err + } + return ns.ID, nil +} + +func RemoveNamespace(id string) error { + _, err := issueNamespaceRequest(&id, "DELETE", "", nil) + return err +} + +func GetNamespaceEndpoints(id string) ([]string, error) { + ns, err := issueNamespaceRequest(&id, "GET", "", nil) + if err != nil { + return nil, err + } + var endpoints []string + for _, rsrc := range ns.ResourceList { + if rsrc.Type == "Endpoint" { + var endpoint namespaceEndpointRequest + err = json.Unmarshal(rsrc.Data, &endpoint) + if err != nil { + return nil, fmt.Errorf("unmarshal endpoint: %s", err) + } + endpoints = append(endpoints, endpoint.ID) + } + } + return endpoints, nil +} + +func AddNamespaceEndpoint(id string, endpointID string) error { + resource := namespaceResourceRequest{ + Type: "Endpoint", + Data: namespaceEndpointRequest{endpointID}, + } + _, err := issueNamespaceRequest(&id, "POST", "addresource", &resource) + return err +} + +func RemoveNamespaceEndpoint(id string, endpointID string) error { + resource := namespaceResourceRequest{ + Type: "Endpoint", + Data: namespaceEndpointRequest{endpointID}, + } + _, err := issueNamespaceRequest(&id, "POST", "removeresource", &resource) + return err +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go new file mode 100644 index 000000000..204633a48 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go @@ -0,0 +1,76 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package hns + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") + + procHNSCall = modvmcompute.NewProc("HNSCall") +) + +func _hnsCall(method string, path string, object string, response **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(method) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + var _p2 *uint16 + _p2, hr = syscall.UTF16PtrFromString(object) + if hr != nil { + return + } + return __hnsCall(_p0, _p1, _p2, response) +} + +func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16) (hr error) { + if hr = procHNSCall.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHNSCall.Addr(), 4, uintptr(unsafe.Pointer(method)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(object)), uintptr(unsafe.Pointer(response)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go new file mode 100644 index 000000000..2f6ec029e --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go @@ -0,0 +1,27 @@ +package interop + +import ( + "syscall" + "unsafe" +) + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go interop.go + +//sys coTaskMemFree(buffer unsafe.Pointer) = api_ms_win_core_com_l1_1_0.CoTaskMemFree + +func ConvertAndFreeCoTaskMemString(buffer *uint16) string { + str := syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(buffer))[:]) + coTaskMemFree(unsafe.Pointer(buffer)) + return str +} + +func ConvertAndFreeCoTaskMemBytes(buffer *uint16) []byte { + return []byte(ConvertAndFreeCoTaskMemString(buffer)) +} + +func Win32FromHresult(hr uintptr) syscall.Errno { + if hr&0x1fff0000 == 0x00070000 { + return syscall.Errno(hr & 0xffff) + } + return syscall.Errno(hr) +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go new file mode 100644 index 000000000..12b0c71c5 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go @@ -0,0 +1,48 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package interop + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modapi_ms_win_core_com_l1_1_0 = windows.NewLazySystemDLL("api-ms-win-core-com-l1-1-0.dll") + + procCoTaskMemFree = modapi_ms_win_core_com_l1_1_0.NewProc("CoTaskMemFree") +) + +func coTaskMemFree(buffer unsafe.Pointer) { + syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(buffer), 0, 0) + return +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go new file mode 100644 index 000000000..cf2c166d9 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go @@ -0,0 +1,32 @@ +package logfields + +const ( + // Identifiers + + ContainerID = "cid" + UVMID = "uvm-id" + ProcessID = "pid" + + // Common Misc + + // Timeout represents an operation timeout. + Timeout = "timeout" + JSON = "json" + + // Keys/values + + Field = "field" + OCIAnnotation = "oci-annotation" + Value = "value" + + // Golang type's + + ExpectedType = "expected-type" + Bool = "bool" + Uint32 = "uint32" + Uint64 = "uint64" + + // runhcs + + VMShimOperation = "vmshim-op" +) diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go new file mode 100644 index 000000000..e5b8b85e0 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go @@ -0,0 +1,24 @@ +package longpath + +import ( + "path/filepath" + "strings" +) + +// LongAbs makes a path absolute and returns it in NT long path form. +func LongAbs(path string) (string, error) { + if strings.HasPrefix(path, `\\?\`) || strings.HasPrefix(path, `\\.\`) { + return path, nil + } + if !filepath.IsAbs(path) { + absPath, err := filepath.Abs(path) + if err != nil { + return "", err + } + path = absPath + } + if strings.HasPrefix(path, `\\`) { + return `\\?\UNC\` + path[2:], nil + } + return `\\?\` + path, nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go new file mode 100644 index 000000000..7e95efb30 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go @@ -0,0 +1,52 @@ +package mergemaps + +import "encoding/json" + +// Merge recursively merges map `fromMap` into map `ToMap`. Any pre-existing values +// in ToMap are overwritten. Values in fromMap are added to ToMap. +// From http://stackoverflow.com/questions/40491438/merging-two-json-strings-in-golang +func Merge(fromMap, ToMap interface{}) interface{} { + switch fromMap := fromMap.(type) { + case map[string]interface{}: + ToMap, ok := ToMap.(map[string]interface{}) + if !ok { + return fromMap + } + for keyToMap, valueToMap := range ToMap { + if valueFromMap, ok := fromMap[keyToMap]; ok { + fromMap[keyToMap] = Merge(valueFromMap, valueToMap) + } else { + fromMap[keyToMap] = valueToMap + } + } + case nil: + // merge(nil, map[string]interface{...}) -> map[string]interface{...} + ToMap, ok := ToMap.(map[string]interface{}) + if ok { + return ToMap + } + } + return fromMap +} + +// MergeJSON merges the contents of a JSON string into an object representation, +// returning a new object suitable for translating to JSON. +func MergeJSON(object interface{}, additionalJSON []byte) (interface{}, error) { + if len(additionalJSON) == 0 { + return object, nil + } + objectJSON, err := json.Marshal(object) + if err != nil { + return nil, err + } + var objectMap, newMap map[string]interface{} + err = json.Unmarshal(objectJSON, &objectMap) + if err != nil { + return nil, err + } + err = json.Unmarshal(additionalJSON, &newMap) + if err != nil { + return nil, err + } + return Merge(newMap, objectMap), nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go new file mode 100644 index 000000000..f31edfaf8 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go @@ -0,0 +1,431 @@ +package safefile + +import ( + "errors" + "io" + "os" + "path/filepath" + "strings" + "syscall" + "unicode/utf16" + "unsafe" + + "github.com/Microsoft/hcsshim/internal/longpath" + + winio "github.com/Microsoft/go-winio" +) + +//go:generate go run $GOROOT\src\syscall\mksyscall_windows.go -output zsyscall_windows.go safeopen.go + +//sys ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) = ntdll.NtCreateFile +//sys ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) = ntdll.NtSetInformationFile +//sys rtlNtStatusToDosError(status uint32) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb +//sys localAlloc(flags uint32, size int) (ptr uintptr) = kernel32.LocalAlloc +//sys localFree(ptr uintptr) = kernel32.LocalFree + +type ioStatusBlock struct { + Status, Information uintptr +} + +type objectAttributes struct { + Length uintptr + RootDirectory uintptr + ObjectName uintptr + Attributes uintptr + SecurityDescriptor uintptr + SecurityQoS uintptr +} + +type unicodeString struct { + Length uint16 + MaximumLength uint16 + Buffer uintptr +} + +type fileLinkInformation struct { + ReplaceIfExists bool + RootDirectory uintptr + FileNameLength uint32 + FileName [1]uint16 +} + +type fileDispositionInformationEx struct { + Flags uintptr +} + +const ( + _FileLinkInformation = 11 + _FileDispositionInformationEx = 64 + + FILE_READ_ATTRIBUTES = 0x0080 + FILE_WRITE_ATTRIBUTES = 0x0100 + DELETE = 0x10000 + + FILE_OPEN = 1 + FILE_CREATE = 2 + + FILE_DIRECTORY_FILE = 0x00000001 + FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020 + FILE_DELETE_ON_CLOSE = 0x00001000 + FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000 + FILE_OPEN_REPARSE_POINT = 0x00200000 + + FILE_DISPOSITION_DELETE = 0x00000001 + + _OBJ_DONT_REPARSE = 0x1000 + + _STATUS_REPARSE_POINT_ENCOUNTERED = 0xC000050B +) + +func OpenRoot(path string) (*os.File, error) { + longpath, err := longpath.LongAbs(path) + if err != nil { + return nil, err + } + return winio.OpenForBackup(longpath, syscall.GENERIC_READ, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, syscall.OPEN_EXISTING) +} + +func ntRelativePath(path string) ([]uint16, error) { + path = filepath.Clean(path) + if strings.Contains(path, ":") { + // Since alternate data streams must follow the file they + // are attached to, finding one here (out of order) is invalid. + return nil, errors.New("path contains invalid character `:`") + } + fspath := filepath.FromSlash(path) + if len(fspath) > 0 && fspath[0] == '\\' { + return nil, errors.New("expected relative path") + } + + path16 := utf16.Encode(([]rune)(fspath)) + if len(path16) > 32767 { + return nil, syscall.ENAMETOOLONG + } + + return path16, nil +} + +// openRelativeInternal opens a relative path from the given root, failing if +// any of the intermediate path components are reparse points. +func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) { + var ( + h uintptr + iosb ioStatusBlock + oa objectAttributes + ) + + path16, err := ntRelativePath(path) + if err != nil { + return nil, err + } + + if root == nil || root.Fd() == 0 { + return nil, errors.New("missing root directory") + } + + upathBuffer := localAlloc(0, int(unsafe.Sizeof(unicodeString{}))+len(path16)*2) + defer localFree(upathBuffer) + + upath := (*unicodeString)(unsafe.Pointer(upathBuffer)) + upath.Length = uint16(len(path16) * 2) + upath.MaximumLength = upath.Length + upath.Buffer = upathBuffer + unsafe.Sizeof(*upath) + copy((*[32768]uint16)(unsafe.Pointer(upath.Buffer))[:], path16) + + oa.Length = unsafe.Sizeof(oa) + oa.ObjectName = upathBuffer + oa.RootDirectory = uintptr(root.Fd()) + oa.Attributes = _OBJ_DONT_REPARSE + status := ntCreateFile( + &h, + accessMask|syscall.SYNCHRONIZE, + &oa, + &iosb, + nil, + 0, + shareFlags, + createDisposition, + FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT|flags, + nil, + 0, + ) + if status != 0 { + return nil, rtlNtStatusToDosError(status) + } + + fullPath, err := longpath.LongAbs(filepath.Join(root.Name(), path)) + if err != nil { + syscall.Close(syscall.Handle(h)) + return nil, err + } + + return os.NewFile(h, fullPath), nil +} + +// OpenRelative opens a relative path from the given root, failing if +// any of the intermediate path components are reparse points. +func OpenRelative(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) { + f, err := openRelativeInternal(path, root, accessMask, shareFlags, createDisposition, flags) + if err != nil { + err = &os.PathError{Op: "open", Path: filepath.Join(root.Name(), path), Err: err} + } + return f, err +} + +// LinkRelative creates a hard link from oldname to newname (relative to oldroot +// and newroot), failing if any of the intermediate path components are reparse +// points. +func LinkRelative(oldname string, oldroot *os.File, newname string, newroot *os.File) error { + // Open the old file. + oldf, err := openRelativeInternal( + oldname, + oldroot, + syscall.FILE_WRITE_ATTRIBUTES, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + FILE_OPEN, + 0, + ) + if err != nil { + return &os.LinkError{Op: "link", Old: filepath.Join(oldroot.Name(), oldname), New: filepath.Join(newroot.Name(), newname), Err: err} + } + defer oldf.Close() + + // Open the parent of the new file. + var parent *os.File + parentPath := filepath.Dir(newname) + if parentPath != "." { + parent, err = openRelativeInternal( + parentPath, + newroot, + syscall.GENERIC_READ, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DIRECTORY_FILE) + if err != nil { + return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: err} + } + defer parent.Close() + + fi, err := winio.GetFileBasicInfo(parent) + if err != nil { + return err + } + if (fi.FileAttributes & syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 { + return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: rtlNtStatusToDosError(_STATUS_REPARSE_POINT_ENCOUNTERED)} + } + + } else { + parent = newroot + } + + // Issue an NT call to create the link. This will be safe because NT will + // not open any more directories to create the link, so it cannot walk any + // more reparse points. + newbase := filepath.Base(newname) + newbase16, err := ntRelativePath(newbase) + if err != nil { + return err + } + + size := int(unsafe.Offsetof(fileLinkInformation{}.FileName)) + len(newbase16)*2 + linkinfoBuffer := localAlloc(0, size) + defer localFree(linkinfoBuffer) + linkinfo := (*fileLinkInformation)(unsafe.Pointer(linkinfoBuffer)) + linkinfo.RootDirectory = parent.Fd() + linkinfo.FileNameLength = uint32(len(newbase16) * 2) + copy((*[32768]uint16)(unsafe.Pointer(&linkinfo.FileName[0]))[:], newbase16) + + var iosb ioStatusBlock + status := ntSetInformationFile( + oldf.Fd(), + &iosb, + linkinfoBuffer, + uint32(size), + _FileLinkInformation, + ) + if status != 0 { + return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(parent.Name(), newbase), Err: rtlNtStatusToDosError(status)} + } + + return nil +} + +// deleteOnClose marks a file to be deleted when the handle is closed. +func deleteOnClose(f *os.File) error { + disposition := fileDispositionInformationEx{Flags: FILE_DISPOSITION_DELETE} + var iosb ioStatusBlock + status := ntSetInformationFile( + f.Fd(), + &iosb, + uintptr(unsafe.Pointer(&disposition)), + uint32(unsafe.Sizeof(disposition)), + _FileDispositionInformationEx, + ) + if status != 0 { + return rtlNtStatusToDosError(status) + } + return nil +} + +// clearReadOnly clears the readonly attribute on a file. +func clearReadOnly(f *os.File) error { + bi, err := winio.GetFileBasicInfo(f) + if err != nil { + return err + } + if bi.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY == 0 { + return nil + } + sbi := winio.FileBasicInfo{ + FileAttributes: bi.FileAttributes &^ syscall.FILE_ATTRIBUTE_READONLY, + } + if sbi.FileAttributes == 0 { + sbi.FileAttributes = syscall.FILE_ATTRIBUTE_NORMAL + } + return winio.SetFileBasicInfo(f, &sbi) +} + +// RemoveRelative removes a file or directory relative to a root, failing if any +// intermediate path components are reparse points. +func RemoveRelative(path string, root *os.File) error { + f, err := openRelativeInternal( + path, + root, + FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES|DELETE, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + FILE_OPEN, + FILE_OPEN_REPARSE_POINT) + if err == nil { + defer f.Close() + err = deleteOnClose(f) + if err == syscall.ERROR_ACCESS_DENIED { + // Maybe the file is marked readonly. Clear the bit and retry. + clearReadOnly(f) + err = deleteOnClose(f) + } + } + if err != nil { + return &os.PathError{Op: "remove", Path: filepath.Join(root.Name(), path), Err: err} + } + return nil +} + +// RemoveAllRelative removes a directory tree relative to a root, failing if any +// intermediate path components are reparse points. +func RemoveAllRelative(path string, root *os.File) error { + fi, err := LstatRelative(path, root) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + fileAttributes := fi.Sys().(*syscall.Win32FileAttributeData).FileAttributes + if fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 || fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 { + // If this is a reparse point, it can't have children. Simple remove will do. + err := RemoveRelative(path, root) + if err == nil || os.IsNotExist(err) { + return nil + } + return err + } + + // It is necessary to use os.Open as Readdirnames does not work with + // OpenRelative. This is safe because the above lstatrelative fails + // if the target is outside the root, and we know this is not a + // symlink from the above FILE_ATTRIBUTE_REPARSE_POINT check. + fd, err := os.Open(filepath.Join(root.Name(), path)) + if err != nil { + if os.IsNotExist(err) { + // Race. It was deleted between the Lstat and Open. + // Return nil per RemoveAll's docs. + return nil + } + return err + } + + // Remove contents & return first error. + for { + names, err1 := fd.Readdirnames(100) + for _, name := range names { + err1 := RemoveAllRelative(path+string(os.PathSeparator)+name, root) + if err == nil { + err = err1 + } + } + if err1 == io.EOF { + break + } + // If Readdirnames returned an error, use it. + if err == nil { + err = err1 + } + if len(names) == 0 { + break + } + } + fd.Close() + + // Remove directory. + err1 := RemoveRelative(path, root) + if err1 == nil || os.IsNotExist(err1) { + return nil + } + if err == nil { + err = err1 + } + return err +} + +// MkdirRelative creates a directory relative to a root, failing if any +// intermediate path components are reparse points. +func MkdirRelative(path string, root *os.File) error { + f, err := openRelativeInternal( + path, + root, + 0, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + FILE_CREATE, + FILE_DIRECTORY_FILE) + if err == nil { + f.Close() + } else { + err = &os.PathError{Op: "mkdir", Path: filepath.Join(root.Name(), path), Err: err} + } + return err +} + +// LstatRelative performs a stat operation on a file relative to a root, failing +// if any intermediate path components are reparse points. +func LstatRelative(path string, root *os.File) (os.FileInfo, error) { + f, err := openRelativeInternal( + path, + root, + FILE_READ_ATTRIBUTES, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + FILE_OPEN, + FILE_OPEN_REPARSE_POINT) + if err != nil { + return nil, &os.PathError{Op: "stat", Path: filepath.Join(root.Name(), path), Err: err} + } + defer f.Close() + return f.Stat() +} + +// EnsureNotReparsePointRelative validates that a given file (relative to a +// root) and all intermediate path components are not a reparse points. +func EnsureNotReparsePointRelative(path string, root *os.File) error { + // Perform an open with OBJ_DONT_REPARSE but without specifying FILE_OPEN_REPARSE_POINT. + f, err := OpenRelative( + path, + root, + 0, + syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, + FILE_OPEN, + 0) + if err != nil { + return err + } + f.Close() + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/safefile/zsyscall_windows.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/safefile/zsyscall_windows.go new file mode 100644 index 000000000..709b9d347 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/safefile/zsyscall_windows.go @@ -0,0 +1,79 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package safefile + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modntdll = windows.NewLazySystemDLL("ntdll.dll") + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + + procNtCreateFile = modntdll.NewProc("NtCreateFile") + procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile") + procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") + procLocalAlloc = modkernel32.NewProc("LocalAlloc") + procLocalFree = modkernel32.NewProc("LocalFree") +) + +func ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) { + r0, _, _ := syscall.Syscall12(procNtCreateFile.Addr(), 11, uintptr(unsafe.Pointer(handle)), uintptr(accessMask), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(allocationSize)), uintptr(fileAttributes), uintptr(shareAccess), uintptr(createDisposition), uintptr(createOptions), uintptr(unsafe.Pointer(eaBuffer)), uintptr(eaLength), 0) + status = uint32(r0) + return +} + +func ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) { + r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(information), uintptr(length), uintptr(class), 0) + status = uint32(r0) + return +} + +func rtlNtStatusToDosError(status uint32) (winerr error) { + r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0) + if r0 != 0 { + winerr = syscall.Errno(r0) + } + return +} + +func localAlloc(flags uint32, size int) (ptr uintptr) { + r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(size), 0) + ptr = uintptr(r0) + return +} + +func localFree(ptr uintptr) { + syscall.Syscall(procLocalFree.Addr(), 1, uintptr(ptr), 0, 0) + return +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go new file mode 100644 index 000000000..995433ace --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go @@ -0,0 +1,245 @@ +package schema1 + +import ( + "encoding/json" + "time" + + "github.com/Microsoft/hcsshim/internal/schema2" +) + +// ProcessConfig is used as both the input of Container.CreateProcess +// and to convert the parameters to JSON for passing onto the HCS +type ProcessConfig struct { + ApplicationName string `json:",omitempty"` + CommandLine string `json:",omitempty"` + CommandArgs []string `json:",omitempty"` // Used by Linux Containers on Windows + User string `json:",omitempty"` + WorkingDirectory string `json:",omitempty"` + Environment map[string]string `json:",omitempty"` + EmulateConsole bool `json:",omitempty"` + CreateStdInPipe bool `json:",omitempty"` + CreateStdOutPipe bool `json:",omitempty"` + CreateStdErrPipe bool `json:",omitempty"` + ConsoleSize [2]uint `json:",omitempty"` + CreateInUtilityVm bool `json:",omitempty"` // Used by Linux Containers on Windows + OCISpecification *json.RawMessage `json:",omitempty"` // Used by Linux Containers on Windows +} + +type Layer struct { + ID string + Path string +} + +type MappedDir struct { + HostPath string + ContainerPath string + ReadOnly bool + BandwidthMaximum uint64 + IOPSMaximum uint64 + CreateInUtilityVM bool + // LinuxMetadata - Support added in 1803/RS4+. + LinuxMetadata bool `json:",omitempty"` +} + +type MappedPipe struct { + HostPath string + ContainerPipeName string +} + +type HvRuntime struct { + ImagePath string `json:",omitempty"` + SkipTemplate bool `json:",omitempty"` + LinuxInitrdFile string `json:",omitempty"` // File under ImagePath on host containing an initrd image for starting a Linux utility VM + LinuxKernelFile string `json:",omitempty"` // File under ImagePath on host containing a kernel for starting a Linux utility VM + LinuxBootParameters string `json:",omitempty"` // Additional boot parameters for starting a Linux Utility VM in initrd mode + BootSource string `json:",omitempty"` // "Vhd" for Linux Utility VM booting from VHD + WritableBootSource bool `json:",omitempty"` // Linux Utility VM booting from VHD +} + +type MappedVirtualDisk struct { + HostPath string `json:",omitempty"` // Path to VHD on the host + ContainerPath string // Platform-specific mount point path in the container + CreateInUtilityVM bool `json:",omitempty"` + ReadOnly bool `json:",omitempty"` + Cache string `json:",omitempty"` // "" (Unspecified); "Disabled"; "Enabled"; "Private"; "PrivateAllowSharing" + AttachOnly bool `json:",omitempty:` +} + +// AssignedDevice represents a device that has been directly assigned to a container +// +// NOTE: Support added in RS5 +type AssignedDevice struct { + // InterfaceClassGUID of the device to assign to container. + InterfaceClassGUID string `json:"InterfaceClassGuid,omitempty"` +} + +// ContainerConfig is used as both the input of CreateContainer +// and to convert the parameters to JSON for passing onto the HCS +type ContainerConfig struct { + SystemType string // HCS requires this to be hard-coded to "Container" + Name string // Name of the container. We use the docker ID. + Owner string `json:",omitempty"` // The management platform that created this container + VolumePath string `json:",omitempty"` // Windows volume path for scratch space. Used by Windows Server Containers only. Format \\?\\Volume{GUID} + IgnoreFlushesDuringBoot bool `json:",omitempty"` // Optimization hint for container startup in Windows + LayerFolderPath string `json:",omitempty"` // Where the layer folders are located. Used by Windows Server Containers only. Format %root%\windowsfilter\containerID + Layers []Layer // List of storage layers. Required for Windows Server and Hyper-V Containers. Format ID=GUID;Path=%root%\windowsfilter\layerID + Credentials string `json:",omitempty"` // Credentials information + ProcessorCount uint32 `json:",omitempty"` // Number of processors to assign to the container. + ProcessorWeight uint64 `json:",omitempty"` // CPU shares (relative weight to other containers with cpu shares). Range is from 1 to 10000. A value of 0 results in default shares. + ProcessorMaximum int64 `json:",omitempty"` // Specifies the portion of processor cycles that this container can use as a percentage times 100. Range is from 1 to 10000. A value of 0 results in no limit. + StorageIOPSMaximum uint64 `json:",omitempty"` // Maximum Storage IOPS + StorageBandwidthMaximum uint64 `json:",omitempty"` // Maximum Storage Bandwidth in bytes per second + StorageSandboxSize uint64 `json:",omitempty"` // Size in bytes that the container system drive should be expanded to if smaller + MemoryMaximumInMB int64 `json:",omitempty"` // Maximum memory available to the container in Megabytes + HostName string `json:",omitempty"` // Hostname + MappedDirectories []MappedDir `json:",omitempty"` // List of mapped directories (volumes/mounts) + MappedPipes []MappedPipe `json:",omitempty"` // List of mapped Windows named pipes + HvPartition bool // True if it a Hyper-V Container + NetworkSharedContainerName string `json:",omitempty"` // Name (ID) of the container that we will share the network stack with. + EndpointList []string `json:",omitempty"` // List of networking endpoints to be attached to container + HvRuntime *HvRuntime `json:",omitempty"` // Hyper-V container settings. Used by Hyper-V containers only. Format ImagePath=%root%\BaseLayerID\UtilityVM + Servicing bool `json:",omitempty"` // True if this container is for servicing + AllowUnqualifiedDNSQuery bool `json:",omitempty"` // True to allow unqualified DNS name resolution + DNSSearchList string `json:",omitempty"` // Comma seperated list of DNS suffixes to use for name resolution + ContainerType string `json:",omitempty"` // "Linux" for Linux containers on Windows. Omitted otherwise. + TerminateOnLastHandleClosed bool `json:",omitempty"` // Should HCS terminate the container once all handles have been closed + MappedVirtualDisks []MappedVirtualDisk `json:",omitempty"` // Array of virtual disks to mount at start + AssignedDevices []AssignedDevice `json:",omitempty"` // Array of devices to assign. NOTE: Support added in RS5 +} + +type ComputeSystemQuery struct { + IDs []string `json:"Ids,omitempty"` + Types []string `json:",omitempty"` + Names []string `json:",omitempty"` + Owners []string `json:",omitempty"` +} + +type PropertyType string + +const ( + PropertyTypeStatistics PropertyType = "Statistics" // V1 and V2 + PropertyTypeProcessList = "ProcessList" // V1 and V2 + PropertyTypeMappedVirtualDisk = "MappedVirtualDisk" // Not supported in V2 schema call + PropertyTypeGuestConnection = "GuestConnection" // V1 and V2. Nil return from HCS before RS5 +) + +type PropertyQuery struct { + PropertyTypes []PropertyType `json:",omitempty"` +} + +// ContainerProperties holds the properties for a container and the processes running in that container +type ContainerProperties struct { + ID string `json:"Id"` + State string + Name string + SystemType string + Owner string + SiloGUID string `json:"SiloGuid,omitempty"` + RuntimeID string `json:"RuntimeId,omitempty"` + IsRuntimeTemplate bool `json:",omitempty"` + RuntimeImagePath string `json:",omitempty"` + Stopped bool `json:",omitempty"` + ExitType string `json:",omitempty"` + AreUpdatesPending bool `json:",omitempty"` + ObRoot string `json:",omitempty"` + Statistics Statistics `json:",omitempty"` + ProcessList []ProcessListItem `json:",omitempty"` + MappedVirtualDiskControllers map[int]MappedVirtualDiskController `json:",omitempty"` + GuestConnectionInfo GuestConnectionInfo `json:",omitempty"` +} + +// MemoryStats holds the memory statistics for a container +type MemoryStats struct { + UsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"` + UsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"` + UsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"` +} + +// ProcessorStats holds the processor statistics for a container +type ProcessorStats struct { + TotalRuntime100ns uint64 `json:",omitempty"` + RuntimeUser100ns uint64 `json:",omitempty"` + RuntimeKernel100ns uint64 `json:",omitempty"` +} + +// StorageStats holds the storage statistics for a container +type StorageStats struct { + ReadCountNormalized uint64 `json:",omitempty"` + ReadSizeBytes uint64 `json:",omitempty"` + WriteCountNormalized uint64 `json:",omitempty"` + WriteSizeBytes uint64 `json:",omitempty"` +} + +// NetworkStats holds the network statistics for a container +type NetworkStats struct { + BytesReceived uint64 `json:",omitempty"` + BytesSent uint64 `json:",omitempty"` + PacketsReceived uint64 `json:",omitempty"` + PacketsSent uint64 `json:",omitempty"` + DroppedPacketsIncoming uint64 `json:",omitempty"` + DroppedPacketsOutgoing uint64 `json:",omitempty"` + EndpointId string `json:",omitempty"` + InstanceId string `json:",omitempty"` +} + +// Statistics is the structure returned by a statistics call on a container +type Statistics struct { + Timestamp time.Time `json:",omitempty"` + ContainerStartTime time.Time `json:",omitempty"` + Uptime100ns uint64 `json:",omitempty"` + Memory MemoryStats `json:",omitempty"` + Processor ProcessorStats `json:",omitempty"` + Storage StorageStats `json:",omitempty"` + Network []NetworkStats `json:",omitempty"` +} + +// ProcessList is the structure of an item returned by a ProcessList call on a container +type ProcessListItem struct { + CreateTimestamp time.Time `json:",omitempty"` + ImageName string `json:",omitempty"` + KernelTime100ns uint64 `json:",omitempty"` + MemoryCommitBytes uint64 `json:",omitempty"` + MemoryWorkingSetPrivateBytes uint64 `json:",omitempty"` + MemoryWorkingSetSharedBytes uint64 `json:",omitempty"` + ProcessId uint32 `json:",omitempty"` + UserTime100ns uint64 `json:",omitempty"` +} + +// MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container +type MappedVirtualDiskController struct { + MappedVirtualDisks map[int]MappedVirtualDisk `json:",omitempty"` +} + +// GuestDefinedCapabilities is part of the GuestConnectionInfo returned by a GuestConnection call on a utility VM +type GuestDefinedCapabilities struct { + NamespaceAddRequestSupported bool `json:",omitempty"` + SignalProcessSupported bool `json:",omitempty"` +} + +// GuestConnectionInfo is the structure of an iterm return by a GuestConnection call on a utility VM +type GuestConnectionInfo struct { + SupportedSchemaVersions []hcsschema.Version `json:",omitempty"` + ProtocolVersion uint32 `json:",omitempty"` + GuestDefinedCapabilities GuestDefinedCapabilities `json:",omitempty"` +} + +// Type of Request Support in ModifySystem +type RequestType string + +// Type of Resource Support in ModifySystem +type ResourceType string + +// RequestType const +const ( + Add RequestType = "Add" + Remove RequestType = "Remove" + Network ResourceType = "Network" +) + +// ResourceModificationRequestResponse is the structure used to send request to the container to modify the system +// Supported resource types are Network and Request Types are Add/Remove +type ResourceModificationRequestResponse struct { + Resource ResourceType `json:"ResourceType"` + Data interface{} `json:"Settings"` + Request RequestType `json:"RequestType,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/attachment.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/attachment.go new file mode 100644 index 000000000..09456cbc2 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/attachment.go @@ -0,0 +1,31 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Attachment struct { + + Type_ string `json:"Type,omitempty"` + + Path string `json:"Path,omitempty"` + + IgnoreFlushes bool `json:"IgnoreFlushes,omitempty"` + + CachingMode string `json:"CachingMode,omitempty"` + + NoWriteHardening bool `json:"NoWriteHardening,omitempty"` + + DisableExpansionOptimization bool `json:"DisableExpansionOptimization,omitempty"` + + IgnoreRelativeLocator bool `json:"IgnoreRelativeLocator,omitempty"` + + CaptureIoAttributionContext bool `json:"CaptureIoAttributionContext,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/battery.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/battery.go new file mode 100644 index 000000000..ecbbed4c2 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/battery.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Battery struct { +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/cache_query_stats_response.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/cache_query_stats_response.go new file mode 100644 index 000000000..243779eab --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/cache_query_stats_response.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CacheQueryStatsResponse struct { + + L3OccupancyBytes int32 `json:"L3OccupancyBytes,omitempty"` + + L3TotalBwBytes int32 `json:"L3TotalBwBytes,omitempty"` + + L3LocalBwBytes int32 `json:"L3LocalBwBytes,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/chipset.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/chipset.go new file mode 100644 index 000000000..ca75277a3 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/chipset.go @@ -0,0 +1,27 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Chipset struct { + Uefi *Uefi `json:"Uefi,omitempty"` + + IsNumLockDisabled bool `json:"IsNumLockDisabled,omitempty"` + + BaseBoardSerialNumber string `json:"BaseBoardSerialNumber,omitempty"` + + ChassisSerialNumber string `json:"ChassisSerialNumber,omitempty"` + + ChassisAssetTag string `json:"ChassisAssetTag,omitempty"` + + UseUtc bool `json:"UseUtc,omitempty"` + + // LinuxKernelDirect - Added in v2.2 Builds >=181117 + LinuxKernelDirect *LinuxKernelDirect `json:"LinuxKernelDirect,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/close_handle.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/close_handle.go new file mode 100644 index 000000000..88f01707a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/close_handle.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CloseHandle struct { + + Handle string `json:"Handle,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/com_port.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/com_port.go new file mode 100644 index 000000000..c665be3d5 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/com_port.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// ComPort specifies the named pipe that will be used for the port, with empty string indicating a disconnected port. +type ComPort struct { + + NamedPipe string `json:"NamedPipe,omitempty"` + + OptimizeForDebugger bool `json:"OptimizeForDebugger,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/compute_system.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/compute_system.go new file mode 100644 index 000000000..85785d285 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/compute_system.go @@ -0,0 +1,27 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ComputeSystem struct { + + Owner string `json:"Owner,omitempty"` + + SchemaVersion *Version `json:"SchemaVersion,omitempty"` + + HostingSystemId string `json:"HostingSystemId,omitempty"` + + HostedSystem *HostedSystem `json:"HostedSystem,omitempty"` + + Container *Container `json:"Container,omitempty"` + + VirtualMachine *VirtualMachine `json:"VirtualMachine,omitempty"` + + ShouldTerminateOnLastHandleClosed bool `json:"ShouldTerminateOnLastHandleClosed,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/configuration.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/configuration.go new file mode 100644 index 000000000..1a47db7d9 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/configuration.go @@ -0,0 +1,72 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "net/http" +) + +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. + ContextOAuth2 = contextKey("token") + + // ContextBasicAuth takes BasicAuth as authentication for the request. + ContextBasicAuth = contextKey("basic") + + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + + // ContextAPIKey takes an APIKey as authentication for the request + ContextAPIKey = contextKey("apikey") +) + +// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} + +type Configuration struct { + BasePath string `json:"basePath,omitempty"` + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + HTTPClient *http.Client +} + +func NewConfiguration() *Configuration { + cfg := &Configuration{ + BasePath: "https://localhost", + DefaultHeader: make(map[string]string), + UserAgent: "Swagger-Codegen/2.1.0/go", + } + return cfg +} + +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} \ No newline at end of file diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/console_size.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/console_size.go new file mode 100644 index 000000000..adbe07fe5 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/console_size.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ConsoleSize struct { + + Height int32 `json:"Height,omitempty"` + + Width int32 `json:"Width,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/container.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/container.go new file mode 100644 index 000000000..17dce28bc --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/container.go @@ -0,0 +1,35 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Container struct { + + GuestOs *GuestOs `json:"GuestOs,omitempty"` + + Storage *Storage `json:"Storage,omitempty"` + + MappedDirectories []MappedDirectory `json:"MappedDirectories,omitempty"` + + MappedPipes []MappedPipe `json:"MappedPipes,omitempty"` + + Memory *Memory `json:"Memory,omitempty"` + + Processor *Processor `json:"Processor,omitempty"` + + Networking *Networking `json:"Networking,omitempty"` + + HvSocket *HvSocket `json:"HvSocket,omitempty"` + + ContainerCredentialGuard *ContainerCredentialGuardState `json:"ContainerCredentialGuard,omitempty"` + + RegistryChanges *RegistryChanges `json:"RegistryChanges,omitempty"` + + AssignedDevices []Device `json:"AssignedDevices,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_credential_guard_state.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_credential_guard_state.go new file mode 100644 index 000000000..0f8f64437 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_credential_guard_state.go @@ -0,0 +1,25 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardState struct { + + // Authentication cookie for calls to a Container Credential Guard instance. + Cookie string `json:"Cookie,omitempty"` + + // Name of the RPC endpoint of the Container Credential Guard instance. + RpcEndpoint string `json:"RpcEndpoint,omitempty"` + + // Transport used for the configured Container Credential Guard instance. + Transport string `json:"Transport,omitempty"` + + // Credential spec used for the configured Container Credential Guard instance. + CredentialSpec string `json:"CredentialSpec,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_memory_information.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_memory_information.go new file mode 100644 index 000000000..754797e21 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_memory_information.go @@ -0,0 +1,26 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// memory usage as viewed from within the container +type ContainerMemoryInformation struct { + + TotalPhysicalBytes int32 `json:"TotalPhysicalBytes,omitempty"` + + TotalUsage int32 `json:"TotalUsage,omitempty"` + + CommittedBytes int32 `json:"CommittedBytes,omitempty"` + + SharedCommittedBytes int32 `json:"SharedCommittedBytes,omitempty"` + + CommitLimitBytes int32 `json:"CommitLimitBytes,omitempty"` + + PeakCommitmentBytes int32 `json:"PeakCommitmentBytes,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/device.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/device.go new file mode 100644 index 000000000..ca319bbbc --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/device.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Device struct { + + // The interface class guid of the device to assign to container. + InterfaceClassGuid string `json:"InterfaceClassGuid,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/devices.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/devices.go new file mode 100644 index 000000000..b2191c571 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/devices.go @@ -0,0 +1,43 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Devices struct { + + ComPorts map[string]ComPort `json:"ComPorts,omitempty"` + + Scsi map[string]Scsi `json:"Scsi,omitempty"` + + VirtualPMem *VirtualPMemController `json:"VirtualPMem,omitempty"` + + NetworkAdapters map[string]NetworkAdapter `json:"NetworkAdapters,omitempty"` + + VideoMonitor *VideoMonitor `json:"VideoMonitor,omitempty"` + + Keyboard *Keyboard `json:"Keyboard,omitempty"` + + Mouse *Mouse `json:"Mouse,omitempty"` + + HvSocket *HvSocket2 `json:"HvSocket,omitempty"` + + EnhancedModeVideo *EnhancedModeVideo `json:"EnhancedModeVideo,omitempty"` + + GuestCrashReporting *GuestCrashReporting `json:"GuestCrashReporting,omitempty"` + + VirtualSmb *VirtualSmb `json:"VirtualSmb,omitempty"` + + Plan9 *Plan9 `json:"Plan9,omitempty"` + + Battery *Battery `json:"Battery,omitempty"` + + FlexibleIov map[string]FlexibleIoDevice `json:"FlexibleIov,omitempty"` + + SharedMemory *SharedMemoryConfiguration `json:"SharedMemory,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/enhanced_mode_video.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/enhanced_mode_video.go new file mode 100644 index 000000000..4fe592f71 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/enhanced_mode_video.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type EnhancedModeVideo struct { + + ConnectionOptions *RdpConnectionOptions `json:"ConnectionOptions,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/flexible_io_device.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/flexible_io_device.go new file mode 100644 index 000000000..51011afe4 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/flexible_io_device.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type FlexibleIoDevice struct { + + EmulatorId string `json:"EmulatorId,omitempty"` + + HostingModel string `json:"HostingModel,omitempty"` + + Configuration []string `json:"Configuration,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection.go new file mode 100644 index 000000000..7db29495b --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestConnection struct { + + // Use Vsock rather than Hyper-V sockets to communicate with the guest service. + UseVsock bool `json:"UseVsock,omitempty"` + + // Don't disconnect the guest connection when pausing the virtual machine. + UseConnectedSuspend bool `json:"UseConnectedSuspend,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection_info.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection_info.go new file mode 100644 index 000000000..8a369bab7 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection_info.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Information about the guest. +type GuestConnectionInfo struct { + + // Each schema version x.y stands for the range of versions a.b where a==x and b<=y. This list comes from the SupportedSchemaVersions field in GcsCapabilities. + SupportedSchemaVersions []Version `json:"SupportedSchemaVersions,omitempty"` + + ProtocolVersion int32 `json:"ProtocolVersion,omitempty"` + + GuestDefinedCapabilities *interface{} `json:"GuestDefinedCapabilities,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_crash_reporting.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_crash_reporting.go new file mode 100644 index 000000000..c5fa76735 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_crash_reporting.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestCrashReporting struct { + + WindowsCrashSettings *WindowsCrashReporting `json:"WindowsCrashSettings,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_os.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_os.go new file mode 100644 index 000000000..c708fc7c3 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_os.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestOs struct { + + HostName string `json:"HostName,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_state.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_state.go new file mode 100644 index 000000000..ef1eec886 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_state.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestState struct { + + // The path to an existing file uses for persistent guest state storage. An empty string indicates the system should initialize new transient, in-memory guest state. + GuestStateFilePath string `json:"GuestStateFilePath,omitempty"` + + // The path to an existing file for persistent runtime state storage. An empty string indicates the system should initialize new transient, in-memory runtime state. + RuntimeStateFilePath string `json:"RuntimeStateFilePath,omitempty"` + + // If true, the guest state and runtime state files will be used as templates to populate transient, in-memory state instead of using the files as persistent backing store. + ForceTransientState bool `json:"ForceTransientState,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hosted_system.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hosted_system.go new file mode 100644 index 000000000..0797584c5 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hosted_system.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HostedSystem struct { + + SchemaVersion *Version `json:"SchemaVersion,omitempty"` + + Container *Container `json:"Container,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket.go new file mode 100644 index 000000000..ef9ffb8dd --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HvSocket struct { + + Config *HvSocketSystemConfig `json:"Config,omitempty"` + + EnablePowerShellDirect bool `json:"EnablePowerShellDirect,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_2.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_2.go new file mode 100644 index 000000000..a19ba15c1 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_2.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// HvSocket configuration for a VM +type HvSocket2 struct { + + HvSocketConfig *HvSocketSystemConfig `json:"HvSocketConfig,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_service_config.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_service_config.go new file mode 100644 index 000000000..a848e91e6 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_service_config.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HvSocketServiceConfig struct { + + // SDDL string that HvSocket will check before allowing a host process to bind to this specific service. If not specified, defaults to the system DefaultBindSecurityDescriptor, defined in HvSocketSystemWpConfig in V1. + BindSecurityDescriptor string `json:"BindSecurityDescriptor,omitempty"` + + // SDDL string that HvSocket will check before allowing a host process to connect to this specific service. If not specified, defaults to the system DefaultConnectSecurityDescriptor, defined in HvSocketSystemWpConfig in V1. + ConnectSecurityDescriptor string `json:"ConnectSecurityDescriptor,omitempty"` + + // If true, HvSocket will process wildcard binds for this service/system combination. Wildcard binds are secured in the registry at SOFTWARE/Microsoft/Windows NT/CurrentVersion/Virtualization/HvSocket/WildcardDescriptors + AllowWildcardBinds bool `json:"AllowWildcardBinds,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_system_config.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_system_config.go new file mode 100644 index 000000000..69f4f9d39 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_system_config.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// This is the HCS Schema version of the HvSocket configuration. The VMWP version is located in Config.Devices.IC in V1. +type HvSocketSystemConfig struct { + + // SDDL string that HvSocket will check before allowing a host process to bind to an unlisted service for this specific container/VM (not wildcard binds). + DefaultBindSecurityDescriptor string `json:"DefaultBindSecurityDescriptor,omitempty"` + + // SDDL string that HvSocket will check before allowing a host process to connect to an unlisted service in the VM/container. + DefaultConnectSecurityDescriptor string `json:"DefaultConnectSecurityDescriptor,omitempty"` + + ServiceTable map[string]HvSocketServiceConfig `json:"ServiceTable,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/keyboard.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/keyboard.go new file mode 100644 index 000000000..3d3fa3b1c --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/keyboard.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Keyboard struct { +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/layer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/layer.go new file mode 100644 index 000000000..b63b8ef12 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/layer.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Layer struct { + + Id string `json:"Id,omitempty"` + + Path string `json:"Path,omitempty"` + + PathType string `json:"PathType,omitempty"` + + // Unspecified defaults to Enabled + Cache string `json:"Cache,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/linux_kernel_direct.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/linux_kernel_direct.go new file mode 100644 index 000000000..0ab6c280f --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/linux_kernel_direct.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.2 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type LinuxKernelDirect struct { + KernelFilePath string `json:"KernelFilePath,omitempty"` + + InitRdPath string `json:"InitRdPath,omitempty"` + + KernelCmdLine string `json:"KernelCmdLine,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_directory.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_directory.go new file mode 100644 index 000000000..a823a6d3b --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_directory.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MappedDirectory struct { + + HostPath string `json:"HostPath,omitempty"` + + HostPathType string `json:"HostPathType,omitempty"` + + ContainerPath string `json:"ContainerPath,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_pipe.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_pipe.go new file mode 100644 index 000000000..2d1d2604a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_pipe.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MappedPipe struct { + + ContainerPipeName string `json:"ContainerPipeName,omitempty"` + + HostPath string `json:"HostPath,omitempty"` + + HostPathType string `json:"HostPathType,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory.go new file mode 100644 index 000000000..e1d135a3a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Memory struct { + + SizeInMB int32 `json:"SizeInMB,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_2.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_2.go new file mode 100644 index 000000000..27d0b8c48 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_2.go @@ -0,0 +1,25 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Memory2 struct { + SizeInMB int32 `json:"SizeInMB,omitempty"` + + AllowOvercommit bool `json:"AllowOvercommit,omitempty"` + + EnableHotHint bool `json:"EnableHotHint,omitempty"` + + EnableColdHint bool `json:"EnableColdHint,omitempty"` + + EnableEpf bool `json:"EnableEpf,omitempty"` + + // EnableDeferredCommit is private in the schema. If regenerated need to add back. + EnableDeferredCommit bool `json:"EnableDeferredCommit,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_information_for_vm.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_information_for_vm.go new file mode 100644 index 000000000..bdd87dffd --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_information_for_vm.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MemoryInformationForVm struct { + + VirtualNodeCount int32 `json:"VirtualNodeCount,omitempty"` + + VirtualMachineMemory *VmMemory `json:"VirtualMachineMemory,omitempty"` + + VirtualNodes []VirtualNodeInfo `json:"VirtualNodes,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_stats.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_stats.go new file mode 100644 index 000000000..6214970f6 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_stats.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Memory runtime statistics +type MemoryStats struct { + + MemoryUsageCommitBytes int32 `json:"MemoryUsageCommitBytes,omitempty"` + + MemoryUsageCommitPeakBytes int32 `json:"MemoryUsageCommitPeakBytes,omitempty"` + + MemoryUsagePrivateWorkingSetBytes int32 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/modify_setting_request.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/modify_setting_request.go new file mode 100644 index 000000000..d29455a3e --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/modify_setting_request.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ModifySettingRequest struct { + ResourcePath string `json:"ResourcePath,omitempty"` + + RequestType string `json:"RequestType,omitempty"` + + Settings interface{} `json:"Settings,omitempty"` // NOTE: Swagger generated as *interface{}. Locally updated + + GuestRequest interface{} `json:"GuestRequest,omitempty"` // NOTE: Swagger generated as *interface{}. Locally updated +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/mouse.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/mouse.go new file mode 100644 index 000000000..ccf8b938f --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/mouse.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Mouse struct { +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/network_adapter.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/network_adapter.go new file mode 100644 index 000000000..c586f66c2 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/network_adapter.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type NetworkAdapter struct { + + EndpointId string `json:"EndpointId,omitempty"` + + MacAddress string `json:"MacAddress,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/networking.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/networking.go new file mode 100644 index 000000000..12c47827c --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/networking.go @@ -0,0 +1,24 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Networking struct { + + AllowUnqualifiedDnsQuery bool `json:"AllowUnqualifiedDnsQuery,omitempty"` + + DnsSearchList string `json:"DnsSearchList,omitempty"` + + NetworkSharedContainerName string `json:"NetworkSharedContainerName,omitempty"` + + // Guid in windows; string in linux + Namespace string `json:"Namespace,omitempty"` + + NetworkAdapters []string `json:"NetworkAdapters,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_notification.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_notification.go new file mode 100644 index 000000000..1cd70d179 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_notification.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Notification data that is indicated to components running in the Virtual Machine. +type PauseNotification struct { + + Reason string `json:"Reason,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_options.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_options.go new file mode 100644 index 000000000..780a5cae2 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_options.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Options for HcsPauseComputeSystem +type PauseOptions struct { + + SuspensionLevel string `json:"SuspensionLevel,omitempty"` + + HostedNotification *PauseNotification `json:"HostedNotification,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9.go new file mode 100644 index 000000000..705c677e1 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Plan9 struct { + + Shares []Plan9Share `json:"Shares,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9_share.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9_share.go new file mode 100644 index 000000000..eb171817a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9_share.go @@ -0,0 +1,33 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Plan9Share struct { + + Name string `json:"Name,omitempty"` + + // The name by which the guest operation system can access this share, via the aname parameter in the Plan9 protocol. + AccessName string `json:"AccessName,omitempty"` + + Path string `json:"Path,omitempty"` + + Port int32 `json:"Port,omitempty"` + + // Flags are marked private. Until they are exported correctly + // + // ReadOnly 0x00000001 + // LinuxMetadata 0x00000004 + // CaseSensitive 0x00000008 + Flags int32 `json:"Flags,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` + + UseShareRootIdentity bool `json:"UseShareRootIdentity,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_details.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_details.go new file mode 100644 index 000000000..63e0b7f8f --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_details.go @@ -0,0 +1,34 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "time" +) + +// Information about a process running in a container +type ProcessDetails struct { + + ProcessId int32 `json:"ProcessId,omitempty"` + + ImageName string `json:"ImageName,omitempty"` + + CreateTimestamp time.Time `json:"CreateTimestamp,omitempty"` + + UserTime100ns int32 `json:"UserTime100ns,omitempty"` + + KernelTime100ns int32 `json:"KernelTime100ns,omitempty"` + + MemoryCommitBytes int32 `json:"MemoryCommitBytes,omitempty"` + + MemoryWorkingSetPrivateBytes int32 `json:"MemoryWorkingSetPrivateBytes,omitempty"` + + MemoryWorkingSetSharedBytes int32 `json:"MemoryWorkingSetSharedBytes,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_modify_request.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_modify_request.go new file mode 100644 index 000000000..29bc2e3d0 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_modify_request.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Passed to HcsRpc_ModifyProcess +type ProcessModifyRequest struct { + + Operation string `json:"Operation,omitempty"` + + ConsoleSize *ConsoleSize `json:"ConsoleSize,omitempty"` + + CloseHandle *CloseHandle `json:"CloseHandle,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_parameters.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_parameters.go new file mode 100644 index 000000000..470c55734 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_parameters.go @@ -0,0 +1,47 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ProcessParameters struct { + + ApplicationName string `json:"ApplicationName,omitempty"` + + CommandLine string `json:"CommandLine,omitempty"` + + // optional alternative to CommandLine, currently only supported by Linux GCS + CommandArgs []string `json:"CommandArgs,omitempty"` + + User string `json:"User,omitempty"` + + WorkingDirectory string `json:"WorkingDirectory,omitempty"` + + Environment map[string]string `json:"Environment,omitempty"` + + // if set, will run as low-privilege process + RestrictedToken bool `json:"RestrictedToken,omitempty"` + + // if set, ignore StdErrPipe + EmulateConsole bool `json:"EmulateConsole,omitempty"` + + CreateStdInPipe bool `json:"CreateStdInPipe,omitempty"` + + CreateStdOutPipe bool `json:"CreateStdOutPipe,omitempty"` + + CreateStdErrPipe bool `json:"CreateStdErrPipe,omitempty"` + + // height then width + ConsoleSize []int32 `json:"ConsoleSize,omitempty"` + + // if set, find an existing session for the user and create the process in it + UseExistingLogin bool `json:"UseExistingLogin,omitempty"` + + // if set, use the legacy console instead of conhost + UseLegacyConsole bool `json:"UseLegacyConsole,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_status.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_status.go new file mode 100644 index 000000000..20793d150 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_status.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Status of a process running in a container +type ProcessStatus struct { + + ProcessId int32 `json:"ProcessId,omitempty"` + + Exited bool `json:"Exited,omitempty"` + + ExitCode int32 `json:"ExitCode,omitempty"` + + LastWaitResult int32 `json:"LastWaitResult,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor.go new file mode 100644 index 000000000..7a60b0245 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Processor struct { + + Count int32 `json:"Count,omitempty"` + + Maximum int32 `json:"Maximum,omitempty"` + + Weight int32 `json:"Weight,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_2.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_2.go new file mode 100644 index 000000000..40d3e7356 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_2.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Processor2 struct { + + Count int32 `json:"Count,omitempty"` + + Limit int32 `json:"Limit,omitempty"` + + Weight int32 `json:"Weight,omitempty"` + + ExposeVirtualizationExtensions bool `json:"ExposeVirtualizationExtensions,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_stats.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_stats.go new file mode 100644 index 000000000..9d3b77e57 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_stats.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// CPU runtime statistics +type ProcessorStats struct { + + TotalRuntime100ns int32 `json:"TotalRuntime100ns,omitempty"` + + RuntimeUser100ns int32 `json:"RuntimeUser100ns,omitempty"` + + RuntimeKernel100ns int32 `json:"RuntimeKernel100ns,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/properties.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/properties.go new file mode 100644 index 000000000..6db2a48f6 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/properties.go @@ -0,0 +1,47 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Properties struct { + + Id string `json:"Id,omitempty"` + + SystemType string `json:"SystemType,omitempty"` + + RuntimeOsType string `json:"RuntimeOsType,omitempty"` + + Name string `json:"Name,omitempty"` + + Owner string `json:"Owner,omitempty"` + + RuntimeId string `json:"RuntimeId,omitempty"` + + RuntimeTemplateId string `json:"RuntimeTemplateId,omitempty"` + + State string `json:"State,omitempty"` + + Stopped bool `json:"Stopped,omitempty"` + + ExitType string `json:"ExitType,omitempty"` + + Memory *MemoryInformationForVm `json:"Memory,omitempty"` + + Statistics *Statistics `json:"Statistics,omitempty"` + + ProcessList []ProcessDetails `json:"ProcessList,omitempty"` + + TerminateOnLastHandleClosed bool `json:"TerminateOnLastHandleClosed,omitempty"` + + HostingSystemId string `json:"HostingSystemId,omitempty"` + + SharedMemoryRegionInfo []SharedMemoryRegionInfo `json:"SharedMemoryRegionInfo,omitempty"` + + GuestConnectionInfo *GuestConnectionInfo `json:"GuestConnectionInfo,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/property_query.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/property_query.go new file mode 100644 index 000000000..22b92ffdf --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/property_query.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// By default the basic properties will be returned. This query provides a way to request specific properties. +type PropertyQuery struct { + + PropertyTypes []string `json:"PropertyTypes,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/rdp_connection_options.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/rdp_connection_options.go new file mode 100644 index 000000000..97e453128 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/rdp_connection_options.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RdpConnectionOptions struct { + + AccessSids []string `json:"AccessSids,omitempty"` + + NamedPipe string `json:"NamedPipe,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_changes.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_changes.go new file mode 100644 index 000000000..fa574ccc8 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_changes.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryChanges struct { + + AddValues []RegistryValue `json:"AddValues,omitempty"` + + DeleteKeys []RegistryKey `json:"DeleteKeys,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_key.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_key.go new file mode 100644 index 000000000..fab03bc60 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_key.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryKey struct { + + Hive string `json:"Hive,omitempty"` + + Name string `json:"Name,omitempty"` + + Volatile bool `json:"Volatile,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_value.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_value.go new file mode 100644 index 000000000..1589f4841 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_value.go @@ -0,0 +1,31 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryValue struct { + + Key *RegistryKey `json:"Key,omitempty"` + + Name string `json:"Name,omitempty"` + + Type_ string `json:"Type,omitempty"` + + // One and only one value type must be set. + StringValue string `json:"StringValue,omitempty"` + + BinaryValue string `json:"BinaryValue,omitempty"` + + DWordValue int32 `json:"DWordValue,omitempty"` + + QWordValue int32 `json:"QWordValue,omitempty"` + + // Only used if RegistryValueType is CustomType The data is in BinaryValue + CustomType int32 `json:"CustomType,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/restore_state.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/restore_state.go new file mode 100644 index 000000000..778ff5873 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/restore_state.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RestoreState struct { + + // The path to the save state file to restore the system from. + SaveStateFilePath string `json:"SaveStateFilePath,omitempty"` + + // The ID of the template system to clone this new system off of. An empty string indicates the system should not be cloned from a template. + TemplateSystemId string `json:"TemplateSystemId,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/save_options.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/save_options.go new file mode 100644 index 000000000..e55fa1d98 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/save_options.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SaveOptions struct { + + // The type of save operation to be performed. + SaveType string `json:"SaveType,omitempty"` + + // The path to the file that will container the saved state. + SaveStateFilePath string `json:"SaveStateFilePath,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/scsi.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/scsi.go new file mode 100644 index 000000000..bf253a470 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/scsi.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Scsi struct { + + // Map of attachments, where the key is the integer LUN number on the controller. + Attachments map[string]Attachment `json:"Attachments,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_configuration.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_configuration.go new file mode 100644 index 000000000..bd573f6cd --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_configuration.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryConfiguration struct { + + Regions []SharedMemoryRegion `json:"Regions,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region.go new file mode 100644 index 000000000..a57b2cba7 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region.go @@ -0,0 +1,23 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryRegion struct { + + SectionName string `json:"SectionName,omitempty"` + + StartOffset int32 `json:"StartOffset,omitempty"` + + Length int32 `json:"Length,omitempty"` + + AllowGuestWrite bool `json:"AllowGuestWrite,omitempty"` + + HiddenFromGuest bool `json:"HiddenFromGuest,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region_info.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region_info.go new file mode 100644 index 000000000..d9a50cc7d --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region_info.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryRegionInfo struct { + + SectionName string `json:"SectionName,omitempty"` + + GuestPhysicalAddress int32 `json:"GuestPhysicalAddress,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/silo_properties.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/silo_properties.go new file mode 100644 index 000000000..599c06e8a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/silo_properties.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Silo job information +type SiloProperties struct { + + Enabled bool `json:"Enabled,omitempty"` + + JobName string `json:"JobName,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/statistics.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/statistics.go new file mode 100644 index 000000000..5cb3ed93b --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/statistics.go @@ -0,0 +1,30 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "time" +) + +// Runtime statistics for a container +type Statistics struct { + + Timestamp time.Time `json:"Timestamp,omitempty"` + + ContainerStartTime time.Time `json:"ContainerStartTime,omitempty"` + + Uptime100ns int32 `json:"Uptime100ns,omitempty"` + + Processor *ProcessorStats `json:"Processor,omitempty"` + + Memory *MemoryStats `json:"Memory,omitempty"` + + Storage *StorageStats `json:"Storage,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage.go new file mode 100644 index 000000000..2627af913 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Storage struct { + + // List of layers that describe the parent hierarchy for a container's storage. These layers combined together, presented as a disposable and/or committable working storage, are used by the container to record all changes done to the parent layers. + Layers []Layer `json:"Layers,omitempty"` + + // Path that points to the scratch space of a container, where parent layers are combined together to present a new disposable and/or committable layer with the changes done during its runtime. + Path string `json:"Path,omitempty"` + + QoS *StorageQoS `json:"QoS,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_qo_s.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_qo_s.go new file mode 100644 index 000000000..8c5255df1 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_qo_s.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type StorageQoS struct { + + IopsMaximum int32 `json:"IopsMaximum,omitempty"` + + BandwidthMaximum int32 `json:"BandwidthMaximum,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_stats.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_stats.go new file mode 100644 index 000000000..198ea57d7 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_stats.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Storage runtime statistics +type StorageStats struct { + + ReadCountNormalized int32 `json:"ReadCountNormalized,omitempty"` + + ReadSizeBytes int32 `json:"ReadSizeBytes,omitempty"` + + WriteCountNormalized int32 `json:"WriteCountNormalized,omitempty"` + + WriteSizeBytes int32 `json:"WriteSizeBytes,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/topology.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/topology.go new file mode 100644 index 000000000..af2e3c823 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/topology.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Topology struct { + + Memory *Memory2 `json:"Memory,omitempty"` + + Processor *Processor2 `json:"Processor,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi.go new file mode 100644 index 000000000..ba91178f9 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Uefi struct { + + EnableDebugger bool `json:"EnableDebugger,omitempty"` + + SecureBootTemplateId string `json:"SecureBootTemplateId,omitempty"` + + BootThis *UefiBootEntry `json:"BootThis,omitempty"` + + Console string `json:"Console,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi_boot_entry.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi_boot_entry.go new file mode 100644 index 000000000..6620fb2bc --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi_boot_entry.go @@ -0,0 +1,23 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type UefiBootEntry struct { + + DeviceType string `json:"DeviceType,omitempty"` + + DevicePath string `json:"DevicePath,omitempty"` + + DiskNumber int32 `json:"DiskNumber,omitempty"` + + OptionalData string `json:"OptionalData,omitempty"` + + VmbFsRootPath string `json:"VmbFsRootPath,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/version.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/version.go new file mode 100644 index 000000000..62c0e4d12 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/version.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Version struct { + + Major int32 `json:"Major,omitempty"` + + Minor int32 `json:"Minor,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/video_monitor.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/video_monitor.go new file mode 100644 index 000000000..0958e5606 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/video_monitor.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VideoMonitor struct { + + HorizontalResolution int32 `json:"HorizontalResolution,omitempty"` + + VerticalResolution int32 `json:"VerticalResolution,omitempty"` + + ConnectionOptions *RdpConnectionOptions `json:"ConnectionOptions,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_machine.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_machine.go new file mode 100644 index 000000000..2d22b1bcb --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_machine.go @@ -0,0 +1,32 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualMachine struct { + + // StopOnReset is private in the schema. If regenerated need to put back. + StopOnReset bool `json:"StopOnReset,omitempty"` + + Chipset *Chipset `json:"Chipset,omitempty"` + + ComputeTopology *Topology `json:"ComputeTopology,omitempty"` + + Devices *Devices `json:"Devices,omitempty"` + + GuestState *GuestState `json:"GuestState,omitempty"` + + RestoreState *RestoreState `json:"RestoreState,omitempty"` + + RegistryChanges *RegistryChanges `json:"RegistryChanges,omitempty"` + + StorageQoS *StorageQoS `json:"StorageQoS,omitempty"` + + GuestConnection *GuestConnection `json:"GuestConnection,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_node_info.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_node_info.go new file mode 100644 index 000000000..48402d8ec --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_node_info.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualNodeInfo struct { + + VirtualNodeIndex int32 `json:"VirtualNodeIndex,omitempty"` + + PhysicalNodeNumber int32 `json:"PhysicalNodeNumber,omitempty"` + + VirtualProcessorCount int32 `json:"VirtualProcessorCount,omitempty"` + + MemoryUsageInPages int32 `json:"MemoryUsageInPages,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_controller.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_controller.go new file mode 100644 index 000000000..f5b7f3e38 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_controller.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualPMemController struct { + Devices map[string]VirtualPMemDevice `json:"Devices,omitempty"` + + MaximumCount uint32 `json:"MaximumCount,omitempty"` + + MaximumSizeBytes uint64 `json:"MaximumSizeBytes,omitempty"` + + Backing string `json:"Backing,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_device.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_device.go new file mode 100644 index 000000000..47714444a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_device.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualPMemDevice struct { + + HostPath string `json:"HostPath,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` + + ImageFormat string `json:"ImageFormat,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb.go new file mode 100644 index 000000000..76131b3a7 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmb struct { + + Shares []VirtualSmbShare `json:"Shares,omitempty"` + + DirectFileMappingInMB int64 `json:"DirectFileMappingInMB,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share.go new file mode 100644 index 000000000..b50098a42 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmbShare struct { + + Name string `json:"Name,omitempty"` + + Path string `json:"Path,omitempty"` + + AllowedFiles []string `json:"AllowedFiles,omitempty"` + + Options *VirtualSmbShareOptions `json:"Options,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share_options.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share_options.go new file mode 100644 index 000000000..c1894279d --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share_options.go @@ -0,0 +1,63 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmbShareOptions struct { + + ReadOnly bool `json:"ReadOnly,omitempty"` + + // convert exclusive access to shared read access + ShareRead bool `json:"ShareRead,omitempty"` + + // all opens will use cached I/O + CacheIo bool `json:"CacheIo,omitempty"` + + // disable oplock support + NoOplocks bool `json:"NoOplocks,omitempty"` + + // Acquire the backup privilege when attempting to open + TakeBackupPrivilege bool `json:"TakeBackupPrivilege,omitempty"` + + // Use the identity of the share root when opening + UseShareRootIdentity bool `json:"UseShareRootIdentity,omitempty"` + + // disable Direct Mapping + NoDirectmap bool `json:"NoDirectmap,omitempty"` + + // disable Byterange locks + NoLocks bool `json:"NoLocks,omitempty"` + + // disable Directory CHange Notifications + NoDirnotify bool `json:"NoDirnotify,omitempty"` + + // share is use for VM shared memory + VmSharedMemory bool `json:"VmSharedMemory,omitempty"` + + // allow access only to the files specified in AllowedFiles + RestrictFileAccess bool `json:"RestrictFileAccess,omitempty"` + + // disable all oplocks except Level II + ForceLevelIIOplocks bool `json:"ForceLevelIIOplocks,omitempty"` + + // Allow the host to reparse this base layer + ReparseBaseLayer bool `json:"ReparseBaseLayer,omitempty"` + + // Enable pseudo-oplocks + PseudoOplocks bool `json:"PseudoOplocks,omitempty"` + + // All opens will use non-cached IO + NonCacheIo bool `json:"NonCacheIo,omitempty"` + + // Enable pseudo directory change notifications + PseudoDirnotify bool `json:"PseudoDirnotify,omitempty"` + + // Block directory enumeration, renames, and deletes. + SingleFileMapping bool `json:"SingleFileMapping,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/vm_memory.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/vm_memory.go new file mode 100644 index 000000000..39f628667 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/vm_memory.go @@ -0,0 +1,27 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VmMemory struct { + + AvailableMemory int32 `json:"AvailableMemory,omitempty"` + + AvailableMemoryBuffer int32 `json:"AvailableMemoryBuffer,omitempty"` + + ReservedMemory int32 `json:"ReservedMemory,omitempty"` + + AssignedMemory int32 `json:"AssignedMemory,omitempty"` + + SlpActive bool `json:"SlpActive,omitempty"` + + BalancingEnabled bool `json:"BalancingEnabled,omitempty"` + + DmOperationInProgress bool `json:"DmOperationInProgress,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/windows_crash_reporting.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/windows_crash_reporting.go new file mode 100644 index 000000000..cf632bbc8 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/schema2/windows_crash_reporting.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type WindowsCrashReporting struct { + + DumpFileName string `json:"DumpFileName,omitempty"` + + MaxDumpSize int64 `json:"MaxDumpSize,omitempty"` +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go new file mode 100644 index 000000000..ff3b6572e --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go @@ -0,0 +1,70 @@ +package timeout + +import ( + "os" + "strconv" + "time" +) + +var ( + // defaultTimeout is the timeout for most operations that is not overridden. + defaultTimeout = 4 * time.Minute + + // defaultTimeoutTestdRetry is the retry loop timeout for testd to respond + // for a disk to come online in LCOW. + defaultTimeoutTestdRetry = 5 * time.Second +) + +// External variables for HCSShim consumers to use. +var ( + // SystemCreate is the timeout for creating a compute system + SystemCreate time.Duration = defaultTimeout + + // SystemStart is the timeout for starting a compute system + SystemStart time.Duration = defaultTimeout + + // SystemPause is the timeout for pausing a compute system + SystemPause time.Duration = defaultTimeout + + // SystemResume is the timeout for resuming a compute system + SystemResume time.Duration = defaultTimeout + + // SyscallWatcher is the timeout before warning of a potential stuck platform syscall. + SyscallWatcher time.Duration = defaultTimeout + + // Tar2VHD is the timeout for the tar2vhd operation to complete + Tar2VHD time.Duration = defaultTimeout + + // ExternalCommandToStart is the timeout for external commands to start + ExternalCommandToStart = defaultTimeout + + // ExternalCommandToComplete is the timeout for external commands to complete. + // Generally this means copying data from their stdio pipes. + ExternalCommandToComplete = defaultTimeout + + // TestDRetryLoop is the timeout for testd retry loop when onlining a SCSI disk in LCOW + TestDRetryLoop = defaultTimeoutTestdRetry +) + +func init() { + SystemCreate = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMCREATE", SystemCreate) + SystemStart = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMSTART", SystemStart) + SystemPause = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMPAUSE", SystemPause) + SystemResume = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMRESUME", SystemResume) + SyscallWatcher = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSCALLWATCHER", SyscallWatcher) + Tar2VHD = durationFromEnvironment("HCSSHIM_TIMEOUT_TAR2VHD", Tar2VHD) + ExternalCommandToStart = durationFromEnvironment("HCSSHIM_TIMEOUT_EXTERNALCOMMANDSTART", ExternalCommandToStart) + ExternalCommandToComplete = durationFromEnvironment("HCSSHIM_TIMEOUT_EXTERNALCOMMANDCOMPLETE", ExternalCommandToComplete) + TestDRetryLoop = durationFromEnvironment("HCSSHIM_TIMEOUT_TESTDRETRYLOOP", TestDRetryLoop) +} + +func durationFromEnvironment(env string, defaultValue time.Duration) time.Duration { + envTimeout := os.Getenv(env) + if len(envTimeout) > 0 { + e, err := strconv.Atoi(envTimeout) + if err == nil && e > 0 { + return time.Second * time.Duration(e) + } + } + return defaultValue +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go new file mode 100644 index 000000000..dcb919268 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go @@ -0,0 +1,32 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// ActivateLayer will find the layer with the given id and mount it's filesystem. +// For a read/write layer, the mounted filesystem will appear as a volume on the +// host, while a read-only layer is generally expected to be a no-op. +// An activated layer must later be deactivated via DeactivateLayer. +func ActivateLayer(path string) (err error) { + title := "hcsshim::ActivateLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + err = activateLayer(&stdDriverInfo, path) + if err != nil { + return hcserror.New(err, title+" - failed", "") + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go new file mode 100644 index 000000000..5784241df --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go @@ -0,0 +1,173 @@ +package wclayer + +import ( + "errors" + "os" + "path/filepath" + "syscall" + + "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/safefile" +) + +type baseLayerWriter struct { + root *os.File + f *os.File + bw *winio.BackupFileWriter + err error + hasUtilityVM bool + dirInfo []dirInfo +} + +type dirInfo struct { + path string + fileInfo winio.FileBasicInfo +} + +// reapplyDirectoryTimes reapplies directory modification, creation, etc. times +// after processing of the directory tree has completed. The times are expected +// to be ordered such that parent directories come before child directories. +func reapplyDirectoryTimes(root *os.File, dis []dirInfo) error { + for i := range dis { + di := &dis[len(dis)-i-1] // reverse order: process child directories first + f, err := safefile.OpenRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, safefile.FILE_OPEN, safefile.FILE_DIRECTORY_FILE) + if err != nil { + return err + } + + err = winio.SetFileBasicInfo(f, &di.fileInfo) + f.Close() + if err != nil { + return err + } + } + return nil +} + +func (w *baseLayerWriter) closeCurrentFile() error { + if w.f != nil { + err := w.bw.Close() + err2 := w.f.Close() + w.f = nil + w.bw = nil + if err != nil { + return err + } + if err2 != nil { + return err2 + } + } + return nil +} + +func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err error) { + defer func() { + if err != nil { + w.err = err + } + }() + + err = w.closeCurrentFile() + if err != nil { + return err + } + + if filepath.ToSlash(name) == `UtilityVM/Files` { + w.hasUtilityVM = true + } + + var f *os.File + defer func() { + if f != nil { + f.Close() + } + }() + + extraFlags := uint32(0) + if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + extraFlags |= safefile.FILE_DIRECTORY_FILE + if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + w.dirInfo = append(w.dirInfo, dirInfo{name, *fileInfo}) + } + } + + mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY) + f, err = safefile.OpenRelative(name, w.root, mode, syscall.FILE_SHARE_READ, safefile.FILE_CREATE, extraFlags) + if err != nil { + return hcserror.New(err, "Failed to safefile.OpenRelative", name) + } + + err = winio.SetFileBasicInfo(f, fileInfo) + if err != nil { + return hcserror.New(err, "Failed to SetFileBasicInfo", name) + } + + w.f = f + w.bw = winio.NewBackupFileWriter(f, true) + f = nil + return nil +} + +func (w *baseLayerWriter) AddLink(name string, target string) (err error) { + defer func() { + if err != nil { + w.err = err + } + }() + + err = w.closeCurrentFile() + if err != nil { + return err + } + + return safefile.LinkRelative(target, w.root, name, w.root) +} + +func (w *baseLayerWriter) Remove(name string) error { + return errors.New("base layer cannot have tombstones") +} + +func (w *baseLayerWriter) Write(b []byte) (int, error) { + n, err := w.bw.Write(b) + if err != nil { + w.err = err + } + return n, err +} + +func (w *baseLayerWriter) Close() error { + defer func() { + w.root.Close() + w.root = nil + }() + err := w.closeCurrentFile() + if err != nil { + return err + } + if w.err == nil { + // Restore the file times of all the directories, since they may have + // been modified by creating child directories. + err = reapplyDirectoryTimes(w.root, w.dirInfo) + if err != nil { + return err + } + + err = ProcessBaseLayer(w.root.Name()) + if err != nil { + return err + } + + if w.hasUtilityVM { + err := safefile.EnsureNotReparsePointRelative("UtilityVM", w.root) + if err != nil { + return err + } + err = ProcessUtilityVMImage(filepath.Join(w.root.Name(), "UtilityVM")) + if err != nil { + return err + } + } + } + return w.err +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go new file mode 100644 index 000000000..be2bc3fd6 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go @@ -0,0 +1,31 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// CreateLayer creates a new, empty, read-only layer on the filesystem based on +// the parent layer provided. +func CreateLayer(path, parent string) (err error) { + title := "hcsshim::CreateLayer" + fields := logrus.Fields{ + "parent": parent, + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + err = createLayer(&stdDriverInfo, path, parent) + if err != nil { + return hcserror.New(err, title+" - failed", "") + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go new file mode 100644 index 000000000..7e3351289 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go @@ -0,0 +1,38 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// CreateScratchLayer creates and populates new read-write layer for use by a container. +// This requires both the id of the direct parent layer, as well as the full list +// of paths to all parent layers up to the base (and including the direct parent +// whose id was provided). +func CreateScratchLayer(path string, parentLayerPaths []string) (err error) { + title := "hcsshim::CreateScratchLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + // Generate layer descriptors + layers, err := layerPathsToDescriptors(parentLayerPaths) + if err != nil { + return err + } + + err = createSandboxLayer(&stdDriverInfo, path, 0, layers) + if err != nil { + return hcserror.New(err, title+" - failed", "") + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go new file mode 100644 index 000000000..2dd5d5715 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go @@ -0,0 +1,29 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// DeactivateLayer will dismount a layer that was mounted via ActivateLayer. +func DeactivateLayer(path string) (err error) { + title := "hcsshim::DeactivateLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + err = deactivateLayer(&stdDriverInfo, path) + if err != nil { + return hcserror.New(err, title+"- failed", "") + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go new file mode 100644 index 000000000..4da690c20 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go @@ -0,0 +1,30 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// DestroyLayer will remove the on-disk files representing the layer with the given +// path, including that layer's containing folder, if any. +func DestroyLayer(path string) (err error) { + title := "hcsshim::DestroyLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + err = destroyLayer(&stdDriverInfo, path) + if err != nil { + return hcserror.New(err, title+" - failed", "") + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go new file mode 100644 index 000000000..651676fb2 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go @@ -0,0 +1,30 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// ExpandScratchSize expands the size of a layer to at least size bytes. +func ExpandScratchSize(path string, size uint64) (err error) { + title := "hcsshim::ExpandScratchSize" + fields := logrus.Fields{ + "path": path, + "size": size, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + err = expandSandboxSize(&stdDriverInfo, path, size) + if err != nil { + return hcserror.New(err, title+" - failed", "") + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go new file mode 100644 index 000000000..0425b3395 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go @@ -0,0 +1,76 @@ +package wclayer + +import ( + "io/ioutil" + "os" + + "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// ExportLayer will create a folder at exportFolderPath and fill that folder with +// the transport format version of the layer identified by layerId. This transport +// format includes any metadata required for later importing the layer (using +// ImportLayer), and requires the full list of parent layer paths in order to +// perform the export. +func ExportLayer(path string, exportFolderPath string, parentLayerPaths []string) (err error) { + title := "hcsshim::ExportLayer" + fields := logrus.Fields{ + "path": path, + "exportFolderPath": exportFolderPath, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + // Generate layer descriptors + layers, err := layerPathsToDescriptors(parentLayerPaths) + if err != nil { + return err + } + + err = exportLayer(&stdDriverInfo, path, exportFolderPath, layers) + if err != nil { + return hcserror.New(err, title+" - failed", "") + } + return nil +} + +type LayerReader interface { + Next() (string, int64, *winio.FileBasicInfo, error) + Read(b []byte) (int, error) + Close() error +} + +// NewLayerReader returns a new layer reader for reading the contents of an on-disk layer. +// The caller must have taken the SeBackupPrivilege privilege +// to call this and any methods on the resulting LayerReader. +func NewLayerReader(path string, parentLayerPaths []string) (LayerReader, error) { + exportPath, err := ioutil.TempDir("", "hcs") + if err != nil { + return nil, err + } + err = ExportLayer(path, exportPath, parentLayerPaths) + if err != nil { + os.RemoveAll(exportPath) + return nil, err + } + return &legacyLayerReaderWrapper{newLegacyLayerReader(exportPath)}, nil +} + +type legacyLayerReaderWrapper struct { + *legacyLayerReader +} + +func (r *legacyLayerReaderWrapper) Close() error { + err := r.legacyLayerReader.Close() + os.RemoveAll(r.root) + return err +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go new file mode 100644 index 000000000..d60b6ed53 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go @@ -0,0 +1,56 @@ +package wclayer + +import ( + "syscall" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// GetLayerMountPath will look for a mounted layer with the given path and return +// the path at which that layer can be accessed. This path may be a volume path +// if the layer is a mounted read-write layer, otherwise it is expected to be the +// folder path at which the layer is stored. +func GetLayerMountPath(path string) (_ string, err error) { + title := "hcsshim::GetLayerMountPath" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + var mountPathLength uintptr + mountPathLength = 0 + + // Call the procedure itself. + logrus.WithFields(fields).Debug("Calling proc (1)") + err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, nil) + if err != nil { + return "", hcserror.New(err, title+" - failed", "(first call)") + } + + // Allocate a mount path of the returned length. + if mountPathLength == 0 { + return "", nil + } + mountPathp := make([]uint16, mountPathLength) + mountPathp[0] = 0 + + // Call the procedure again + logrus.WithFields(fields).Debug("Calling proc (2)") + err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, &mountPathp[0]) + if err != nil { + return "", hcserror.New(err, title+" - failed", "(second call)") + } + + mountPath := syscall.UTF16ToString(mountPathp[0:]) + fields["mountPath"] = mountPath + return mountPath, nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go new file mode 100644 index 000000000..dbd83ef2b --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go @@ -0,0 +1,29 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/sirupsen/logrus" +) + +// GetSharedBaseImages will enumerate the images stored in the common central +// image store and return descriptive info about those images for the purpose +// of registering them with the graphdriver, graph, and tagstore. +func GetSharedBaseImages() (imageData string, err error) { + title := "hcsshim::GetSharedBaseImages" + logrus.Debug(title) + defer func() { + if err != nil { + logrus.WithError(err).Error(err) + } else { + logrus.WithField("imageData", imageData).Debug(title + " - succeeded") + } + }() + + var buffer *uint16 + err = getBaseImages(&buffer) + if err != nil { + return "", hcserror.New(err, title+" - failed", "") + } + return interop.ConvertAndFreeCoTaskMemString(buffer), nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go new file mode 100644 index 000000000..05735df6c --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go @@ -0,0 +1,30 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// GrantVmAccess adds access to a file for a given VM +func GrantVmAccess(vmid string, filepath string) (err error) { + title := "hcsshim::GrantVmAccess" + fields := logrus.Fields{ + "vm-id": vmid, + "path": filepath, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + err = grantVmAccess(vmid, filepath) + if err != nil { + return hcserror.New(err, title+" - failed", "") + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go new file mode 100644 index 000000000..76a804f2a --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go @@ -0,0 +1,135 @@ +package wclayer + +import ( + "io/ioutil" + "os" + "path/filepath" + + "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/safefile" + "github.com/sirupsen/logrus" +) + +// ImportLayer will take the contents of the folder at importFolderPath and import +// that into a layer with the id layerId. Note that in order to correctly populate +// the layer and interperet the transport format, all parent layers must already +// be present on the system at the paths provided in parentLayerPaths. +func ImportLayer(path string, importFolderPath string, parentLayerPaths []string) (err error) { + title := "hcsshim::ImportLayer" + fields := logrus.Fields{ + "path": path, + "importFolderPath": importFolderPath, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + // Generate layer descriptors + layers, err := layerPathsToDescriptors(parentLayerPaths) + if err != nil { + return err + } + + err = importLayer(&stdDriverInfo, path, importFolderPath, layers) + if err != nil { + return hcserror.New(err, title+" - failed", "") + } + return nil +} + +// LayerWriter is an interface that supports writing a new container image layer. +type LayerWriter interface { + // Add adds a file to the layer with given metadata. + Add(name string, fileInfo *winio.FileBasicInfo) error + // AddLink adds a hard link to the layer. The target must already have been added. + AddLink(name string, target string) error + // Remove removes a file that was present in a parent layer from the layer. + Remove(name string) error + // Write writes data to the current file. The data must be in the format of a Win32 + // backup stream. + Write(b []byte) (int, error) + // Close finishes the layer writing process and releases any resources. + Close() error +} + +type legacyLayerWriterWrapper struct { + *legacyLayerWriter + path string + parentLayerPaths []string +} + +func (r *legacyLayerWriterWrapper) Close() error { + defer os.RemoveAll(r.root.Name()) + defer r.legacyLayerWriter.CloseRoots() + err := r.legacyLayerWriter.Close() + if err != nil { + return err + } + + if err = ImportLayer(r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil { + return err + } + for _, name := range r.Tombstones { + if err = safefile.RemoveRelative(name, r.destRoot); err != nil && !os.IsNotExist(err) { + return err + } + } + // Add any hard links that were collected. + for _, lnk := range r.PendingLinks { + if err = safefile.RemoveRelative(lnk.Path, r.destRoot); err != nil && !os.IsNotExist(err) { + return err + } + if err = safefile.LinkRelative(lnk.Target, lnk.TargetRoot, lnk.Path, r.destRoot); err != nil { + return err + } + } + // Prepare the utility VM for use if one is present in the layer. + if r.HasUtilityVM { + err := safefile.EnsureNotReparsePointRelative("UtilityVM", r.destRoot) + if err != nil { + return err + } + err = ProcessUtilityVMImage(filepath.Join(r.destRoot.Name(), "UtilityVM")) + if err != nil { + return err + } + } + return nil +} + +// NewLayerWriter returns a new layer writer for creating a layer on disk. +// The caller must have taken the SeBackupPrivilege and SeRestorePrivilege privileges +// to call this and any methods on the resulting LayerWriter. +func NewLayerWriter(path string, parentLayerPaths []string) (LayerWriter, error) { + if len(parentLayerPaths) == 0 { + // This is a base layer. It gets imported differently. + f, err := safefile.OpenRoot(path) + if err != nil { + return nil, err + } + return &baseLayerWriter{ + root: f, + }, nil + } + + importPath, err := ioutil.TempDir("", "hcs") + if err != nil { + return nil, err + } + w, err := newLegacyLayerWriter(importPath, parentLayerPaths, path) + if err != nil { + return nil, err + } + return &legacyLayerWriterWrapper{ + legacyLayerWriter: w, + path: importPath, + parentLayerPaths: parentLayerPaths, + }, nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go new file mode 100644 index 000000000..258167a57 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go @@ -0,0 +1,33 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// LayerExists will return true if a layer with the given id exists and is known +// to the system. +func LayerExists(path string) (_ bool, err error) { + title := "hcsshim::LayerExists" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + // Call the procedure itself. + var exists uint32 + err = layerExists(&stdDriverInfo, path, &exists) + if err != nil { + return false, hcserror.New(err, title+" - failed", "") + } + fields["layer-exists"] = exists != 0 + return exists != 0, nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go new file mode 100644 index 000000000..90df3bedc --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go @@ -0,0 +1,13 @@ +package wclayer + +import ( + "path/filepath" + + "github.com/Microsoft/hcsshim/internal/guid" +) + +// LayerID returns the layer ID of a layer on disk. +func LayerID(path string) (guid.GUID, error) { + _, file := filepath.Split(path) + return NameToGuid(file) +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go new file mode 100644 index 000000000..6d0ae8a07 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go @@ -0,0 +1,96 @@ +package wclayer + +// This file contains utility functions to support storage (graph) related +// functionality. + +import ( + "syscall" + + "github.com/Microsoft/hcsshim/internal/guid" + "github.com/sirupsen/logrus" +) + +/* To pass into syscall, we need a struct matching the following: +enum GraphDriverType +{ + DiffDriver, + FilterDriver +}; + +struct DriverInfo { + GraphDriverType Flavour; + LPCWSTR HomeDir; +}; +*/ + +type driverInfo struct { + Flavour int + HomeDirp *uint16 +} + +var ( + utf16EmptyString uint16 + stdDriverInfo = driverInfo{1, &utf16EmptyString} +) + +/* To pass into syscall, we need a struct matching the following: +typedef struct _WC_LAYER_DESCRIPTOR { + + // + // The ID of the layer + // + + GUID LayerId; + + // + // Additional flags + // + + union { + struct { + ULONG Reserved : 31; + ULONG Dirty : 1; // Created from sandbox as a result of snapshot + }; + ULONG Value; + } Flags; + + // + // Path to the layer root directory, null-terminated + // + + PCWSTR Path; + +} WC_LAYER_DESCRIPTOR, *PWC_LAYER_DESCRIPTOR; +*/ +type WC_LAYER_DESCRIPTOR struct { + LayerId guid.GUID + Flags uint32 + Pathp *uint16 +} + +func layerPathsToDescriptors(parentLayerPaths []string) ([]WC_LAYER_DESCRIPTOR, error) { + // Array of descriptors that gets constructed. + var layers []WC_LAYER_DESCRIPTOR + + for i := 0; i < len(parentLayerPaths); i++ { + g, err := LayerID(parentLayerPaths[i]) + if err != nil { + logrus.WithError(err).Debug("Failed to convert name to guid") + return nil, err + } + + p, err := syscall.UTF16PtrFromString(parentLayerPaths[i]) + if err != nil { + logrus.WithError(err).Debug("Failed conversion of parentLayerPath to pointer") + return nil, err + } + + layers = append(layers, WC_LAYER_DESCRIPTOR{ + LayerId: g, + Flags: 0, + Pathp: p, + }) + } + + return layers, nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go new file mode 100644 index 000000000..b8ea5d263 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go @@ -0,0 +1,815 @@ +package wclayer + +import ( + "bufio" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "syscall" + + "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/longpath" + "github.com/Microsoft/hcsshim/internal/safefile" +) + +var errorIterationCanceled = errors.New("") + +var mutatedUtilityVMFiles = map[string]bool{ + `EFI\Microsoft\Boot\BCD`: true, + `EFI\Microsoft\Boot\BCD.LOG`: true, + `EFI\Microsoft\Boot\BCD.LOG1`: true, + `EFI\Microsoft\Boot\BCD.LOG2`: true, +} + +const ( + filesPath = `Files` + hivesPath = `Hives` + utilityVMPath = `UtilityVM` + utilityVMFilesPath = `UtilityVM\Files` +) + +func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os.File, err error) { + return winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createDisposition) +} + +func hasPathPrefix(p, prefix string) bool { + return strings.HasPrefix(p, prefix) && len(p) > len(prefix) && p[len(prefix)] == '\\' +} + +type fileEntry struct { + path string + fi os.FileInfo + err error +} + +type legacyLayerReader struct { + root string + result chan *fileEntry + proceed chan bool + currentFile *os.File + backupReader *winio.BackupFileReader +} + +// newLegacyLayerReader returns a new LayerReader that can read the Windows +// container layer transport format from disk. +func newLegacyLayerReader(root string) *legacyLayerReader { + r := &legacyLayerReader{ + root: root, + result: make(chan *fileEntry), + proceed: make(chan bool), + } + go r.walk() + return r +} + +func readTombstones(path string) (map[string]([]string), error) { + tf, err := os.Open(filepath.Join(path, "tombstones.txt")) + if err != nil { + return nil, err + } + defer tf.Close() + s := bufio.NewScanner(tf) + if !s.Scan() || s.Text() != "\xef\xbb\xbfVersion 1.0" { + return nil, errors.New("Invalid tombstones file") + } + + ts := make(map[string]([]string)) + for s.Scan() { + t := filepath.Join(filesPath, s.Text()[1:]) // skip leading `\` + dir := filepath.Dir(t) + ts[dir] = append(ts[dir], t) + } + if err = s.Err(); err != nil { + return nil, err + } + + return ts, nil +} + +func (r *legacyLayerReader) walkUntilCancelled() error { + root, err := longpath.LongAbs(r.root) + if err != nil { + return err + } + + r.root = root + ts, err := readTombstones(r.root) + if err != nil { + return err + } + + err = filepath.Walk(r.root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // Indirect fix for https://github.com/moby/moby/issues/32838#issuecomment-343610048. + // Handle failure from what may be a golang bug in the conversion of + // UTF16 to UTF8 in files which are left in the recycle bin. Os.Lstat + // which is called by filepath.Walk will fail when a filename contains + // unicode characters. Skip the recycle bin regardless which is goodness. + if strings.EqualFold(path, filepath.Join(r.root, `Files\$Recycle.Bin`)) && info.IsDir() { + return filepath.SkipDir + } + + if path == r.root || path == filepath.Join(r.root, "tombstones.txt") || strings.HasSuffix(path, ".$wcidirs$") { + return nil + } + + r.result <- &fileEntry{path, info, nil} + if !<-r.proceed { + return errorIterationCanceled + } + + // List all the tombstones. + if info.IsDir() { + relPath, err := filepath.Rel(r.root, path) + if err != nil { + return err + } + if dts, ok := ts[relPath]; ok { + for _, t := range dts { + r.result <- &fileEntry{filepath.Join(r.root, t), nil, nil} + if !<-r.proceed { + return errorIterationCanceled + } + } + } + } + return nil + }) + if err == errorIterationCanceled { + return nil + } + if err == nil { + return io.EOF + } + return err +} + +func (r *legacyLayerReader) walk() { + defer close(r.result) + if !<-r.proceed { + return + } + + err := r.walkUntilCancelled() + if err != nil { + for { + r.result <- &fileEntry{err: err} + if !<-r.proceed { + return + } + } + } +} + +func (r *legacyLayerReader) reset() { + if r.backupReader != nil { + r.backupReader.Close() + r.backupReader = nil + } + if r.currentFile != nil { + r.currentFile.Close() + r.currentFile = nil + } +} + +func findBackupStreamSize(r io.Reader) (int64, error) { + br := winio.NewBackupStreamReader(r) + for { + hdr, err := br.Next() + if err != nil { + if err == io.EOF { + err = nil + } + return 0, err + } + if hdr.Id == winio.BackupData { + return hdr.Size, nil + } + } +} + +func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.FileBasicInfo, err error) { + r.reset() + r.proceed <- true + fe := <-r.result + if fe == nil { + err = errors.New("LegacyLayerReader closed") + return + } + if fe.err != nil { + err = fe.err + return + } + + path, err = filepath.Rel(r.root, fe.path) + if err != nil { + return + } + + if fe.fi == nil { + // This is a tombstone. Return a nil fileInfo. + return + } + + if fe.fi.IsDir() && hasPathPrefix(path, filesPath) { + fe.path += ".$wcidirs$" + } + + f, err := openFileOrDir(fe.path, syscall.GENERIC_READ, syscall.OPEN_EXISTING) + if err != nil { + return + } + defer func() { + if f != nil { + f.Close() + } + }() + + fileInfo, err = winio.GetFileBasicInfo(f) + if err != nil { + return + } + + if !hasPathPrefix(path, filesPath) { + size = fe.fi.Size() + r.backupReader = winio.NewBackupFileReader(f, false) + if path == hivesPath || path == filesPath { + // The Hives directory has a non-deterministic file time because of the + // nature of the import process. Use the times from System_Delta. + var g *os.File + g, err = os.Open(filepath.Join(r.root, hivesPath, `System_Delta`)) + if err != nil { + return + } + attr := fileInfo.FileAttributes + fileInfo, err = winio.GetFileBasicInfo(g) + g.Close() + if err != nil { + return + } + fileInfo.FileAttributes = attr + } + + // The creation time and access time get reset for files outside of the Files path. + fileInfo.CreationTime = fileInfo.LastWriteTime + fileInfo.LastAccessTime = fileInfo.LastWriteTime + + } else { + // The file attributes are written before the backup stream. + var attr uint32 + err = binary.Read(f, binary.LittleEndian, &attr) + if err != nil { + return + } + fileInfo.FileAttributes = attr + beginning := int64(4) + + // Find the accurate file size. + if !fe.fi.IsDir() { + size, err = findBackupStreamSize(f) + if err != nil { + err = &os.PathError{Op: "findBackupStreamSize", Path: fe.path, Err: err} + return + } + } + + // Return back to the beginning of the backup stream. + _, err = f.Seek(beginning, 0) + if err != nil { + return + } + } + + r.currentFile = f + f = nil + return +} + +func (r *legacyLayerReader) Read(b []byte) (int, error) { + if r.backupReader == nil { + if r.currentFile == nil { + return 0, io.EOF + } + return r.currentFile.Read(b) + } + return r.backupReader.Read(b) +} + +func (r *legacyLayerReader) Seek(offset int64, whence int) (int64, error) { + if r.backupReader == nil { + if r.currentFile == nil { + return 0, errors.New("no current file") + } + return r.currentFile.Seek(offset, whence) + } + return 0, errors.New("seek not supported on this stream") +} + +func (r *legacyLayerReader) Close() error { + r.proceed <- false + <-r.result + r.reset() + return nil +} + +type pendingLink struct { + Path, Target string + TargetRoot *os.File +} + +type pendingDir struct { + Path string + Root *os.File +} + +type legacyLayerWriter struct { + root *os.File + destRoot *os.File + parentRoots []*os.File + currentFile *os.File + bufWriter *bufio.Writer + currentFileName string + currentFileRoot *os.File + backupWriter *winio.BackupFileWriter + Tombstones []string + HasUtilityVM bool + uvmDi []dirInfo + addedFiles map[string]bool + PendingLinks []pendingLink + pendingDirs []pendingDir + currentIsDir bool +} + +// newLegacyLayerWriter returns a LayerWriter that can write the contaler layer +// transport format to disk. +func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) (w *legacyLayerWriter, err error) { + w = &legacyLayerWriter{ + addedFiles: make(map[string]bool), + } + defer func() { + if err != nil { + w.CloseRoots() + w = nil + } + }() + w.root, err = safefile.OpenRoot(root) + if err != nil { + return + } + w.destRoot, err = safefile.OpenRoot(destRoot) + if err != nil { + return + } + for _, r := range parentRoots { + f, err := safefile.OpenRoot(r) + if err != nil { + return w, err + } + w.parentRoots = append(w.parentRoots, f) + } + w.bufWriter = bufio.NewWriterSize(ioutil.Discard, 65536) + return +} + +func (w *legacyLayerWriter) CloseRoots() { + if w.root != nil { + w.root.Close() + w.root = nil + } + if w.destRoot != nil { + w.destRoot.Close() + w.destRoot = nil + } + for i := range w.parentRoots { + w.parentRoots[i].Close() + } + w.parentRoots = nil +} + +func (w *legacyLayerWriter) initUtilityVM() error { + if !w.HasUtilityVM { + err := safefile.MkdirRelative(utilityVMPath, w.destRoot) + if err != nil { + return err + } + // Server 2016 does not support multiple layers for the utility VM, so + // clone the utility VM from the parent layer into this layer. Use hard + // links to avoid unnecessary copying, since most of the files are + // immutable. + err = cloneTree(w.parentRoots[0], w.destRoot, utilityVMFilesPath, mutatedUtilityVMFiles) + if err != nil { + return fmt.Errorf("cloning the parent utility VM image failed: %s", err) + } + w.HasUtilityVM = true + } + return nil +} + +func (w *legacyLayerWriter) reset() error { + err := w.bufWriter.Flush() + if err != nil { + return err + } + w.bufWriter.Reset(ioutil.Discard) + if w.currentIsDir { + r := w.currentFile + br := winio.NewBackupStreamReader(r) + // Seek to the beginning of the backup stream, skipping the fileattrs + if _, err := r.Seek(4, io.SeekStart); err != nil { + return err + } + + for { + bhdr, err := br.Next() + if err == io.EOF { + // end of backupstream data + break + } + if err != nil { + return err + } + switch bhdr.Id { + case winio.BackupReparseData: + // The current file is a `.$wcidirs$` metadata file that + // describes a directory reparse point. Delete the placeholder + // directory to prevent future files being added into the + // destination of the reparse point during the ImportLayer call + if err := safefile.RemoveRelative(w.currentFileName, w.currentFileRoot); err != nil { + return err + } + w.pendingDirs = append(w.pendingDirs, pendingDir{Path: w.currentFileName, Root: w.currentFileRoot}) + default: + // ignore all other stream types, as we only care about directory reparse points + } + } + w.currentIsDir = false + } + if w.backupWriter != nil { + w.backupWriter.Close() + w.backupWriter = nil + } + if w.currentFile != nil { + w.currentFile.Close() + w.currentFile = nil + w.currentFileName = "" + w.currentFileRoot = nil + } + return nil +} + +// copyFileWithMetadata copies a file using the backup/restore APIs in order to preserve metadata +func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) { + src, err := safefile.OpenRelative( + subPath, + srcRoot, + syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY, + syscall.FILE_SHARE_READ, + safefile.FILE_OPEN, + safefile.FILE_OPEN_REPARSE_POINT) + if err != nil { + return nil, err + } + defer src.Close() + srcr := winio.NewBackupFileReader(src, true) + defer srcr.Close() + + fileInfo, err = winio.GetFileBasicInfo(src) + if err != nil { + return nil, err + } + + extraFlags := uint32(0) + if isDir { + extraFlags |= safefile.FILE_DIRECTORY_FILE + } + dest, err := safefile.OpenRelative( + subPath, + destRoot, + syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, + syscall.FILE_SHARE_READ, + safefile.FILE_CREATE, + extraFlags) + if err != nil { + return nil, err + } + defer dest.Close() + + err = winio.SetFileBasicInfo(dest, fileInfo) + if err != nil { + return nil, err + } + + destw := winio.NewBackupFileWriter(dest, true) + defer func() { + cerr := destw.Close() + if err == nil { + err = cerr + } + }() + + _, err = io.Copy(destw, srcr) + if err != nil { + return nil, err + } + + return fileInfo, nil +} + +// cloneTree clones a directory tree using hard links. It skips hard links for +// the file names in the provided map and just copies those files. +func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles map[string]bool) error { + var di []dirInfo + err := safefile.EnsureNotReparsePointRelative(subPath, srcRoot) + if err != nil { + return err + } + err = filepath.Walk(filepath.Join(srcRoot.Name(), subPath), func(srcFilePath string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + relPath, err := filepath.Rel(srcRoot.Name(), srcFilePath) + if err != nil { + return err + } + + fileAttributes := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes + // Directories, reparse points, and files that will be mutated during + // utility VM import must be copied. All other files can be hard linked. + isReparsePoint := fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 + // In go1.9, FileInfo.IsDir() returns false if the directory is also a symlink. + // See: https://github.com/golang/go/commit/1989921aef60c83e6f9127a8448fb5ede10e9acc + // Fixes the problem by checking syscall.FILE_ATTRIBUTE_DIRECTORY directly + isDir := fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 + + if isDir || isReparsePoint || mutatedFiles[relPath] { + fi, err := copyFileWithMetadata(srcRoot, destRoot, relPath, isDir) + if err != nil { + return err + } + if isDir && !isReparsePoint { + di = append(di, dirInfo{path: relPath, fileInfo: *fi}) + } + } else { + err = safefile.LinkRelative(relPath, srcRoot, relPath, destRoot) + if err != nil { + return err + } + } + + return nil + }) + if err != nil { + return err + } + + return reapplyDirectoryTimes(destRoot, di) +} + +func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error { + if err := w.reset(); err != nil { + return err + } + + if name == utilityVMPath { + return w.initUtilityVM() + } + + name = filepath.Clean(name) + if hasPathPrefix(name, utilityVMPath) { + if !w.HasUtilityVM { + return errors.New("missing UtilityVM directory") + } + if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath { + return errors.New("invalid UtilityVM layer") + } + createDisposition := uint32(safefile.FILE_OPEN) + if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { + st, err := safefile.LstatRelative(name, w.destRoot) + if err != nil && !os.IsNotExist(err) { + return err + } + if st != nil { + // Delete the existing file/directory if it is not the same type as this directory. + existingAttr := st.Sys().(*syscall.Win32FileAttributeData).FileAttributes + if (uint32(fileInfo.FileAttributes)^existingAttr)&(syscall.FILE_ATTRIBUTE_DIRECTORY|syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 { + if err = safefile.RemoveAllRelative(name, w.destRoot); err != nil { + return err + } + st = nil + } + } + if st == nil { + if err = safefile.MkdirRelative(name, w.destRoot); err != nil { + return err + } + } + if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + w.uvmDi = append(w.uvmDi, dirInfo{path: name, fileInfo: *fileInfo}) + } + } else { + // Overwrite any existing hard link. + err := safefile.RemoveRelative(name, w.destRoot) + if err != nil && !os.IsNotExist(err) { + return err + } + createDisposition = safefile.FILE_CREATE + } + + f, err := safefile.OpenRelative( + name, + w.destRoot, + syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, + syscall.FILE_SHARE_READ, + createDisposition, + safefile.FILE_OPEN_REPARSE_POINT, + ) + if err != nil { + return err + } + defer func() { + if f != nil { + f.Close() + safefile.RemoveRelative(name, w.destRoot) + } + }() + + err = winio.SetFileBasicInfo(f, fileInfo) + if err != nil { + return err + } + + w.backupWriter = winio.NewBackupFileWriter(f, true) + w.bufWriter.Reset(w.backupWriter) + w.currentFile = f + w.currentFileName = name + w.currentFileRoot = w.destRoot + w.addedFiles[name] = true + f = nil + return nil + } + + fname := name + if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { + err := safefile.MkdirRelative(name, w.root) + if err != nil { + return err + } + fname += ".$wcidirs$" + w.currentIsDir = true + } + + f, err := safefile.OpenRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, safefile.FILE_CREATE, 0) + if err != nil { + return err + } + defer func() { + if f != nil { + f.Close() + safefile.RemoveRelative(fname, w.root) + } + }() + + strippedFi := *fileInfo + strippedFi.FileAttributes = 0 + err = winio.SetFileBasicInfo(f, &strippedFi) + if err != nil { + return err + } + + if hasPathPrefix(name, hivesPath) { + w.backupWriter = winio.NewBackupFileWriter(f, false) + w.bufWriter.Reset(w.backupWriter) + } else { + w.bufWriter.Reset(f) + // The file attributes are written before the stream. + err = binary.Write(w.bufWriter, binary.LittleEndian, uint32(fileInfo.FileAttributes)) + if err != nil { + w.bufWriter.Reset(ioutil.Discard) + return err + } + } + + w.currentFile = f + w.currentFileName = name + w.currentFileRoot = w.root + w.addedFiles[name] = true + f = nil + return nil +} + +func (w *legacyLayerWriter) AddLink(name string, target string) error { + if err := w.reset(); err != nil { + return err + } + + target = filepath.Clean(target) + var roots []*os.File + if hasPathPrefix(target, filesPath) { + // Look for cross-layer hard link targets in the parent layers, since + // nothing is in the destination path yet. + roots = w.parentRoots + } else if hasPathPrefix(target, utilityVMFilesPath) { + // Since the utility VM is fully cloned into the destination path + // already, look for cross-layer hard link targets directly in the + // destination path. + roots = []*os.File{w.destRoot} + } + + if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) { + return errors.New("invalid hard link in layer") + } + + // Find to try the target of the link in a previously added file. If that + // fails, search in parent layers. + var selectedRoot *os.File + if _, ok := w.addedFiles[target]; ok { + selectedRoot = w.destRoot + } else { + for _, r := range roots { + if _, err := safefile.LstatRelative(target, r); err != nil { + if !os.IsNotExist(err) { + return err + } + } else { + selectedRoot = r + break + } + } + if selectedRoot == nil { + return fmt.Errorf("failed to find link target for '%s' -> '%s'", name, target) + } + } + + // The link can't be written until after the ImportLayer call. + w.PendingLinks = append(w.PendingLinks, pendingLink{ + Path: name, + Target: target, + TargetRoot: selectedRoot, + }) + w.addedFiles[name] = true + return nil +} + +func (w *legacyLayerWriter) Remove(name string) error { + name = filepath.Clean(name) + if hasPathPrefix(name, filesPath) { + w.Tombstones = append(w.Tombstones, name) + } else if hasPathPrefix(name, utilityVMFilesPath) { + err := w.initUtilityVM() + if err != nil { + return err + } + // Make sure the path exists; os.RemoveAll will not fail if the file is + // already gone, and this needs to be a fatal error for diagnostics + // purposes. + if _, err := safefile.LstatRelative(name, w.destRoot); err != nil { + return err + } + err = safefile.RemoveAllRelative(name, w.destRoot) + if err != nil { + return err + } + } else { + return fmt.Errorf("invalid tombstone %s", name) + } + + return nil +} + +func (w *legacyLayerWriter) Write(b []byte) (int, error) { + if w.backupWriter == nil && w.currentFile == nil { + return 0, errors.New("closed") + } + return w.bufWriter.Write(b) +} + +func (w *legacyLayerWriter) Close() error { + if err := w.reset(); err != nil { + return err + } + if err := safefile.RemoveRelative("tombstones.txt", w.root); err != nil && !os.IsNotExist(err) { + return err + } + for _, pd := range w.pendingDirs { + err := safefile.MkdirRelative(pd.Path, pd.Root) + if err != nil { + return err + } + } + if w.HasUtilityVM { + err := reapplyDirectoryTimes(w.destRoot, w.uvmDi) + if err != nil { + return err + } + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go new file mode 100644 index 000000000..45a63cf65 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go @@ -0,0 +1,34 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/guid" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// NameToGuid converts the given string into a GUID using the algorithm in the +// Host Compute Service, ensuring GUIDs generated with the same string are common +// across all clients. +func NameToGuid(name string) (id guid.GUID, err error) { + title := "hcsshim::NameToGuid" + fields := logrus.Fields{ + "name": name, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + err = nameToGuid(name, &id) + if err != nil { + err = hcserror.New(err, title+" - failed", "") + return + } + fields["guid"] = id.String() + return +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go new file mode 100644 index 000000000..2b65b0186 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go @@ -0,0 +1,47 @@ +package wclayer + +import ( + "sync" + + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +var prepareLayerLock sync.Mutex + +// PrepareLayer finds a mounted read-write layer matching path and enables the +// the filesystem filter for use on that layer. This requires the paths to all +// parent layers, and is necessary in order to view or interact with the layer +// as an actual filesystem (reading and writing files, creating directories, etc). +// Disabling the filter must be done via UnprepareLayer. +func PrepareLayer(path string, parentLayerPaths []string) (err error) { + title := "hcsshim::PrepareLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + // Generate layer descriptors + layers, err := layerPathsToDescriptors(parentLayerPaths) + if err != nil { + return err + } + + // This lock is a temporary workaround for a Windows bug. Only allowing one + // call to prepareLayer at a time vastly reduces the chance of a timeout. + prepareLayerLock.Lock() + defer prepareLayerLock.Unlock() + err = prepareLayer(&stdDriverInfo, path, layers) + if err != nil { + return hcserror.New(err, title+" - failed", "") + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go new file mode 100644 index 000000000..884207c3e --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go @@ -0,0 +1,23 @@ +package wclayer + +import "os" + +// ProcessBaseLayer post-processes a base layer that has had its files extracted. +// The files should have been extracted to \Files. +func ProcessBaseLayer(path string) error { + err := processBaseImage(path) + if err != nil { + return &os.PathError{Op: "ProcessBaseLayer", Path: path, Err: err} + } + return nil +} + +// ProcessUtilityVMImage post-processes a utility VM image that has had its files extracted. +// The files should have been extracted to \Files. +func ProcessUtilityVMImage(path string) error { + err := processUtilityImage(path) + if err != nil { + return &os.PathError{Op: "ProcessUtilityVMImage", Path: path, Err: err} + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go new file mode 100644 index 000000000..bccd45969 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go @@ -0,0 +1,30 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// UnprepareLayer disables the filesystem filter for the read-write layer with +// the given id. +func UnprepareLayer(path string) (err error) { + title := "hcsshim::UnprepareLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() + + err = unprepareLayer(&stdDriverInfo, path) + if err != nil { + return hcserror.New(err, title+" - failed", "") + } + return nil +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go new file mode 100644 index 000000000..78f2aacd8 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go @@ -0,0 +1,27 @@ +package wclayer + +import "github.com/Microsoft/hcsshim/internal/guid" + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go wclayer.go + +//sys activateLayer(info *driverInfo, id string) (hr error) = vmcompute.ActivateLayer? +//sys copyLayer(info *driverInfo, srcId string, dstId string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.CopyLayer? +//sys createLayer(info *driverInfo, id string, parent string) (hr error) = vmcompute.CreateLayer? +//sys createSandboxLayer(info *driverInfo, id string, parent uintptr, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.CreateSandboxLayer? +//sys expandSandboxSize(info *driverInfo, id string, size uint64) (hr error) = vmcompute.ExpandSandboxSize? +//sys deactivateLayer(info *driverInfo, id string) (hr error) = vmcompute.DeactivateLayer? +//sys destroyLayer(info *driverInfo, id string) (hr error) = vmcompute.DestroyLayer? +//sys exportLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.ExportLayer? +//sys getLayerMountPath(info *driverInfo, id string, length *uintptr, buffer *uint16) (hr error) = vmcompute.GetLayerMountPath? +//sys getBaseImages(buffer **uint16) (hr error) = vmcompute.GetBaseImages? +//sys importLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.ImportLayer? +//sys layerExists(info *driverInfo, id string, exists *uint32) (hr error) = vmcompute.LayerExists? +//sys nameToGuid(name string, guid *_guid) (hr error) = vmcompute.NameToGuid? +//sys prepareLayer(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.PrepareLayer? +//sys unprepareLayer(info *driverInfo, id string) (hr error) = vmcompute.UnprepareLayer? +//sys processBaseImage(path string) (hr error) = vmcompute.ProcessBaseImage? +//sys processUtilityImage(path string) (hr error) = vmcompute.ProcessUtilityImage? + +//sys grantVmAccess(vmid string, filepath string) (hr error) = vmcompute.GrantVmAccess? + +type _guid = guid.GUID diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go new file mode 100644 index 000000000..d853ab259 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go @@ -0,0 +1,510 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package wclayer + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") + + procActivateLayer = modvmcompute.NewProc("ActivateLayer") + procCopyLayer = modvmcompute.NewProc("CopyLayer") + procCreateLayer = modvmcompute.NewProc("CreateLayer") + procCreateSandboxLayer = modvmcompute.NewProc("CreateSandboxLayer") + procExpandSandboxSize = modvmcompute.NewProc("ExpandSandboxSize") + procDeactivateLayer = modvmcompute.NewProc("DeactivateLayer") + procDestroyLayer = modvmcompute.NewProc("DestroyLayer") + procExportLayer = modvmcompute.NewProc("ExportLayer") + procGetLayerMountPath = modvmcompute.NewProc("GetLayerMountPath") + procGetBaseImages = modvmcompute.NewProc("GetBaseImages") + procImportLayer = modvmcompute.NewProc("ImportLayer") + procLayerExists = modvmcompute.NewProc("LayerExists") + procNameToGuid = modvmcompute.NewProc("NameToGuid") + procPrepareLayer = modvmcompute.NewProc("PrepareLayer") + procUnprepareLayer = modvmcompute.NewProc("UnprepareLayer") + procProcessBaseImage = modvmcompute.NewProc("ProcessBaseImage") + procProcessUtilityImage = modvmcompute.NewProc("ProcessUtilityImage") + procGrantVmAccess = modvmcompute.NewProc("GrantVmAccess") +) + +func activateLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _activateLayer(info, _p0) +} + +func _activateLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procActivateLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procActivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func copyLayer(info *driverInfo, srcId string, dstId string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(srcId) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(dstId) + if hr != nil { + return + } + return _copyLayer(info, _p0, _p1, descriptors) +} + +func _copyLayer(info *driverInfo, srcId *uint16, dstId *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p2 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p2 = &descriptors[0] + } + if hr = procCopyLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procCopyLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(srcId)), uintptr(unsafe.Pointer(dstId)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func createLayer(info *driverInfo, id string, parent string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(parent) + if hr != nil { + return + } + return _createLayer(info, _p0, _p1) +} + +func _createLayer(info *driverInfo, id *uint16, parent *uint16) (hr error) { + if hr = procCreateLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procCreateLayer.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(parent))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func createSandboxLayer(info *driverInfo, id string, parent uintptr, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _createSandboxLayer(info, _p0, parent, descriptors) +} + +func _createSandboxLayer(info *driverInfo, id *uint16, parent uintptr, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p1 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p1 = &descriptors[0] + } + if hr = procCreateSandboxLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procCreateSandboxLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(parent), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func expandSandboxSize(info *driverInfo, id string, size uint64) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _expandSandboxSize(info, _p0, size) +} + +func _expandSandboxSize(info *driverInfo, id *uint16, size uint64) (hr error) { + if hr = procExpandSandboxSize.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procExpandSandboxSize.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(size)) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func deactivateLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _deactivateLayer(info, _p0) +} + +func _deactivateLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procDeactivateLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procDeactivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func destroyLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _destroyLayer(info, _p0) +} + +func _destroyLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procDestroyLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procDestroyLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func exportLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _exportLayer(info, _p0, _p1, descriptors) +} + +func _exportLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p2 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p2 = &descriptors[0] + } + if hr = procExportLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procExportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func getLayerMountPath(info *driverInfo, id string, length *uintptr, buffer *uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _getLayerMountPath(info, _p0, length, buffer) +} + +func _getLayerMountPath(info *driverInfo, id *uint16, length *uintptr, buffer *uint16) (hr error) { + if hr = procGetLayerMountPath.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procGetLayerMountPath.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(length)), uintptr(unsafe.Pointer(buffer)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func getBaseImages(buffer **uint16) (hr error) { + if hr = procGetBaseImages.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procGetBaseImages.Addr(), 1, uintptr(unsafe.Pointer(buffer)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func importLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _importLayer(info, _p0, _p1, descriptors) +} + +func _importLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p2 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p2 = &descriptors[0] + } + if hr = procImportLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procImportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func layerExists(info *driverInfo, id string, exists *uint32) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _layerExists(info, _p0, exists) +} + +func _layerExists(info *driverInfo, id *uint16, exists *uint32) (hr error) { + if hr = procLayerExists.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procLayerExists.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(exists))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func nameToGuid(name string, guid *_guid) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(name) + if hr != nil { + return + } + return _nameToGuid(_p0, guid) +} + +func _nameToGuid(name *uint16, guid *_guid) (hr error) { + if hr = procNameToGuid.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procNameToGuid.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(guid)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func prepareLayer(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _prepareLayer(info, _p0, descriptors) +} + +func _prepareLayer(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p1 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p1 = &descriptors[0] + } + if hr = procPrepareLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procPrepareLayer.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func unprepareLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _unprepareLayer(info, _p0) +} + +func _unprepareLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procUnprepareLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procUnprepareLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func processBaseImage(path string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _processBaseImage(_p0) +} + +func _processBaseImage(path *uint16) (hr error) { + if hr = procProcessBaseImage.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procProcessBaseImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func processUtilityImage(path string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _processUtilityImage(_p0) +} + +func _processUtilityImage(path *uint16) (hr error) { + if hr = procProcessUtilityImage.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procProcessUtilityImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func grantVmAccess(vmid string, filepath string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(vmid) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(filepath) + if hr != nil { + return + } + return _grantVmAccess(_p0, _p1) +} + +func _grantVmAccess(vmid *uint16, filepath *uint16) (hr error) { + if hr = procGrantVmAccess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procGrantVmAccess.Addr(), 2, uintptr(unsafe.Pointer(vmid)), uintptr(unsafe.Pointer(filepath)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/layer.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/layer.go new file mode 100644 index 000000000..df0e63bbd --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/layer.go @@ -0,0 +1,106 @@ +package hcsshim + +import ( + "crypto/sha1" + "path/filepath" + + "github.com/Microsoft/hcsshim/internal/guid" + "github.com/Microsoft/hcsshim/internal/wclayer" +) + +func layerPath(info *DriverInfo, id string) string { + return filepath.Join(info.HomeDir, id) +} + +func ActivateLayer(info DriverInfo, id string) error { + return wclayer.ActivateLayer(layerPath(&info, id)) +} +func CreateLayer(info DriverInfo, id, parent string) error { + return wclayer.CreateLayer(layerPath(&info, id), parent) +} + +// New clients should use CreateScratchLayer instead. Kept in to preserve API compatibility. +func CreateSandboxLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error { + return wclayer.CreateScratchLayer(layerPath(&info, layerId), parentLayerPaths) +} +func CreateScratchLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error { + return wclayer.CreateScratchLayer(layerPath(&info, layerId), parentLayerPaths) +} +func DeactivateLayer(info DriverInfo, id string) error { + return wclayer.DeactivateLayer(layerPath(&info, id)) +} +func DestroyLayer(info DriverInfo, id string) error { + return wclayer.DestroyLayer(layerPath(&info, id)) +} + +// New clients should use ExpandScratchSize instead. Kept in to preserve API compatibility. +func ExpandSandboxSize(info DriverInfo, layerId string, size uint64) error { + return wclayer.ExpandScratchSize(layerPath(&info, layerId), size) +} +func ExpandScratchSize(info DriverInfo, layerId string, size uint64) error { + return wclayer.ExpandScratchSize(layerPath(&info, layerId), size) +} +func ExportLayer(info DriverInfo, layerId string, exportFolderPath string, parentLayerPaths []string) error { + return wclayer.ExportLayer(layerPath(&info, layerId), exportFolderPath, parentLayerPaths) +} +func GetLayerMountPath(info DriverInfo, id string) (string, error) { + return wclayer.GetLayerMountPath(layerPath(&info, id)) +} +func GetSharedBaseImages() (imageData string, err error) { + return wclayer.GetSharedBaseImages() +} +func ImportLayer(info DriverInfo, layerID string, importFolderPath string, parentLayerPaths []string) error { + return wclayer.ImportLayer(layerPath(&info, layerID), importFolderPath, parentLayerPaths) +} +func LayerExists(info DriverInfo, id string) (bool, error) { + return wclayer.LayerExists(layerPath(&info, id)) +} +func PrepareLayer(info DriverInfo, layerId string, parentLayerPaths []string) error { + return wclayer.PrepareLayer(layerPath(&info, layerId), parentLayerPaths) +} +func ProcessBaseLayer(path string) error { + return wclayer.ProcessBaseLayer(path) +} +func ProcessUtilityVMImage(path string) error { + return wclayer.ProcessUtilityVMImage(path) +} +func UnprepareLayer(info DriverInfo, layerId string) error { + return wclayer.UnprepareLayer(layerPath(&info, layerId)) +} + +type DriverInfo struct { + Flavour int + HomeDir string +} + +type GUID [16]byte + +func NameToGuid(name string) (id GUID, err error) { + g, err := wclayer.NameToGuid(name) + return GUID(g), err +} + +func NewGUID(source string) *GUID { + h := sha1.Sum([]byte(source)) + var g GUID + copy(g[0:], h[0:16]) + return &g +} + +func (g *GUID) ToString() string { + return (guid.GUID)(*g).String() +} + +type LayerReader = wclayer.LayerReader + +func NewLayerReader(info DriverInfo, layerID string, parentLayerPaths []string) (LayerReader, error) { + return wclayer.NewLayerReader(layerPath(&info, layerID), parentLayerPaths) +} + +type LayerWriter = wclayer.LayerWriter + +func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) { + return wclayer.NewLayerWriter(layerPath(&info, layerID), parentLayerPaths) +} + +type WC_LAYER_DESCRIPTOR = wclayer.WC_LAYER_DESCRIPTOR diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/process.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/process.go new file mode 100644 index 000000000..ca8acbb7c --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/process.go @@ -0,0 +1,72 @@ +package hcsshim + +import ( + "io" + "time" + + "github.com/Microsoft/hcsshim/internal/hcs" +) + +// ContainerError is an error encountered in HCS +type process struct { + p *hcs.Process +} + +// Pid returns the process ID of the process within the container. +func (process *process) Pid() int { + return process.p.Pid() +} + +// Kill signals the process to terminate but does not wait for it to finish terminating. +func (process *process) Kill() error { + return convertProcessError(process.p.Kill(), process) +} + +// Wait waits for the process to exit. +func (process *process) Wait() error { + return convertProcessError(process.p.Wait(), process) +} + +// WaitTimeout waits for the process to exit or the duration to elapse. It returns +// false if timeout occurs. +func (process *process) WaitTimeout(timeout time.Duration) error { + return convertProcessError(process.p.WaitTimeout(timeout), process) +} + +// ExitCode returns the exit code of the process. The process must have +// already terminated. +func (process *process) ExitCode() (int, error) { + code, err := process.p.ExitCode() + if err != nil { + err = convertProcessError(err, process) + } + return code, err +} + +// ResizeConsole resizes the console of the process. +func (process *process) ResizeConsole(width, height uint16) error { + return convertProcessError(process.p.ResizeConsole(width, height), process) +} + +// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing +// these pipes does not close the underlying pipes; it should be possible to +// call this multiple times to get multiple interfaces. +func (process *process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) { + stdin, stdout, stderr, err := process.p.Stdio() + if err != nil { + err = convertProcessError(err, process) + } + return stdin, stdout, stderr, err +} + +// CloseStdin closes the write side of the stdin pipe so that the process is +// notified on the read side that there is no more data in stdin. +func (process *process) CloseStdin() error { + return convertProcessError(process.p.CloseStdin(), process) +} + +// Close cleans up any state associated with the process but does not kill +// or wait on it. +func (process *process) Close() error { + return convertProcessError(process.p.Close(), process) +} diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/vendor.conf b/src/runtime/vendor/github.com/Microsoft/hcsshim/vendor.conf new file mode 100644 index 000000000..6e0ed1566 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/vendor.conf @@ -0,0 +1,21 @@ +github.com/blang/semver v3.1.0 +github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 +github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 +github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55 +github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f +github.com/konsorten/go-windows-terminal-sequences v1.0.1 +github.com/linuxkit/virtsock 8e79449dea0735c1c056d814934dd035734cc97c +github.com/Microsoft/go-winio 16cfc975803886a5e47c4257a24c8d8c52e178b2 +github.com/Microsoft/opengcs v0.3.9 +github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 +github.com/opencontainers/runtime-tools 1d69bd0f9c39677d0630e50664fbc3154ae61b88 +github.com/pkg/errors v0.8.1 +github.com/sirupsen/logrus v1.3.0 +github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 +github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c +github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6 +github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b +github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874 +golang.org/x/crypto ff983b9c42bc9fbf91556e191cc8efb585c16908 +golang.org/x/sync 37e7f081c4d4c64e13b10787722085407fe5d15f +golang.org/x/sys e5ecc2a6747ce8d4af18ed98b3de5ae30eb3a5bb \ No newline at end of file diff --git a/src/runtime/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go b/src/runtime/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go new file mode 100644 index 000000000..8bed84857 --- /dev/null +++ b/src/runtime/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go @@ -0,0 +1,54 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package hcsshim + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") + + procSetCurrentThreadCompartmentId = modiphlpapi.NewProc("SetCurrentThreadCompartmentId") +) + +func SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) { + r0, _, _ := syscall.Syscall(procSetCurrentThreadCompartmentId.Addr(), 1, uintptr(compartmentId), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} diff --git a/src/runtime/vendor/github.com/PuerkitoBio/purell/.gitignore b/src/runtime/vendor/github.com/PuerkitoBio/purell/.gitignore new file mode 100644 index 000000000..748e4c807 --- /dev/null +++ b/src/runtime/vendor/github.com/PuerkitoBio/purell/.gitignore @@ -0,0 +1,5 @@ +*.sublime-* +.DS_Store +*.swp +*.swo +tags diff --git a/src/runtime/vendor/github.com/PuerkitoBio/purell/.travis.yml b/src/runtime/vendor/github.com/PuerkitoBio/purell/.travis.yml new file mode 100644 index 000000000..facfc91c6 --- /dev/null +++ b/src/runtime/vendor/github.com/PuerkitoBio/purell/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - 1.4 + - 1.5 + - 1.6 + - tip diff --git a/src/runtime/vendor/github.com/PuerkitoBio/purell/LICENSE b/src/runtime/vendor/github.com/PuerkitoBio/purell/LICENSE new file mode 100644 index 000000000..4b9986dea --- /dev/null +++ b/src/runtime/vendor/github.com/PuerkitoBio/purell/LICENSE @@ -0,0 +1,12 @@ +Copyright (c) 2012, Martin Angers +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/runtime/vendor/github.com/PuerkitoBio/purell/README.md b/src/runtime/vendor/github.com/PuerkitoBio/purell/README.md new file mode 100644 index 000000000..09e8a32cb --- /dev/null +++ b/src/runtime/vendor/github.com/PuerkitoBio/purell/README.md @@ -0,0 +1,187 @@ +# Purell + +Purell is a tiny Go library to normalize URLs. It returns a pure URL. Pure-ell. Sanitizer and all. Yeah, I know... + +Based on the [wikipedia paper][wiki] and the [RFC 3986 document][rfc]. + +[![build status](https://secure.travis-ci.org/PuerkitoBio/purell.png)](http://travis-ci.org/PuerkitoBio/purell) + +## Install + +`go get github.com/PuerkitoBio/purell` + +## Changelog + +* **2016-11-14 (v1.1.0)** : IDN: Conform to RFC 5895: Fold character width (thanks to @beeker1121). +* **2016-07-27 (v1.0.0)** : Normalize IDN to ASCII (thanks to @zenovich). +* **2015-02-08** : Add fix for relative paths issue ([PR #5][pr5]) and add fix for unnecessary encoding of reserved characters ([see issue #7][iss7]). +* **v0.2.0** : Add benchmarks, Attempt IDN support. +* **v0.1.0** : Initial release. + +## Examples + +From `example_test.go` (note that in your code, you would import "github.com/PuerkitoBio/purell", and would prefix references to its methods and constants with "purell."): + +```go +package purell + +import ( + "fmt" + "net/url" +) + +func ExampleNormalizeURLString() { + if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/", + FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil { + panic(err) + } else { + fmt.Print(normalized) + } + // Output: http://somewebsite.com:80/Amazing%3F/url/ +} + +func ExampleMustNormalizeURLString() { + normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/", + FlagsUnsafeGreedy) + fmt.Print(normalized) + + // Output: http://somewebsite.com/Amazing%FA/url +} + +func ExampleNormalizeURL() { + if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil { + panic(err) + } else { + normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment) + fmt.Print(normalized) + } + + // Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0 +} +``` + +## API + +As seen in the examples above, purell offers three methods, `NormalizeURLString(string, NormalizationFlags) (string, error)`, `MustNormalizeURLString(string, NormalizationFlags) (string)` and `NormalizeURL(*url.URL, NormalizationFlags) (string)`. They all normalize the provided URL based on the specified flags. Here are the available flags: + +```go +const ( + // Safe normalizations + FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1 + FlagLowercaseHost // http://HOST -> http://host + FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF + FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA + FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$ + FlagRemoveDefaultPort // http://host:80 -> http://host + FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path + + // Usually safe normalizations + FlagRemoveTrailingSlash // http://host/path/ -> http://host/path + FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags) + FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c + + // Unsafe normalizations + FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/ + FlagRemoveFragment // http://host/path#fragment -> http://host/path + FlagForceHTTP // https://host -> http://host + FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b + FlagRemoveWWW // http://www.host/ -> http://host/ + FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags) + FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3 + + // Normalizations not in the wikipedia article, required to cover tests cases + // submitted by jehiah + FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147 + FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147 + FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147 + FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path + FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path + + // Convenience set of safe normalizations + FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator + + // For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags, + // while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix". + + // Convenience set of usually safe normalizations (includes FlagsSafe) + FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments + FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments + + // Convenience set of unsafe normalizations (includes FlagsUsuallySafe) + FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery + FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery + + // Convenience set of all available flags + FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator + FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator +) +``` + +For convenience, the set of flags `FlagsSafe`, `FlagsUsuallySafe[Greedy|NonGreedy]`, `FlagsUnsafe[Greedy|NonGreedy]` and `FlagsAll[Greedy|NonGreedy]` are provided for the similarly grouped normalizations on [wikipedia's URL normalization page][wiki]. You can add (using the bitwise OR `|` operator) or remove (using the bitwise AND NOT `&^` operator) individual flags from the sets if required, to build your own custom set. + +The [full godoc reference is available on gopkgdoc][godoc]. + +Some things to note: + +* `FlagDecodeUnnecessaryEscapes`, `FlagEncodeNecessaryEscapes`, `FlagUppercaseEscapes` and `FlagRemoveEmptyQuerySeparator` are always implicitly set, because internally, the URL string is parsed as an URL object, which automatically decodes unnecessary escapes, uppercases and encodes necessary ones, and removes empty query separators (an unnecessary `?` at the end of the url). So this operation cannot **not** be done. For this reason, `FlagRemoveEmptyQuerySeparator` (as well as the other three) has been included in the `FlagsSafe` convenience set, instead of `FlagsUnsafe`, where Wikipedia puts it. + +* The `FlagDecodeUnnecessaryEscapes` decodes the following escapes (*from -> to*): + - %24 -> $ + - %26 -> & + - %2B-%3B -> +,-./0123456789:; + - %3D -> = + - %40-%5A -> @ABCDEFGHIJKLMNOPQRSTUVWXYZ + - %5F -> _ + - %61-%7A -> abcdefghijklmnopqrstuvwxyz + - %7E -> ~ + + +* When the `NormalizeURL` function is used (passing an URL object), this source URL object is modified (that is, after the call, the URL object will be modified to reflect the normalization). + +* The *replace IP with domain name* normalization (`http://208.77.188.166/ → http://www.example.com/`) is obviously not possible for a library without making some network requests. This is not implemented in purell. + +* The *remove unused query string parameters* and *remove default query parameters* are also not implemented, since this is a very case-specific normalization, and it is quite trivial to do with an URL object. + +### Safe vs Usually Safe vs Unsafe + +Purell allows you to control the level of risk you take while normalizing an URL. You can aggressively normalize, play it totally safe, or anything in between. + +Consider the following URL: + +`HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid` + +Normalizing with the `FlagsSafe` gives: + +`https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid` + +With the `FlagsUsuallySafeGreedy`: + +`https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid` + +And with `FlagsUnsafeGreedy`: + +`http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3` + +## TODOs + +* Add a class/default instance to allow specifying custom directory index names? At the moment, removing directory index removes `(^|/)((?:default|index)\.\w{1,4})$`. + +## Thanks / Contributions + +@rogpeppe +@jehiah +@opennota +@pchristopher1275 +@zenovich +@beeker1121 + +## License + +The [BSD 3-Clause license][bsd]. + +[bsd]: http://opensource.org/licenses/BSD-3-Clause +[wiki]: http://en.wikipedia.org/wiki/URL_normalization +[rfc]: http://tools.ietf.org/html/rfc3986#section-6 +[godoc]: http://go.pkgdoc.org/github.com/PuerkitoBio/purell +[pr5]: https://github.com/PuerkitoBio/purell/pull/5 +[iss7]: https://github.com/PuerkitoBio/purell/issues/7 diff --git a/src/runtime/vendor/github.com/PuerkitoBio/purell/purell.go b/src/runtime/vendor/github.com/PuerkitoBio/purell/purell.go new file mode 100644 index 000000000..645e1b76f --- /dev/null +++ b/src/runtime/vendor/github.com/PuerkitoBio/purell/purell.go @@ -0,0 +1,379 @@ +/* +Package purell offers URL normalization as described on the wikipedia page: +http://en.wikipedia.org/wiki/URL_normalization +*/ +package purell + +import ( + "bytes" + "fmt" + "net/url" + "regexp" + "sort" + "strconv" + "strings" + + "github.com/PuerkitoBio/urlesc" + "golang.org/x/net/idna" + "golang.org/x/text/unicode/norm" + "golang.org/x/text/width" +) + +// A set of normalization flags determines how a URL will +// be normalized. +type NormalizationFlags uint + +const ( + // Safe normalizations + FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1 + FlagLowercaseHost // http://HOST -> http://host + FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF + FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA + FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$ + FlagRemoveDefaultPort // http://host:80 -> http://host + FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path + + // Usually safe normalizations + FlagRemoveTrailingSlash // http://host/path/ -> http://host/path + FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags) + FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c + + // Unsafe normalizations + FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/ + FlagRemoveFragment // http://host/path#fragment -> http://host/path + FlagForceHTTP // https://host -> http://host + FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b + FlagRemoveWWW // http://www.host/ -> http://host/ + FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags) + FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3 + + // Normalizations not in the wikipedia article, required to cover tests cases + // submitted by jehiah + FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147 + FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147 + FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147 + FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path + FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path + + // Convenience set of safe normalizations + FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator + + // For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags, + // while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix". + + // Convenience set of usually safe normalizations (includes FlagsSafe) + FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments + FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments + + // Convenience set of unsafe normalizations (includes FlagsUsuallySafe) + FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery + FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery + + // Convenience set of all available flags + FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator + FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator +) + +const ( + defaultHttpPort = ":80" + defaultHttpsPort = ":443" +) + +// Regular expressions used by the normalizations +var rxPort = regexp.MustCompile(`(:\d+)/?$`) +var rxDirIndex = regexp.MustCompile(`(^|/)((?:default|index)\.\w{1,4})$`) +var rxDupSlashes = regexp.MustCompile(`/{2,}`) +var rxDWORDHost = regexp.MustCompile(`^(\d+)((?:\.+)?(?:\:\d*)?)$`) +var rxOctalHost = regexp.MustCompile(`^(0\d*)\.(0\d*)\.(0\d*)\.(0\d*)((?:\.+)?(?:\:\d*)?)$`) +var rxHexHost = regexp.MustCompile(`^0x([0-9A-Fa-f]+)((?:\.+)?(?:\:\d*)?)$`) +var rxHostDots = regexp.MustCompile(`^(.+?)(:\d+)?$`) +var rxEmptyPort = regexp.MustCompile(`:+$`) + +// Map of flags to implementation function. +// FlagDecodeUnnecessaryEscapes has no action, since it is done automatically +// by parsing the string as an URL. Same for FlagUppercaseEscapes and FlagRemoveEmptyQuerySeparator. + +// Since maps have undefined traversing order, make a slice of ordered keys +var flagsOrder = []NormalizationFlags{ + FlagLowercaseScheme, + FlagLowercaseHost, + FlagRemoveDefaultPort, + FlagRemoveDirectoryIndex, + FlagRemoveDotSegments, + FlagRemoveFragment, + FlagForceHTTP, // Must be after remove default port (because https=443/http=80) + FlagRemoveDuplicateSlashes, + FlagRemoveWWW, + FlagAddWWW, + FlagSortQuery, + FlagDecodeDWORDHost, + FlagDecodeOctalHost, + FlagDecodeHexHost, + FlagRemoveUnnecessaryHostDots, + FlagRemoveEmptyPortSeparator, + FlagRemoveTrailingSlash, // These two (add/remove trailing slash) must be last + FlagAddTrailingSlash, +} + +// ... and then the map, where order is unimportant +var flags = map[NormalizationFlags]func(*url.URL){ + FlagLowercaseScheme: lowercaseScheme, + FlagLowercaseHost: lowercaseHost, + FlagRemoveDefaultPort: removeDefaultPort, + FlagRemoveDirectoryIndex: removeDirectoryIndex, + FlagRemoveDotSegments: removeDotSegments, + FlagRemoveFragment: removeFragment, + FlagForceHTTP: forceHTTP, + FlagRemoveDuplicateSlashes: removeDuplicateSlashes, + FlagRemoveWWW: removeWWW, + FlagAddWWW: addWWW, + FlagSortQuery: sortQuery, + FlagDecodeDWORDHost: decodeDWORDHost, + FlagDecodeOctalHost: decodeOctalHost, + FlagDecodeHexHost: decodeHexHost, + FlagRemoveUnnecessaryHostDots: removeUnncessaryHostDots, + FlagRemoveEmptyPortSeparator: removeEmptyPortSeparator, + FlagRemoveTrailingSlash: removeTrailingSlash, + FlagAddTrailingSlash: addTrailingSlash, +} + +// MustNormalizeURLString returns the normalized string, and panics if an error occurs. +// It takes an URL string as input, as well as the normalization flags. +func MustNormalizeURLString(u string, f NormalizationFlags) string { + result, e := NormalizeURLString(u, f) + if e != nil { + panic(e) + } + return result +} + +// NormalizeURLString returns the normalized string, or an error if it can't be parsed into an URL object. +// It takes an URL string as input, as well as the normalization flags. +func NormalizeURLString(u string, f NormalizationFlags) (string, error) { + parsed, err := url.Parse(u) + if err != nil { + return "", err + } + + if f&FlagLowercaseHost == FlagLowercaseHost { + parsed.Host = strings.ToLower(parsed.Host) + } + + // The idna package doesn't fully conform to RFC 5895 + // (https://tools.ietf.org/html/rfc5895), so we do it here. + // Taken from Go 1.8 cycle source, courtesy of bradfitz. + // TODO: Remove when (if?) idna package conforms to RFC 5895. + parsed.Host = width.Fold.String(parsed.Host) + parsed.Host = norm.NFC.String(parsed.Host) + if parsed.Host, err = idna.ToASCII(parsed.Host); err != nil { + return "", err + } + + return NormalizeURL(parsed, f), nil +} + +// NormalizeURL returns the normalized string. +// It takes a parsed URL object as input, as well as the normalization flags. +func NormalizeURL(u *url.URL, f NormalizationFlags) string { + for _, k := range flagsOrder { + if f&k == k { + flags[k](u) + } + } + return urlesc.Escape(u) +} + +func lowercaseScheme(u *url.URL) { + if len(u.Scheme) > 0 { + u.Scheme = strings.ToLower(u.Scheme) + } +} + +func lowercaseHost(u *url.URL) { + if len(u.Host) > 0 { + u.Host = strings.ToLower(u.Host) + } +} + +func removeDefaultPort(u *url.URL) { + if len(u.Host) > 0 { + scheme := strings.ToLower(u.Scheme) + u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string { + if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) { + return "" + } + return val + }) + } +} + +func removeTrailingSlash(u *url.URL) { + if l := len(u.Path); l > 0 { + if strings.HasSuffix(u.Path, "/") { + u.Path = u.Path[:l-1] + } + } else if l = len(u.Host); l > 0 { + if strings.HasSuffix(u.Host, "/") { + u.Host = u.Host[:l-1] + } + } +} + +func addTrailingSlash(u *url.URL) { + if l := len(u.Path); l > 0 { + if !strings.HasSuffix(u.Path, "/") { + u.Path += "/" + } + } else if l = len(u.Host); l > 0 { + if !strings.HasSuffix(u.Host, "/") { + u.Host += "/" + } + } +} + +func removeDotSegments(u *url.URL) { + if len(u.Path) > 0 { + var dotFree []string + var lastIsDot bool + + sections := strings.Split(u.Path, "/") + for _, s := range sections { + if s == ".." { + if len(dotFree) > 0 { + dotFree = dotFree[:len(dotFree)-1] + } + } else if s != "." { + dotFree = append(dotFree, s) + } + lastIsDot = (s == "." || s == "..") + } + // Special case if host does not end with / and new path does not begin with / + u.Path = strings.Join(dotFree, "/") + if u.Host != "" && !strings.HasSuffix(u.Host, "/") && !strings.HasPrefix(u.Path, "/") { + u.Path = "/" + u.Path + } + // Special case if the last segment was a dot, make sure the path ends with a slash + if lastIsDot && !strings.HasSuffix(u.Path, "/") { + u.Path += "/" + } + } +} + +func removeDirectoryIndex(u *url.URL) { + if len(u.Path) > 0 { + u.Path = rxDirIndex.ReplaceAllString(u.Path, "$1") + } +} + +func removeFragment(u *url.URL) { + u.Fragment = "" +} + +func forceHTTP(u *url.URL) { + if strings.ToLower(u.Scheme) == "https" { + u.Scheme = "http" + } +} + +func removeDuplicateSlashes(u *url.URL) { + if len(u.Path) > 0 { + u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/") + } +} + +func removeWWW(u *url.URL) { + if len(u.Host) > 0 && strings.HasPrefix(strings.ToLower(u.Host), "www.") { + u.Host = u.Host[4:] + } +} + +func addWWW(u *url.URL) { + if len(u.Host) > 0 && !strings.HasPrefix(strings.ToLower(u.Host), "www.") { + u.Host = "www." + u.Host + } +} + +func sortQuery(u *url.URL) { + q := u.Query() + + if len(q) > 0 { + arKeys := make([]string, len(q)) + i := 0 + for k, _ := range q { + arKeys[i] = k + i++ + } + sort.Strings(arKeys) + buf := new(bytes.Buffer) + for _, k := range arKeys { + sort.Strings(q[k]) + for _, v := range q[k] { + if buf.Len() > 0 { + buf.WriteRune('&') + } + buf.WriteString(fmt.Sprintf("%s=%s", k, urlesc.QueryEscape(v))) + } + } + + // Rebuild the raw query string + u.RawQuery = buf.String() + } +} + +func decodeDWORDHost(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxDWORDHost.FindStringSubmatch(u.Host); len(matches) > 2 { + var parts [4]int64 + + dword, _ := strconv.ParseInt(matches[1], 10, 0) + for i, shift := range []uint{24, 16, 8, 0} { + parts[i] = dword >> shift & 0xFF + } + u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[2]) + } + } +} + +func decodeOctalHost(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxOctalHost.FindStringSubmatch(u.Host); len(matches) > 5 { + var parts [4]int64 + + for i := 1; i <= 4; i++ { + parts[i-1], _ = strconv.ParseInt(matches[i], 8, 0) + } + u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[5]) + } + } +} + +func decodeHexHost(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxHexHost.FindStringSubmatch(u.Host); len(matches) > 2 { + // Conversion is safe because of regex validation + parsed, _ := strconv.ParseInt(matches[1], 16, 0) + // Set host as DWORD (base 10) encoded host + u.Host = fmt.Sprintf("%d%s", parsed, matches[2]) + // The rest is the same as decoding a DWORD host + decodeDWORDHost(u) + } + } +} + +func removeUnncessaryHostDots(u *url.URL) { + if len(u.Host) > 0 { + if matches := rxHostDots.FindStringSubmatch(u.Host); len(matches) > 1 { + // Trim the leading and trailing dots + u.Host = strings.Trim(matches[1], ".") + if len(matches) > 2 { + u.Host += matches[2] + } + } + } +} + +func removeEmptyPortSeparator(u *url.URL) { + if len(u.Host) > 0 { + u.Host = rxEmptyPort.ReplaceAllString(u.Host, "") + } +} diff --git a/src/runtime/vendor/github.com/PuerkitoBio/urlesc/.travis.yml b/src/runtime/vendor/github.com/PuerkitoBio/urlesc/.travis.yml new file mode 100644 index 000000000..ba6b225f9 --- /dev/null +++ b/src/runtime/vendor/github.com/PuerkitoBio/urlesc/.travis.yml @@ -0,0 +1,15 @@ +language: go + +go: + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - tip + +install: + - go build . + +script: + - go test -v diff --git a/src/runtime/vendor/github.com/PuerkitoBio/urlesc/LICENSE b/src/runtime/vendor/github.com/PuerkitoBio/urlesc/LICENSE new file mode 100644 index 000000000..744875676 --- /dev/null +++ b/src/runtime/vendor/github.com/PuerkitoBio/urlesc/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/runtime/vendor/github.com/PuerkitoBio/urlesc/README.md b/src/runtime/vendor/github.com/PuerkitoBio/urlesc/README.md new file mode 100644 index 000000000..57aff0a53 --- /dev/null +++ b/src/runtime/vendor/github.com/PuerkitoBio/urlesc/README.md @@ -0,0 +1,16 @@ +urlesc [![Build Status](https://travis-ci.org/PuerkitoBio/urlesc.svg?branch=master)](https://travis-ci.org/PuerkitoBio/urlesc) [![GoDoc](http://godoc.org/github.com/PuerkitoBio/urlesc?status.svg)](http://godoc.org/github.com/PuerkitoBio/urlesc) +====== + +Package urlesc implements query escaping as per RFC 3986. + +It contains some parts of the net/url package, modified so as to allow +some reserved characters incorrectly escaped by net/url (see [issue 5684](https://github.com/golang/go/issues/5684)). + +## Install + + go get github.com/PuerkitoBio/urlesc + +## License + +Go license (BSD-3-Clause) + diff --git a/src/runtime/vendor/github.com/PuerkitoBio/urlesc/urlesc.go b/src/runtime/vendor/github.com/PuerkitoBio/urlesc/urlesc.go new file mode 100644 index 000000000..1b8462459 --- /dev/null +++ b/src/runtime/vendor/github.com/PuerkitoBio/urlesc/urlesc.go @@ -0,0 +1,180 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package urlesc implements query escaping as per RFC 3986. +// It contains some parts of the net/url package, modified so as to allow +// some reserved characters incorrectly escaped by net/url. +// See https://github.com/golang/go/issues/5684 +package urlesc + +import ( + "bytes" + "net/url" + "strings" +) + +type encoding int + +const ( + encodePath encoding = 1 + iota + encodeUserPassword + encodeQueryComponent + encodeFragment +) + +// Return true if the specified character should be escaped when +// appearing in a URL string, according to RFC 3986. +func shouldEscape(c byte, mode encoding) bool { + // §2.3 Unreserved characters (alphanum) + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { + return false + } + + switch c { + case '-', '.', '_', '~': // §2.3 Unreserved characters (mark) + return false + + // §2.2 Reserved characters (reserved) + case ':', '/', '?', '#', '[', ']', '@', // gen-delims + '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': // sub-delims + // Different sections of the URL allow a few of + // the reserved characters to appear unescaped. + switch mode { + case encodePath: // §3.3 + // The RFC allows sub-delims and : @. + // '/', '[' and ']' can be used to assign meaning to individual path + // segments. This package only manipulates the path as a whole, + // so we allow those as well. That leaves only ? and # to escape. + return c == '?' || c == '#' + + case encodeUserPassword: // §3.2.1 + // The RFC allows : and sub-delims in + // userinfo. The parsing of userinfo treats ':' as special so we must escape + // all the gen-delims. + return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@' + + case encodeQueryComponent: // §3.4 + // The RFC allows / and ?. + return c != '/' && c != '?' + + case encodeFragment: // §4.1 + // The RFC text is silent but the grammar allows + // everything, so escape nothing but # + return c == '#' + } + } + + // Everything else must be escaped. + return true +} + +// QueryEscape escapes the string so it can be safely placed +// inside a URL query. +func QueryEscape(s string) string { + return escape(s, encodeQueryComponent) +} + +func escape(s string, mode encoding) string { + spaceCount, hexCount := 0, 0 + for i := 0; i < len(s); i++ { + c := s[i] + if shouldEscape(c, mode) { + if c == ' ' && mode == encodeQueryComponent { + spaceCount++ + } else { + hexCount++ + } + } + } + + if spaceCount == 0 && hexCount == 0 { + return s + } + + t := make([]byte, len(s)+2*hexCount) + j := 0 + for i := 0; i < len(s); i++ { + switch c := s[i]; { + case c == ' ' && mode == encodeQueryComponent: + t[j] = '+' + j++ + case shouldEscape(c, mode): + t[j] = '%' + t[j+1] = "0123456789ABCDEF"[c>>4] + t[j+2] = "0123456789ABCDEF"[c&15] + j += 3 + default: + t[j] = s[i] + j++ + } + } + return string(t) +} + +var uiReplacer = strings.NewReplacer( + "%21", "!", + "%27", "'", + "%28", "(", + "%29", ")", + "%2A", "*", +) + +// unescapeUserinfo unescapes some characters that need not to be escaped as per RFC3986. +func unescapeUserinfo(s string) string { + return uiReplacer.Replace(s) +} + +// Escape reassembles the URL into a valid URL string. +// The general form of the result is one of: +// +// scheme:opaque +// scheme://userinfo@host/path?query#fragment +// +// If u.Opaque is non-empty, String uses the first form; +// otherwise it uses the second form. +// +// In the second form, the following rules apply: +// - if u.Scheme is empty, scheme: is omitted. +// - if u.User is nil, userinfo@ is omitted. +// - if u.Host is empty, host/ is omitted. +// - if u.Scheme and u.Host are empty and u.User is nil, +// the entire scheme://userinfo@host/ is omitted. +// - if u.Host is non-empty and u.Path begins with a /, +// the form host/path does not add its own /. +// - if u.RawQuery is empty, ?query is omitted. +// - if u.Fragment is empty, #fragment is omitted. +func Escape(u *url.URL) string { + var buf bytes.Buffer + if u.Scheme != "" { + buf.WriteString(u.Scheme) + buf.WriteByte(':') + } + if u.Opaque != "" { + buf.WriteString(u.Opaque) + } else { + if u.Scheme != "" || u.Host != "" || u.User != nil { + buf.WriteString("//") + if ui := u.User; ui != nil { + buf.WriteString(unescapeUserinfo(ui.String())) + buf.WriteByte('@') + } + if h := u.Host; h != "" { + buf.WriteString(h) + } + } + if u.Path != "" && u.Path[0] != '/' && u.Host != "" { + buf.WriteByte('/') + } + buf.WriteString(escape(u.Path, encodePath)) + } + if u.RawQuery != "" { + buf.WriteByte('?') + buf.WriteString(u.RawQuery) + } + if u.Fragment != "" { + buf.WriteByte('#') + buf.WriteString(escape(u.Fragment, encodeFragment)) + } + return buf.String() +} diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/.travis.yml b/src/runtime/vendor/github.com/asaskevich/govalidator/.travis.yml new file mode 100644 index 000000000..e29f8eef5 --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/.travis.yml @@ -0,0 +1,14 @@ +language: go + +go: + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - 1.6 + - tip + +notifications: + email: + - bwatas@gmail.com diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md b/src/runtime/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md new file mode 100644 index 000000000..f0f7e3a8a --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md @@ -0,0 +1,63 @@ +#### Support +If you do have a contribution to the package, feel free to create a Pull Request or an Issue. + +#### What to contribute +If you don't know what to do, there are some features and functions that need to be done + +- [ ] Refactor code +- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check +- [ ] Create actual list of contributors and projects that currently using this package +- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues) +- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions) +- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new +- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc +- [ ] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224) +- [ ] Implement fuzzing testing +- [ ] Implement some struct/map/array utilities +- [ ] Implement map/array validation +- [ ] Implement benchmarking +- [ ] Implement batch of examples +- [ ] Look at forks for new features and fixes + +#### Advice +Feel free to create what you want, but keep in mind when you implement new features: +- Code must be clear and readable, names of variables/constants clearly describes what they are doing +- Public functions must be documented and described in source file and added to README.md to the list of available functions +- There are must be unit-tests for any new functions and improvements + +## Financial contributions + +We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/govalidator). +Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed. + + +## Credits + + +### Contributors + +Thank you to all the people who have already contributed to govalidator! + + + +### Backers + +Thank you to all our backers! [[Become a backer](https://opencollective.com/govalidator#backer)] + + + + +### Sponsors + +Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/govalidator#sponsor)) + + + + + + + + + + + \ No newline at end of file diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/LICENSE b/src/runtime/vendor/github.com/asaskevich/govalidator/LICENSE new file mode 100644 index 000000000..2f9a31fad --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Alex Saskevich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/README.md b/src/runtime/vendor/github.com/asaskevich/govalidator/README.md new file mode 100644 index 000000000..0e8793f71 --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/README.md @@ -0,0 +1,496 @@ +govalidator +=========== +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) [![Coverage Status](https://img.shields.io/coveralls/asaskevich/govalidator.svg)](https://coveralls.io/r/asaskevich/govalidator?branch=master) [![wercker status](https://app.wercker.com/status/1ec990b09ea86c910d5f08b0e02c6043/s "wercker status")](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043) +[![Build Status](https://travis-ci.org/asaskevich/govalidator.svg?branch=master)](https://travis-ci.org/asaskevich/govalidator) [![Go Report Card](https://goreportcard.com/badge/github.com/asaskevich/govalidator)](https://goreportcard.com/report/github.com/asaskevich/govalidator) [![GoSearch](http://go-search.org/badge?id=github.com%2Fasaskevich%2Fgovalidator)](http://go-search.org/view?id=github.com%2Fasaskevich%2Fgovalidator) [![Backers on Open Collective](https://opencollective.com/govalidator/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/govalidator/sponsors/badge.svg)](#sponsors) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator?ref=badge_shield) + +A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js). + +#### Installation +Make sure that Go is installed on your computer. +Type the following command in your terminal: + + go get github.com/asaskevich/govalidator + +or you can get specified release of the package with `gopkg.in`: + + go get gopkg.in/asaskevich/govalidator.v4 + +After it the package is ready to use. + + +#### Import package in your project +Add following line in your `*.go` file: +```go +import "github.com/asaskevich/govalidator" +``` +If you are unhappy to use long `govalidator`, you can do something like this: +```go +import ( + valid "github.com/asaskevich/govalidator" +) +``` + +#### Activate behavior to require all fields have a validation tag by default +`SetFieldsRequiredByDefault` causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). A good place to activate this is a package init function or the main() function. + +`SetNilPtrAllowedByRequired` causes validation to pass when struct fields marked by `required` are set to nil. This is disabled by default for consistency, but some packages that need to be able to determine between `nil` and `zero value` state can use this. If disabled, both `nil` and `zero` values cause validation errors. + +```go +import "github.com/asaskevich/govalidator" + +func init() { + govalidator.SetFieldsRequiredByDefault(true) +} +``` + +Here's some code to explain it: +```go +// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter): +type exampleStruct struct { + Name string `` + Email string `valid:"email"` +} + +// this, however, will only fail when Email is empty or an invalid email address: +type exampleStruct2 struct { + Name string `valid:"-"` + Email string `valid:"email"` +} + +// lastly, this will only fail when Email is an invalid email address but not when it's empty: +type exampleStruct2 struct { + Name string `valid:"-"` + Email string `valid:"email,optional"` +} +``` + +#### Recent breaking changes (see [#123](https://github.com/asaskevich/govalidator/pull/123)) +##### Custom validator function signature +A context was added as the second parameter, for structs this is the object being validated – this makes dependent validation possible. +```go +import "github.com/asaskevich/govalidator" + +// old signature +func(i interface{}) bool + +// new signature +func(i interface{}, o interface{}) bool +``` + +##### Adding a custom validator +This was changed to prevent data races when accessing custom validators. +```go +import "github.com/asaskevich/govalidator" + +// before +govalidator.CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool { + // ... +}) + +// after +govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool { + // ... +})) +``` + +#### List of functions: +```go +func Abs(value float64) float64 +func BlackList(str, chars string) string +func ByteLength(str string, params ...string) bool +func CamelCaseToUnderscore(str string) string +func Contains(str, substring string) bool +func Count(array []interface{}, iterator ConditionIterator) int +func Each(array []interface{}, iterator Iterator) +func ErrorByField(e error, field string) string +func ErrorsByField(e error) map[string]string +func Filter(array []interface{}, iterator ConditionIterator) []interface{} +func Find(array []interface{}, iterator ConditionIterator) interface{} +func GetLine(s string, index int) (string, error) +func GetLines(s string) []string +func InRange(value, left, right float64) bool +func IsASCII(str string) bool +func IsAlpha(str string) bool +func IsAlphanumeric(str string) bool +func IsBase64(str string) bool +func IsByteLength(str string, min, max int) bool +func IsCIDR(str string) bool +func IsCreditCard(str string) bool +func IsDNSName(str string) bool +func IsDataURI(str string) bool +func IsDialString(str string) bool +func IsDivisibleBy(str, num string) bool +func IsEmail(str string) bool +func IsFilePath(str string) (bool, int) +func IsFloat(str string) bool +func IsFullWidth(str string) bool +func IsHalfWidth(str string) bool +func IsHexadecimal(str string) bool +func IsHexcolor(str string) bool +func IsHost(str string) bool +func IsIP(str string) bool +func IsIPv4(str string) bool +func IsIPv6(str string) bool +func IsISBN(str string, version int) bool +func IsISBN10(str string) bool +func IsISBN13(str string) bool +func IsISO3166Alpha2(str string) bool +func IsISO3166Alpha3(str string) bool +func IsISO693Alpha2(str string) bool +func IsISO693Alpha3b(str string) bool +func IsISO4217(str string) bool +func IsIn(str string, params ...string) bool +func IsInt(str string) bool +func IsJSON(str string) bool +func IsLatitude(str string) bool +func IsLongitude(str string) bool +func IsLowerCase(str string) bool +func IsMAC(str string) bool +func IsMongoID(str string) bool +func IsMultibyte(str string) bool +func IsNatural(value float64) bool +func IsNegative(value float64) bool +func IsNonNegative(value float64) bool +func IsNonPositive(value float64) bool +func IsNull(str string) bool +func IsNumeric(str string) bool +func IsPort(str string) bool +func IsPositive(value float64) bool +func IsPrintableASCII(str string) bool +func IsRFC3339(str string) bool +func IsRFC3339WithoutZone(str string) bool +func IsRGBcolor(str string) bool +func IsRequestURI(rawurl string) bool +func IsRequestURL(rawurl string) bool +func IsSSN(str string) bool +func IsSemver(str string) bool +func IsTime(str string, format string) bool +func IsURL(str string) bool +func IsUTFDigit(str string) bool +func IsUTFLetter(str string) bool +func IsUTFLetterNumeric(str string) bool +func IsUTFNumeric(str string) bool +func IsUUID(str string) bool +func IsUUIDv3(str string) bool +func IsUUIDv4(str string) bool +func IsUUIDv5(str string) bool +func IsUpperCase(str string) bool +func IsVariableWidth(str string) bool +func IsWhole(value float64) bool +func LeftTrim(str, chars string) string +func Map(array []interface{}, iterator ResultIterator) []interface{} +func Matches(str, pattern string) bool +func NormalizeEmail(str string) (string, error) +func PadBoth(str string, padStr string, padLen int) string +func PadLeft(str string, padStr string, padLen int) string +func PadRight(str string, padStr string, padLen int) string +func Range(str string, params ...string) bool +func RemoveTags(s string) string +func ReplacePattern(str, pattern, replace string) string +func Reverse(s string) string +func RightTrim(str, chars string) string +func RuneLength(str string, params ...string) bool +func SafeFileName(str string) string +func SetFieldsRequiredByDefault(value bool) +func Sign(value float64) float64 +func StringLength(str string, params ...string) bool +func StringMatches(s string, params ...string) bool +func StripLow(str string, keepNewLines bool) string +func ToBoolean(str string) (bool, error) +func ToFloat(str string) (float64, error) +func ToInt(str string) (int64, error) +func ToJSON(obj interface{}) (string, error) +func ToString(obj interface{}) string +func Trim(str, chars string) string +func Truncate(str string, length int, ending string) string +func UnderscoreToCamelCase(s string) string +func ValidateStruct(s interface{}) (bool, error) +func WhiteList(str, chars string) string +type ConditionIterator +type CustomTypeValidator +type Error +func (e Error) Error() string +type Errors +func (es Errors) Error() string +func (es Errors) Errors() []error +type ISO3166Entry +type Iterator +type ParamValidator +type ResultIterator +type UnsupportedTypeError +func (e *UnsupportedTypeError) Error() string +type Validator +``` + +#### Examples +###### IsURL +```go +println(govalidator.IsURL(`http://user@pass:domain.com/path/page`)) +``` +###### ToString +```go +type User struct { + FirstName string + LastName string +} + +str := govalidator.ToString(&User{"John", "Juan"}) +println(str) +``` +###### Each, Map, Filter, Count for slices +Each iterates over the slice/array and calls Iterator for every item +```go +data := []interface{}{1, 2, 3, 4, 5} +var fn govalidator.Iterator = func(value interface{}, index int) { + println(value.(int)) +} +govalidator.Each(data, fn) +``` +```go +data := []interface{}{1, 2, 3, 4, 5} +var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} { + return value.(int) * 3 +} +_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15} +``` +```go +data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} +var fn govalidator.ConditionIterator = func(value interface{}, index int) bool { + return value.(int)%2 == 0 +} +_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10} +_ = govalidator.Count(data, fn) // result = 5 +``` +###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2) +If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this: +```go +govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool { + return str == "duck" +}) +``` +For completely custom validators (interface-based), see below. + +Here is a list of available validators for struct fields (validator - used function): +```go +"email": IsEmail, +"url": IsURL, +"dialstring": IsDialString, +"requrl": IsRequestURL, +"requri": IsRequestURI, +"alpha": IsAlpha, +"utfletter": IsUTFLetter, +"alphanum": IsAlphanumeric, +"utfletternum": IsUTFLetterNumeric, +"numeric": IsNumeric, +"utfnumeric": IsUTFNumeric, +"utfdigit": IsUTFDigit, +"hexadecimal": IsHexadecimal, +"hexcolor": IsHexcolor, +"rgbcolor": IsRGBcolor, +"lowercase": IsLowerCase, +"uppercase": IsUpperCase, +"int": IsInt, +"float": IsFloat, +"null": IsNull, +"uuid": IsUUID, +"uuidv3": IsUUIDv3, +"uuidv4": IsUUIDv4, +"uuidv5": IsUUIDv5, +"creditcard": IsCreditCard, +"isbn10": IsISBN10, +"isbn13": IsISBN13, +"json": IsJSON, +"multibyte": IsMultibyte, +"ascii": IsASCII, +"printableascii": IsPrintableASCII, +"fullwidth": IsFullWidth, +"halfwidth": IsHalfWidth, +"variablewidth": IsVariableWidth, +"base64": IsBase64, +"datauri": IsDataURI, +"ip": IsIP, +"port": IsPort, +"ipv4": IsIPv4, +"ipv6": IsIPv6, +"dns": IsDNSName, +"host": IsHost, +"mac": IsMAC, +"latitude": IsLatitude, +"longitude": IsLongitude, +"ssn": IsSSN, +"semver": IsSemver, +"rfc3339": IsRFC3339, +"rfc3339WithoutZone": IsRFC3339WithoutZone, +"ISO3166Alpha2": IsISO3166Alpha2, +"ISO3166Alpha3": IsISO3166Alpha3, +``` +Validators with parameters + +```go +"range(min|max)": Range, +"length(min|max)": ByteLength, +"runelength(min|max)": RuneLength, +"matches(pattern)": StringMatches, +"in(string1|string2|...|stringN)": IsIn, +``` + +And here is small example of usage: +```go +type Post struct { + Title string `valid:"alphanum,required"` + Message string `valid:"duck,ascii"` + AuthorIP string `valid:"ipv4"` + Date string `valid:"-"` +} +post := &Post{ + Title: "My Example Post", + Message: "duck", + AuthorIP: "123.234.54.3", +} + +// Add your own struct validation tags +govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool { + return str == "duck" +}) + +result, err := govalidator.ValidateStruct(post) +if err != nil { + println("error: " + err.Error()) +} +println(result) +``` +###### WhiteList +```go +// Remove all characters from string ignoring characters between "a" and "z" +println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa") +``` + +###### Custom validation functions +Custom validation using your own domain specific validators is also available - here's an example of how to use it: +```go +import "github.com/asaskevich/govalidator" + +type CustomByteArray [6]byte // custom types are supported and can be validated + +type StructWithCustomByteArray struct { + ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence + Email string `valid:"email"` + CustomMinLength int `valid:"-"` +} + +govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool { + switch v := context.(type) { // you can type switch on the context interface being validated + case StructWithCustomByteArray: + // you can check and validate against some other field in the context, + // return early or not validate against the context at all – your choice + case SomeOtherType: + // ... + default: + // expecting some other type? Throw/panic here or continue + } + + switch v := i.(type) { // type switch on the struct field being validated + case CustomByteArray: + for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes + if e != 0 { + return true + } + } + } + return false +})) +govalidator.CustomTypeTagMap.Set("customMinLengthValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool { + switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation + case StructWithCustomByteArray: + return len(v.ID) >= v.CustomMinLength + } + return false +})) +``` + +###### Custom error messages +Custom error messages are supported via annotations by adding the `~` separator - here's an example of how to use it: +```go +type Ticket struct { + Id int64 `json:"id"` + FirstName string `json:"firstname" valid:"required~First name is blank"` +} +``` + +#### Notes +Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator). +Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator). + +#### Support +If you do have a contribution to the package, feel free to create a Pull Request or an Issue. + +#### What to contribute +If you don't know what to do, there are some features and functions that need to be done + +- [ ] Refactor code +- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check +- [ ] Create actual list of contributors and projects that currently using this package +- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues) +- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions) +- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new +- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc +- [ ] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224) +- [ ] Implement fuzzing testing +- [ ] Implement some struct/map/array utilities +- [ ] Implement map/array validation +- [ ] Implement benchmarking +- [ ] Implement batch of examples +- [ ] Look at forks for new features and fixes + +#### Advice +Feel free to create what you want, but keep in mind when you implement new features: +- Code must be clear and readable, names of variables/constants clearly describes what they are doing +- Public functions must be documented and described in source file and added to README.md to the list of available functions +- There are must be unit-tests for any new functions and improvements + +## Credits +### Contributors + +This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. + +#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors) +* [Daniel Lohse](https://github.com/annismckenzie) +* [Attila Oláh](https://github.com/attilaolah) +* [Daniel Korner](https://github.com/Dadie) +* [Steven Wilkin](https://github.com/stevenwilkin) +* [Deiwin Sarjas](https://github.com/deiwin) +* [Noah Shibley](https://github.com/slugmobile) +* [Nathan Davies](https://github.com/nathj07) +* [Matt Sanford](https://github.com/mzsanford) +* [Simon ccl1115](https://github.com/ccl1115) + + + + +### Backers + +Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/govalidator#backer)] + + + + +### Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/govalidator#sponsor)] + + + + + + + + + + + + + + + +## License +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator?ref=badge_large) \ No newline at end of file diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/arrays.go b/src/runtime/vendor/github.com/asaskevich/govalidator/arrays.go new file mode 100644 index 000000000..5bace2654 --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/arrays.go @@ -0,0 +1,58 @@ +package govalidator + +// Iterator is the function that accepts element of slice/array and its index +type Iterator func(interface{}, int) + +// ResultIterator is the function that accepts element of slice/array and its index and returns any result +type ResultIterator func(interface{}, int) interface{} + +// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean +type ConditionIterator func(interface{}, int) bool + +// Each iterates over the slice and apply Iterator to every item +func Each(array []interface{}, iterator Iterator) { + for index, data := range array { + iterator(data, index) + } +} + +// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result. +func Map(array []interface{}, iterator ResultIterator) []interface{} { + var result = make([]interface{}, len(array)) + for index, data := range array { + result[index] = iterator(data, index) + } + return result +} + +// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise. +func Find(array []interface{}, iterator ConditionIterator) interface{} { + for index, data := range array { + if iterator(data, index) { + return data + } + } + return nil +} + +// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice. +func Filter(array []interface{}, iterator ConditionIterator) []interface{} { + var result = make([]interface{}, 0) + for index, data := range array { + if iterator(data, index) { + result = append(result, data) + } + } + return result +} + +// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator. +func Count(array []interface{}, iterator ConditionIterator) int { + count := 0 + for index, data := range array { + if iterator(data, index) { + count = count + 1 + } + } + return count +} diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/converter.go b/src/runtime/vendor/github.com/asaskevich/govalidator/converter.go new file mode 100644 index 000000000..cf1e5d569 --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/converter.go @@ -0,0 +1,64 @@ +package govalidator + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" +) + +// ToString convert the input to a string. +func ToString(obj interface{}) string { + res := fmt.Sprintf("%v", obj) + return string(res) +} + +// ToJSON convert the input to a valid JSON string +func ToJSON(obj interface{}) (string, error) { + res, err := json.Marshal(obj) + if err != nil { + res = []byte("") + } + return string(res), err +} + +// ToFloat convert the input string to a float, or 0.0 if the input is not a float. +func ToFloat(str string) (float64, error) { + res, err := strconv.ParseFloat(str, 64) + if err != nil { + res = 0.0 + } + return res, err +} + +// ToInt convert the input string or any int type to an integer type 64, or 0 if the input is not an integer. +func ToInt(value interface{}) (res int64, err error) { + val := reflect.ValueOf(value) + + switch value.(type) { + case int, int8, int16, int32, int64: + res = val.Int() + case uint, uint8, uint16, uint32, uint64: + res = int64(val.Uint()) + case string: + if IsInt(val.String()) { + res, err = strconv.ParseInt(val.String(), 0, 64) + if err != nil { + res = 0 + } + } else { + err = fmt.Errorf("math: square root of negative number %g", value) + res = 0 + } + default: + err = fmt.Errorf("math: square root of negative number %g", value) + res = 0 + } + + return +} + +// ToBoolean convert the input string to a boolean. +func ToBoolean(str string) (bool, error) { + return strconv.ParseBool(str) +} diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/error.go b/src/runtime/vendor/github.com/asaskevich/govalidator/error.go new file mode 100644 index 000000000..655b750cb --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/error.go @@ -0,0 +1,43 @@ +package govalidator + +import "strings" + +// Errors is an array of multiple errors and conforms to the error interface. +type Errors []error + +// Errors returns itself. +func (es Errors) Errors() []error { + return es +} + +func (es Errors) Error() string { + var errs []string + for _, e := range es { + errs = append(errs, e.Error()) + } + return strings.Join(errs, ";") +} + +// Error encapsulates a name, an error and whether there's a custom error message or not. +type Error struct { + Name string + Err error + CustomErrorMessageExists bool + + // Validator indicates the name of the validator that failed + Validator string + Path []string +} + +func (e Error) Error() string { + if e.CustomErrorMessageExists { + return e.Err.Error() + } + + errName := e.Name + if len(e.Path) > 0 { + errName = strings.Join(append(e.Path, e.Name), ".") + } + + return errName + ": " + e.Err.Error() +} diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/numerics.go b/src/runtime/vendor/github.com/asaskevich/govalidator/numerics.go new file mode 100644 index 000000000..7e6c652e1 --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/numerics.go @@ -0,0 +1,97 @@ +package govalidator + +import ( + "math" + "reflect" +) + +// Abs returns absolute value of number +func Abs(value float64) float64 { + return math.Abs(value) +} + +// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise +func Sign(value float64) float64 { + if value > 0 { + return 1 + } else if value < 0 { + return -1 + } else { + return 0 + } +} + +// IsNegative returns true if value < 0 +func IsNegative(value float64) bool { + return value < 0 +} + +// IsPositive returns true if value > 0 +func IsPositive(value float64) bool { + return value > 0 +} + +// IsNonNegative returns true if value >= 0 +func IsNonNegative(value float64) bool { + return value >= 0 +} + +// IsNonPositive returns true if value <= 0 +func IsNonPositive(value float64) bool { + return value <= 0 +} + +// InRange returns true if value lies between left and right border +func InRangeInt(value, left, right interface{}) bool { + value64, _ := ToInt(value) + left64, _ := ToInt(left) + right64, _ := ToInt(right) + if left64 > right64 { + left64, right64 = right64, left64 + } + return value64 >= left64 && value64 <= right64 +} + +// InRange returns true if value lies between left and right border +func InRangeFloat32(value, left, right float32) bool { + if left > right { + left, right = right, left + } + return value >= left && value <= right +} + +// InRange returns true if value lies between left and right border +func InRangeFloat64(value, left, right float64) bool { + if left > right { + left, right = right, left + } + return value >= left && value <= right +} + +// InRange returns true if value lies between left and right border, generic type to handle int, float32 or float64, all types must the same type +func InRange(value interface{}, left interface{}, right interface{}) bool { + + reflectValue := reflect.TypeOf(value).Kind() + reflectLeft := reflect.TypeOf(left).Kind() + reflectRight := reflect.TypeOf(right).Kind() + + if reflectValue == reflect.Int && reflectLeft == reflect.Int && reflectRight == reflect.Int { + return InRangeInt(value.(int), left.(int), right.(int)) + } else if reflectValue == reflect.Float32 && reflectLeft == reflect.Float32 && reflectRight == reflect.Float32 { + return InRangeFloat32(value.(float32), left.(float32), right.(float32)) + } else if reflectValue == reflect.Float64 && reflectLeft == reflect.Float64 && reflectRight == reflect.Float64 { + return InRangeFloat64(value.(float64), left.(float64), right.(float64)) + } else { + return false + } +} + +// IsWhole returns true if value is whole number +func IsWhole(value float64) bool { + return math.Remainder(value, 1) == 0 +} + +// IsNatural returns true if value is natural number (positive and whole) +func IsNatural(value float64) bool { + return IsWhole(value) && IsPositive(value) +} diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/patterns.go b/src/runtime/vendor/github.com/asaskevich/govalidator/patterns.go new file mode 100644 index 000000000..61a05d438 --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/patterns.go @@ -0,0 +1,101 @@ +package govalidator + +import "regexp" + +// Basic regular expressions for validating strings +const ( + Email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" + CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$" + ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$" + ISBN13 string = "^(?:[0-9]{13})$" + UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" + UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" + Alpha string = "^[a-zA-Z]+$" + Alphanumeric string = "^[a-zA-Z0-9]+$" + Numeric string = "^[0-9]+$" + Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$" + Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$" + Hexadecimal string = "^[0-9a-fA-F]+$" + Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$" + ASCII string = "^[\x00-\x7F]+$" + Multibyte string = "[^\x00-\x7F]" + FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]" + HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]" + Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" + PrintableASCII string = "^[\x20-\x7E]+$" + DataURI string = "^data:.+\\/(.+);base64$" + Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" + Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" + DNSName string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$` + IP string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` + URLSchema string = `((ftp|tcp|udp|wss?|https?):\/\/)` + URLUsername string = `(\S+(:\S*)?@)` + URLPath string = `((\/|\?|#)[^\s]*)` + URLPort string = `(:(\d{1,5}))` + URLIP string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))` + URLSubdomain string = `((www\.)|([a-zA-Z0-9]+([-_\.]?[a-zA-Z0-9])*[a-zA-Z0-9]\.[a-zA-Z0-9]+))` + URL string = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$` + SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` + WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$` + UnixPath string = `^(/[^/\x00]*)+/?$` + Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$" + tagName string = "valid" + hasLowerCase string = ".*[[:lower:]]" + hasUpperCase string = ".*[[:upper:]]" + hasWhitespace string = ".*[[:space:]]" + hasWhitespaceOnly string = "^[[:space:]]+$" +) + +// Used by IsFilePath func +const ( + // Unknown is unresolved OS type + Unknown = iota + // Win is Windows type + Win + // Unix is *nix OS types + Unix +) + +var ( + userRegexp = regexp.MustCompile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$") + hostRegexp = regexp.MustCompile("^[^\\s]+\\.[^\\s]+$") + userDotRegexp = regexp.MustCompile("(^[.]{1})|([.]{1}$)|([.]{2,})") + rxEmail = regexp.MustCompile(Email) + rxCreditCard = regexp.MustCompile(CreditCard) + rxISBN10 = regexp.MustCompile(ISBN10) + rxISBN13 = regexp.MustCompile(ISBN13) + rxUUID3 = regexp.MustCompile(UUID3) + rxUUID4 = regexp.MustCompile(UUID4) + rxUUID5 = regexp.MustCompile(UUID5) + rxUUID = regexp.MustCompile(UUID) + rxAlpha = regexp.MustCompile(Alpha) + rxAlphanumeric = regexp.MustCompile(Alphanumeric) + rxNumeric = regexp.MustCompile(Numeric) + rxInt = regexp.MustCompile(Int) + rxFloat = regexp.MustCompile(Float) + rxHexadecimal = regexp.MustCompile(Hexadecimal) + rxHexcolor = regexp.MustCompile(Hexcolor) + rxRGBcolor = regexp.MustCompile(RGBcolor) + rxASCII = regexp.MustCompile(ASCII) + rxPrintableASCII = regexp.MustCompile(PrintableASCII) + rxMultibyte = regexp.MustCompile(Multibyte) + rxFullWidth = regexp.MustCompile(FullWidth) + rxHalfWidth = regexp.MustCompile(HalfWidth) + rxBase64 = regexp.MustCompile(Base64) + rxDataURI = regexp.MustCompile(DataURI) + rxLatitude = regexp.MustCompile(Latitude) + rxLongitude = regexp.MustCompile(Longitude) + rxDNSName = regexp.MustCompile(DNSName) + rxURL = regexp.MustCompile(URL) + rxSSN = regexp.MustCompile(SSN) + rxWinPath = regexp.MustCompile(WinPath) + rxUnixPath = regexp.MustCompile(UnixPath) + rxSemver = regexp.MustCompile(Semver) + rxHasLowerCase = regexp.MustCompile(hasLowerCase) + rxHasUpperCase = regexp.MustCompile(hasUpperCase) + rxHasWhitespace = regexp.MustCompile(hasWhitespace) + rxHasWhitespaceOnly = regexp.MustCompile(hasWhitespaceOnly) +) diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/types.go b/src/runtime/vendor/github.com/asaskevich/govalidator/types.go new file mode 100644 index 000000000..4f7e9274a --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/types.go @@ -0,0 +1,636 @@ +package govalidator + +import ( + "reflect" + "regexp" + "sort" + "sync" +) + +// Validator is a wrapper for a validator function that returns bool and accepts string. +type Validator func(str string) bool + +// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type. +// The second parameter should be the context (in the case of validating a struct: the whole object being validated). +type CustomTypeValidator func(i interface{}, o interface{}) bool + +// ParamValidator is a wrapper for validator functions that accepts additional parameters. +type ParamValidator func(str string, params ...string) bool +type tagOptionsMap map[string]tagOption + +func (t tagOptionsMap) orderedKeys() []string { + var keys []string + for k := range t { + keys = append(keys, k) + } + + sort.Slice(keys, func(a, b int) bool { + return t[keys[a]].order < t[keys[b]].order + }) + + return keys +} + +type tagOption struct { + name string + customErrorMessage string + order int +} + +// UnsupportedTypeError is a wrapper for reflect.Type +type UnsupportedTypeError struct { + Type reflect.Type +} + +// stringValues is a slice of reflect.Value holding *reflect.StringValue. +// It implements the methods to sort by string. +type stringValues []reflect.Value + +// ParamTagMap is a map of functions accept variants parameters +var ParamTagMap = map[string]ParamValidator{ + "length": ByteLength, + "range": Range, + "runelength": RuneLength, + "stringlength": StringLength, + "matches": StringMatches, + "in": isInRaw, + "rsapub": IsRsaPub, +} + +// ParamTagRegexMap maps param tags to their respective regexes. +var ParamTagRegexMap = map[string]*regexp.Regexp{ + "range": regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"), + "length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"), + "runelength": regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"), + "stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"), + "in": regexp.MustCompile(`^in\((.*)\)`), + "matches": regexp.MustCompile(`^matches\((.+)\)$`), + "rsapub": regexp.MustCompile("^rsapub\\((\\d+)\\)$"), +} + +type customTypeTagMap struct { + validators map[string]CustomTypeValidator + + sync.RWMutex +} + +func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) { + tm.RLock() + defer tm.RUnlock() + v, ok := tm.validators[name] + return v, ok +} + +func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) { + tm.Lock() + defer tm.Unlock() + tm.validators[name] = ctv +} + +// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function. +// Use this to validate compound or custom types that need to be handled as a whole, e.g. +// `type UUID [16]byte` (this would be handled as an array of bytes). +var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)} + +// TagMap is a map of functions, that can be used as tags for ValidateStruct function. +var TagMap = map[string]Validator{ + "email": IsEmail, + "url": IsURL, + "dialstring": IsDialString, + "requrl": IsRequestURL, + "requri": IsRequestURI, + "alpha": IsAlpha, + "utfletter": IsUTFLetter, + "alphanum": IsAlphanumeric, + "utfletternum": IsUTFLetterNumeric, + "numeric": IsNumeric, + "utfnumeric": IsUTFNumeric, + "utfdigit": IsUTFDigit, + "hexadecimal": IsHexadecimal, + "hexcolor": IsHexcolor, + "rgbcolor": IsRGBcolor, + "lowercase": IsLowerCase, + "uppercase": IsUpperCase, + "int": IsInt, + "float": IsFloat, + "null": IsNull, + "uuid": IsUUID, + "uuidv3": IsUUIDv3, + "uuidv4": IsUUIDv4, + "uuidv5": IsUUIDv5, + "creditcard": IsCreditCard, + "isbn10": IsISBN10, + "isbn13": IsISBN13, + "json": IsJSON, + "multibyte": IsMultibyte, + "ascii": IsASCII, + "printableascii": IsPrintableASCII, + "fullwidth": IsFullWidth, + "halfwidth": IsHalfWidth, + "variablewidth": IsVariableWidth, + "base64": IsBase64, + "datauri": IsDataURI, + "ip": IsIP, + "port": IsPort, + "ipv4": IsIPv4, + "ipv6": IsIPv6, + "dns": IsDNSName, + "host": IsHost, + "mac": IsMAC, + "latitude": IsLatitude, + "longitude": IsLongitude, + "ssn": IsSSN, + "semver": IsSemver, + "rfc3339": IsRFC3339, + "rfc3339WithoutZone": IsRFC3339WithoutZone, + "ISO3166Alpha2": IsISO3166Alpha2, + "ISO3166Alpha3": IsISO3166Alpha3, + "ISO4217": IsISO4217, +} + +// ISO3166Entry stores country codes +type ISO3166Entry struct { + EnglishShortName string + FrenchShortName string + Alpha2Code string + Alpha3Code string + Numeric string +} + +//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes" +var ISO3166List = []ISO3166Entry{ + {"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"}, + {"Albania", "Albanie (l')", "AL", "ALB", "008"}, + {"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"}, + {"Algeria", "Algérie (l')", "DZ", "DZA", "012"}, + {"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"}, + {"Andorra", "Andorre (l')", "AD", "AND", "020"}, + {"Angola", "Angola (l')", "AO", "AGO", "024"}, + {"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"}, + {"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"}, + {"Argentina", "Argentine (l')", "AR", "ARG", "032"}, + {"Australia", "Australie (l')", "AU", "AUS", "036"}, + {"Austria", "Autriche (l')", "AT", "AUT", "040"}, + {"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"}, + {"Bahrain", "Bahreïn", "BH", "BHR", "048"}, + {"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"}, + {"Armenia", "Arménie (l')", "AM", "ARM", "051"}, + {"Barbados", "Barbade (la)", "BB", "BRB", "052"}, + {"Belgium", "Belgique (la)", "BE", "BEL", "056"}, + {"Bermuda", "Bermudes (les)", "BM", "BMU", "060"}, + {"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"}, + {"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"}, + {"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"}, + {"Botswana", "Botswana (le)", "BW", "BWA", "072"}, + {"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"}, + {"Brazil", "Brésil (le)", "BR", "BRA", "076"}, + {"Belize", "Belize (le)", "BZ", "BLZ", "084"}, + {"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"}, + {"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"}, + {"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"}, + {"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"}, + {"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"}, + {"Myanmar", "Myanmar (le)", "MM", "MMR", "104"}, + {"Burundi", "Burundi (le)", "BI", "BDI", "108"}, + {"Belarus", "Bélarus (le)", "BY", "BLR", "112"}, + {"Cambodia", "Cambodge (le)", "KH", "KHM", "116"}, + {"Cameroon", "Cameroun (le)", "CM", "CMR", "120"}, + {"Canada", "Canada (le)", "CA", "CAN", "124"}, + {"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"}, + {"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"}, + {"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"}, + {"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"}, + {"Chad", "Tchad (le)", "TD", "TCD", "148"}, + {"Chile", "Chili (le)", "CL", "CHL", "152"}, + {"China", "Chine (la)", "CN", "CHN", "156"}, + {"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"}, + {"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"}, + {"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"}, + {"Colombia", "Colombie (la)", "CO", "COL", "170"}, + {"Comoros (the)", "Comores (les)", "KM", "COM", "174"}, + {"Mayotte", "Mayotte", "YT", "MYT", "175"}, + {"Congo (the)", "Congo (le)", "CG", "COG", "178"}, + {"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"}, + {"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"}, + {"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"}, + {"Croatia", "Croatie (la)", "HR", "HRV", "191"}, + {"Cuba", "Cuba", "CU", "CUB", "192"}, + {"Cyprus", "Chypre", "CY", "CYP", "196"}, + {"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"}, + {"Benin", "Bénin (le)", "BJ", "BEN", "204"}, + {"Denmark", "Danemark (le)", "DK", "DNK", "208"}, + {"Dominica", "Dominique (la)", "DM", "DMA", "212"}, + {"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"}, + {"Ecuador", "Équateur (l')", "EC", "ECU", "218"}, + {"El Salvador", "El Salvador", "SV", "SLV", "222"}, + {"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"}, + {"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"}, + {"Eritrea", "Érythrée (l')", "ER", "ERI", "232"}, + {"Estonia", "Estonie (l')", "EE", "EST", "233"}, + {"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"}, + {"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"}, + {"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"}, + {"Fiji", "Fidji (les)", "FJ", "FJI", "242"}, + {"Finland", "Finlande (la)", "FI", "FIN", "246"}, + {"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"}, + {"France", "France (la)", "FR", "FRA", "250"}, + {"French Guiana", "Guyane française (la )", "GF", "GUF", "254"}, + {"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"}, + {"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"}, + {"Djibouti", "Djibouti", "DJ", "DJI", "262"}, + {"Gabon", "Gabon (le)", "GA", "GAB", "266"}, + {"Georgia", "Géorgie (la)", "GE", "GEO", "268"}, + {"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"}, + {"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"}, + {"Germany", "Allemagne (l')", "DE", "DEU", "276"}, + {"Ghana", "Ghana (le)", "GH", "GHA", "288"}, + {"Gibraltar", "Gibraltar", "GI", "GIB", "292"}, + {"Kiribati", "Kiribati", "KI", "KIR", "296"}, + {"Greece", "Grèce (la)", "GR", "GRC", "300"}, + {"Greenland", "Groenland (le)", "GL", "GRL", "304"}, + {"Grenada", "Grenade (la)", "GD", "GRD", "308"}, + {"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"}, + {"Guam", "Guam", "GU", "GUM", "316"}, + {"Guatemala", "Guatemala (le)", "GT", "GTM", "320"}, + {"Guinea", "Guinée (la)", "GN", "GIN", "324"}, + {"Guyana", "Guyana (le)", "GY", "GUY", "328"}, + {"Haiti", "Haïti", "HT", "HTI", "332"}, + {"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"}, + {"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"}, + {"Honduras", "Honduras (le)", "HN", "HND", "340"}, + {"Hong Kong", "Hong Kong", "HK", "HKG", "344"}, + {"Hungary", "Hongrie (la)", "HU", "HUN", "348"}, + {"Iceland", "Islande (l')", "IS", "ISL", "352"}, + {"India", "Inde (l')", "IN", "IND", "356"}, + {"Indonesia", "Indonésie (l')", "ID", "IDN", "360"}, + {"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"}, + {"Iraq", "Iraq (l')", "IQ", "IRQ", "368"}, + {"Ireland", "Irlande (l')", "IE", "IRL", "372"}, + {"Israel", "Israël", "IL", "ISR", "376"}, + {"Italy", "Italie (l')", "IT", "ITA", "380"}, + {"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"}, + {"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"}, + {"Japan", "Japon (le)", "JP", "JPN", "392"}, + {"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"}, + {"Jordan", "Jordanie (la)", "JO", "JOR", "400"}, + {"Kenya", "Kenya (le)", "KE", "KEN", "404"}, + {"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"}, + {"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"}, + {"Kuwait", "Koweït (le)", "KW", "KWT", "414"}, + {"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"}, + {"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"}, + {"Lebanon", "Liban (le)", "LB", "LBN", "422"}, + {"Lesotho", "Lesotho (le)", "LS", "LSO", "426"}, + {"Latvia", "Lettonie (la)", "LV", "LVA", "428"}, + {"Liberia", "Libéria (le)", "LR", "LBR", "430"}, + {"Libya", "Libye (la)", "LY", "LBY", "434"}, + {"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"}, + {"Lithuania", "Lituanie (la)", "LT", "LTU", "440"}, + {"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"}, + {"Macao", "Macao", "MO", "MAC", "446"}, + {"Madagascar", "Madagascar", "MG", "MDG", "450"}, + {"Malawi", "Malawi (le)", "MW", "MWI", "454"}, + {"Malaysia", "Malaisie (la)", "MY", "MYS", "458"}, + {"Maldives", "Maldives (les)", "MV", "MDV", "462"}, + {"Mali", "Mali (le)", "ML", "MLI", "466"}, + {"Malta", "Malte", "MT", "MLT", "470"}, + {"Martinique", "Martinique (la)", "MQ", "MTQ", "474"}, + {"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"}, + {"Mauritius", "Maurice", "MU", "MUS", "480"}, + {"Mexico", "Mexique (le)", "MX", "MEX", "484"}, + {"Monaco", "Monaco", "MC", "MCO", "492"}, + {"Mongolia", "Mongolie (la)", "MN", "MNG", "496"}, + {"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"}, + {"Montenegro", "Monténégro (le)", "ME", "MNE", "499"}, + {"Montserrat", "Montserrat", "MS", "MSR", "500"}, + {"Morocco", "Maroc (le)", "MA", "MAR", "504"}, + {"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"}, + {"Oman", "Oman", "OM", "OMN", "512"}, + {"Namibia", "Namibie (la)", "NA", "NAM", "516"}, + {"Nauru", "Nauru", "NR", "NRU", "520"}, + {"Nepal", "Népal (le)", "NP", "NPL", "524"}, + {"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"}, + {"Curaçao", "Curaçao", "CW", "CUW", "531"}, + {"Aruba", "Aruba", "AW", "ABW", "533"}, + {"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"}, + {"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"}, + {"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"}, + {"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"}, + {"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"}, + {"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"}, + {"Niger (the)", "Niger (le)", "NE", "NER", "562"}, + {"Nigeria", "Nigéria (le)", "NG", "NGA", "566"}, + {"Niue", "Niue", "NU", "NIU", "570"}, + {"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"}, + {"Norway", "Norvège (la)", "NO", "NOR", "578"}, + {"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"}, + {"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"}, + {"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"}, + {"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"}, + {"Palau", "Palaos (les)", "PW", "PLW", "585"}, + {"Pakistan", "Pakistan (le)", "PK", "PAK", "586"}, + {"Panama", "Panama (le)", "PA", "PAN", "591"}, + {"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"}, + {"Paraguay", "Paraguay (le)", "PY", "PRY", "600"}, + {"Peru", "Pérou (le)", "PE", "PER", "604"}, + {"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"}, + {"Pitcairn", "Pitcairn", "PN", "PCN", "612"}, + {"Poland", "Pologne (la)", "PL", "POL", "616"}, + {"Portugal", "Portugal (le)", "PT", "PRT", "620"}, + {"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"}, + {"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"}, + {"Puerto Rico", "Porto Rico", "PR", "PRI", "630"}, + {"Qatar", "Qatar (le)", "QA", "QAT", "634"}, + {"Réunion", "Réunion (La)", "RE", "REU", "638"}, + {"Romania", "Roumanie (la)", "RO", "ROU", "642"}, + {"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"}, + {"Rwanda", "Rwanda (le)", "RW", "RWA", "646"}, + {"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"}, + {"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"}, + {"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"}, + {"Anguilla", "Anguilla", "AI", "AIA", "660"}, + {"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"}, + {"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"}, + {"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"}, + {"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"}, + {"San Marino", "Saint-Marin", "SM", "SMR", "674"}, + {"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"}, + {"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"}, + {"Senegal", "Sénégal (le)", "SN", "SEN", "686"}, + {"Serbia", "Serbie (la)", "RS", "SRB", "688"}, + {"Seychelles", "Seychelles (les)", "SC", "SYC", "690"}, + {"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"}, + {"Singapore", "Singapour", "SG", "SGP", "702"}, + {"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"}, + {"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"}, + {"Slovenia", "Slovénie (la)", "SI", "SVN", "705"}, + {"Somalia", "Somalie (la)", "SO", "SOM", "706"}, + {"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"}, + {"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"}, + {"Spain", "Espagne (l')", "ES", "ESP", "724"}, + {"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"}, + {"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"}, + {"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"}, + {"Suriname", "Suriname (le)", "SR", "SUR", "740"}, + {"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"}, + {"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"}, + {"Sweden", "Suède (la)", "SE", "SWE", "752"}, + {"Switzerland", "Suisse (la)", "CH", "CHE", "756"}, + {"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"}, + {"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"}, + {"Thailand", "Thaïlande (la)", "TH", "THA", "764"}, + {"Togo", "Togo (le)", "TG", "TGO", "768"}, + {"Tokelau", "Tokelau (les)", "TK", "TKL", "772"}, + {"Tonga", "Tonga (les)", "TO", "TON", "776"}, + {"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"}, + {"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"}, + {"Tunisia", "Tunisie (la)", "TN", "TUN", "788"}, + {"Turkey", "Turquie (la)", "TR", "TUR", "792"}, + {"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"}, + {"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"}, + {"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"}, + {"Uganda", "Ouganda (l')", "UG", "UGA", "800"}, + {"Ukraine", "Ukraine (l')", "UA", "UKR", "804"}, + {"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"}, + {"Egypt", "Égypte (l')", "EG", "EGY", "818"}, + {"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"}, + {"Guernsey", "Guernesey", "GG", "GGY", "831"}, + {"Jersey", "Jersey", "JE", "JEY", "832"}, + {"Isle of Man", "Île de Man", "IM", "IMN", "833"}, + {"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"}, + {"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"}, + {"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"}, + {"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"}, + {"Uruguay", "Uruguay (l')", "UY", "URY", "858"}, + {"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"}, + {"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"}, + {"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"}, + {"Samoa", "Samoa (le)", "WS", "WSM", "882"}, + {"Yemen", "Yémen (le)", "YE", "YEM", "887"}, + {"Zambia", "Zambie (la)", "ZM", "ZMB", "894"}, +} + +// ISO4217List is the list of ISO currency codes +var ISO4217List = []string{ + "AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", + "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD", + "CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK", + "DJF", "DKK", "DOP", "DZD", + "EGP", "ERN", "ETB", "EUR", + "FJD", "FKP", + "GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", + "HKD", "HNL", "HRK", "HTG", "HUF", + "IDR", "ILS", "INR", "IQD", "IRR", "ISK", + "JMD", "JOD", "JPY", + "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", + "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", + "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN", + "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", + "OMR", + "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", + "QAR", + "RON", "RSD", "RUB", "RWF", + "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "SVC", "SYP", "SZL", + "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", + "UAH", "UGX", "USD", "USN", "UYI", "UYU", "UZS", + "VEF", "VND", "VUV", + "WST", + "XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX", + "YER", + "ZAR", "ZMW", "ZWL", +} + +// ISO693Entry stores ISO language codes +type ISO693Entry struct { + Alpha3bCode string + Alpha2Code string + English string +} + +//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json +var ISO693List = []ISO693Entry{ + {Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"}, + {Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"}, + {Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"}, + {Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"}, + {Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"}, + {Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"}, + {Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"}, + {Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"}, + {Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"}, + {Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"}, + {Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"}, + {Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"}, + {Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"}, + {Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"}, + {Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"}, + {Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"}, + {Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"}, + {Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"}, + {Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"}, + {Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"}, + {Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"}, + {Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"}, + {Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"}, + {Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"}, + {Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"}, + {Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"}, + {Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"}, + {Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"}, + {Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"}, + {Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"}, + {Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"}, + {Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"}, + {Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"}, + {Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"}, + {Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"}, + {Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"}, + {Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"}, + {Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"}, + {Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"}, + {Alpha3bCode: "eng", Alpha2Code: "en", English: "English"}, + {Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"}, + {Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"}, + {Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"}, + {Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"}, + {Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"}, + {Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"}, + {Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"}, + {Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"}, + {Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"}, + {Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"}, + {Alpha3bCode: "ger", Alpha2Code: "de", English: "German"}, + {Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"}, + {Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"}, + {Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"}, + {Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"}, + {Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"}, + {Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"}, + {Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"}, + {Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"}, + {Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"}, + {Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"}, + {Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"}, + {Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"}, + {Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"}, + {Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"}, + {Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"}, + {Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"}, + {Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"}, + {Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"}, + {Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"}, + {Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"}, + {Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"}, + {Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"}, + {Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"}, + {Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"}, + {Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"}, + {Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"}, + {Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"}, + {Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"}, + {Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"}, + {Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"}, + {Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"}, + {Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"}, + {Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"}, + {Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"}, + {Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"}, + {Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"}, + {Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"}, + {Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"}, + {Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"}, + {Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"}, + {Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"}, + {Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"}, + {Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"}, + {Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"}, + {Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"}, + {Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"}, + {Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"}, + {Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"}, + {Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"}, + {Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"}, + {Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"}, + {Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"}, + {Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"}, + {Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"}, + {Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"}, + {Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"}, + {Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"}, + {Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"}, + {Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"}, + {Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"}, + {Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"}, + {Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"}, + {Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"}, + {Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"}, + {Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"}, + {Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"}, + {Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"}, + {Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"}, + {Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"}, + {Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"}, + {Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"}, + {Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"}, + {Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"}, + {Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"}, + {Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"}, + {Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"}, + {Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"}, + {Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"}, + {Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"}, + {Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"}, + {Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"}, + {Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"}, + {Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"}, + {Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"}, + {Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"}, + {Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"}, + {Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"}, + {Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"}, + {Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"}, + {Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"}, + {Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"}, + {Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"}, + {Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"}, + {Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"}, + {Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"}, + {Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"}, + {Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"}, + {Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"}, + {Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"}, + {Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"}, + {Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"}, + {Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"}, + {Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"}, + {Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"}, + {Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"}, + {Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"}, + {Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"}, + {Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"}, + {Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"}, + {Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"}, + {Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"}, + {Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"}, + {Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"}, + {Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"}, + {Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"}, + {Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"}, + {Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"}, + {Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"}, + {Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"}, + {Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"}, + {Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"}, + {Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"}, + {Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"}, + {Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"}, + {Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"}, + {Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"}, + {Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"}, + {Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"}, + {Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"}, + {Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"}, + {Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"}, + {Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"}, + {Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"}, +} diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/utils.go b/src/runtime/vendor/github.com/asaskevich/govalidator/utils.go new file mode 100644 index 000000000..a0b706a74 --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/utils.go @@ -0,0 +1,270 @@ +package govalidator + +import ( + "errors" + "fmt" + "html" + "math" + "path" + "regexp" + "strings" + "unicode" + "unicode/utf8" +) + +// Contains check if the string contains the substring. +func Contains(str, substring string) bool { + return strings.Contains(str, substring) +} + +// Matches check if string matches the pattern (pattern is regular expression) +// In case of error return false +func Matches(str, pattern string) bool { + match, _ := regexp.MatchString(pattern, str) + return match +} + +// LeftTrim trim characters from the left-side of the input. +// If second argument is empty, it's will be remove leading spaces. +func LeftTrim(str, chars string) string { + if chars == "" { + return strings.TrimLeftFunc(str, unicode.IsSpace) + } + r, _ := regexp.Compile("^[" + chars + "]+") + return r.ReplaceAllString(str, "") +} + +// RightTrim trim characters from the right-side of the input. +// If second argument is empty, it's will be remove spaces. +func RightTrim(str, chars string) string { + if chars == "" { + return strings.TrimRightFunc(str, unicode.IsSpace) + } + r, _ := regexp.Compile("[" + chars + "]+$") + return r.ReplaceAllString(str, "") +} + +// Trim trim characters from both sides of the input. +// If second argument is empty, it's will be remove spaces. +func Trim(str, chars string) string { + return LeftTrim(RightTrim(str, chars), chars) +} + +// WhiteList remove characters that do not appear in the whitelist. +func WhiteList(str, chars string) string { + pattern := "[^" + chars + "]+" + r, _ := regexp.Compile(pattern) + return r.ReplaceAllString(str, "") +} + +// BlackList remove characters that appear in the blacklist. +func BlackList(str, chars string) string { + pattern := "[" + chars + "]+" + r, _ := regexp.Compile(pattern) + return r.ReplaceAllString(str, "") +} + +// StripLow remove characters with a numerical value < 32 and 127, mostly control characters. +// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD). +func StripLow(str string, keepNewLines bool) string { + chars := "" + if keepNewLines { + chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F" + } else { + chars = "\x00-\x1F\x7F" + } + return BlackList(str, chars) +} + +// ReplacePattern replace regular expression pattern in string +func ReplacePattern(str, pattern, replace string) string { + r, _ := regexp.Compile(pattern) + return r.ReplaceAllString(str, replace) +} + +// Escape replace <, >, & and " with HTML entities. +var Escape = html.EscapeString + +func addSegment(inrune, segment []rune) []rune { + if len(segment) == 0 { + return inrune + } + if len(inrune) != 0 { + inrune = append(inrune, '_') + } + inrune = append(inrune, segment...) + return inrune +} + +// UnderscoreToCamelCase converts from underscore separated form to camel case form. +// Ex.: my_func => MyFunc +func UnderscoreToCamelCase(s string) string { + return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1) +} + +// CamelCaseToUnderscore converts from camel case form to underscore separated form. +// Ex.: MyFunc => my_func +func CamelCaseToUnderscore(str string) string { + var output []rune + var segment []rune + for _, r := range str { + + // not treat number as separate segment + if !unicode.IsLower(r) && string(r) != "_" && !unicode.IsNumber(r) { + output = addSegment(output, segment) + segment = nil + } + segment = append(segment, unicode.ToLower(r)) + } + output = addSegment(output, segment) + return string(output) +} + +// Reverse return reversed string +func Reverse(s string) string { + r := []rune(s) + for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 { + r[i], r[j] = r[j], r[i] + } + return string(r) +} + +// GetLines split string by "\n" and return array of lines +func GetLines(s string) []string { + return strings.Split(s, "\n") +} + +// GetLine return specified line of multiline string +func GetLine(s string, index int) (string, error) { + lines := GetLines(s) + if index < 0 || index >= len(lines) { + return "", errors.New("line index out of bounds") + } + return lines[index], nil +} + +// RemoveTags remove all tags from HTML string +func RemoveTags(s string) string { + return ReplacePattern(s, "<[^>]*>", "") +} + +// SafeFileName return safe string that can be used in file names +func SafeFileName(str string) string { + name := strings.ToLower(str) + name = path.Clean(path.Base(name)) + name = strings.Trim(name, " ") + separators, err := regexp.Compile(`[ &_=+:]`) + if err == nil { + name = separators.ReplaceAllString(name, "-") + } + legal, err := regexp.Compile(`[^[:alnum:]-.]`) + if err == nil { + name = legal.ReplaceAllString(name, "") + } + for strings.Contains(name, "--") { + name = strings.Replace(name, "--", "-", -1) + } + return name +} + +// NormalizeEmail canonicalize an email address. +// The local part of the email address is lowercased for all domains; the hostname is always lowercased and +// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail). +// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and +// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are +// normalized to @gmail.com. +func NormalizeEmail(str string) (string, error) { + if !IsEmail(str) { + return "", fmt.Errorf("%s is not an email", str) + } + parts := strings.Split(str, "@") + parts[0] = strings.ToLower(parts[0]) + parts[1] = strings.ToLower(parts[1]) + if parts[1] == "gmail.com" || parts[1] == "googlemail.com" { + parts[1] = "gmail.com" + parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0] + } + return strings.Join(parts, "@"), nil +} + +// Truncate a string to the closest length without breaking words. +func Truncate(str string, length int, ending string) string { + var aftstr, befstr string + if len(str) > length { + words := strings.Fields(str) + before, present := 0, 0 + for i := range words { + befstr = aftstr + before = present + aftstr = aftstr + words[i] + " " + present = len(aftstr) + if present > length && i != 0 { + if (length - before) < (present - length) { + return Trim(befstr, " /\\.,\"'#!?&@+-") + ending + } + return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending + } + } + } + + return str +} + +// PadLeft pad left side of string if size of string is less then indicated pad length +func PadLeft(str string, padStr string, padLen int) string { + return buildPadStr(str, padStr, padLen, true, false) +} + +// PadRight pad right side of string if size of string is less then indicated pad length +func PadRight(str string, padStr string, padLen int) string { + return buildPadStr(str, padStr, padLen, false, true) +} + +// PadBoth pad sides of string if size of string is less then indicated pad length +func PadBoth(str string, padStr string, padLen int) string { + return buildPadStr(str, padStr, padLen, true, true) +} + +// PadString either left, right or both sides, not the padding string can be unicode and more then one +// character +func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string { + + // When padded length is less then the current string size + if padLen < utf8.RuneCountInString(str) { + return str + } + + padLen -= utf8.RuneCountInString(str) + + targetLen := padLen + + targetLenLeft := targetLen + targetLenRight := targetLen + if padLeft && padRight { + targetLenLeft = padLen / 2 + targetLenRight = padLen - targetLenLeft + } + + strToRepeatLen := utf8.RuneCountInString(padStr) + + repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen))) + repeatedString := strings.Repeat(padStr, repeatTimes) + + leftSide := "" + if padLeft { + leftSide = repeatedString[0:targetLenLeft] + } + + rightSide := "" + if padRight { + rightSide = repeatedString[0:targetLenRight] + } + + return leftSide + str + rightSide +} + +// TruncatingErrorf removes extra args from fmt.Errorf if not formatted in the str object +func TruncatingErrorf(str string, args ...interface{}) error { + n := strings.Count(str, "%s") + return fmt.Errorf(str, args[:n]...) +} diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/validator.go b/src/runtime/vendor/github.com/asaskevich/govalidator/validator.go new file mode 100644 index 000000000..b18bbcb4c --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/validator.go @@ -0,0 +1,1278 @@ +// Package govalidator is package of validators and sanitizers for strings, structs and collections. +package govalidator + +import ( + "bytes" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "fmt" + "io/ioutil" + "net" + "net/url" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "time" + "unicode" + "unicode/utf8" +) + +var ( + fieldsRequiredByDefault bool + nilPtrAllowedByRequired = false + notNumberRegexp = regexp.MustCompile("[^0-9]+") + whiteSpacesAndMinus = regexp.MustCompile(`[\s-]+`) + paramsRegexp = regexp.MustCompile(`\(.*\)$`) +) + +const maxURLRuneCount = 2083 +const minURLRuneCount = 3 +const RF3339WithoutZone = "2006-01-02T15:04:05" + +// SetFieldsRequiredByDefault causes validation to fail when struct fields +// do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). +// This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter): +// type exampleStruct struct { +// Name string `` +// Email string `valid:"email"` +// This, however, will only fail when Email is empty or an invalid email address: +// type exampleStruct2 struct { +// Name string `valid:"-"` +// Email string `valid:"email"` +// Lastly, this will only fail when Email is an invalid email address but not when it's empty: +// type exampleStruct2 struct { +// Name string `valid:"-"` +// Email string `valid:"email,optional"` +func SetFieldsRequiredByDefault(value bool) { + fieldsRequiredByDefault = value +} + +// SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required. +// The validation will still reject ptr fields in their zero value state. Example with this enabled: +// type exampleStruct struct { +// Name *string `valid:"required"` +// With `Name` set to "", this will be considered invalid input and will cause a validation error. +// With `Name` set to nil, this will be considered valid by validation. +// By default this is disabled. +func SetNilPtrAllowedByRequired(value bool) { + nilPtrAllowedByRequired = value +} + +// IsEmail check if the string is an email. +func IsEmail(str string) bool { + // TODO uppercase letters are not supported + return rxEmail.MatchString(str) +} + +// IsExistingEmail check if the string is an email of existing domain +func IsExistingEmail(email string) bool { + + if len(email) < 6 || len(email) > 254 { + return false + } + at := strings.LastIndex(email, "@") + if at <= 0 || at > len(email)-3 { + return false + } + user := email[:at] + host := email[at+1:] + if len(user) > 64 { + return false + } + if userDotRegexp.MatchString(user) || !userRegexp.MatchString(user) || !hostRegexp.MatchString(host) { + return false + } + switch host { + case "localhost", "example.com": + return true + } + if _, err := net.LookupMX(host); err != nil { + if _, err := net.LookupIP(host); err != nil { + return false + } + } + + return true +} + +// IsURL check if the string is an URL. +func IsURL(str string) bool { + if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") { + return false + } + strTemp := str + if strings.Contains(str, ":") && !strings.Contains(str, "://") { + // support no indicated urlscheme but with colon for port number + // http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString + strTemp = "http://" + str + } + u, err := url.Parse(strTemp) + if err != nil { + return false + } + if strings.HasPrefix(u.Host, ".") { + return false + } + if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { + return false + } + return rxURL.MatchString(str) +} + +// IsRequestURL check if the string rawurl, assuming +// it was received in an HTTP request, is a valid +// URL confirm to RFC 3986 +func IsRequestURL(rawurl string) bool { + url, err := url.ParseRequestURI(rawurl) + if err != nil { + return false //Couldn't even parse the rawurl + } + if len(url.Scheme) == 0 { + return false //No Scheme found + } + return true +} + +// IsRequestURI check if the string rawurl, assuming +// it was received in an HTTP request, is an +// absolute URI or an absolute path. +func IsRequestURI(rawurl string) bool { + _, err := url.ParseRequestURI(rawurl) + return err == nil +} + +// IsAlpha check if the string contains only letters (a-zA-Z). Empty string is valid. +func IsAlpha(str string) bool { + if IsNull(str) { + return true + } + return rxAlpha.MatchString(str) +} + +//IsUTFLetter check if the string contains only unicode letter characters. +//Similar to IsAlpha but for all languages. Empty string is valid. +func IsUTFLetter(str string) bool { + if IsNull(str) { + return true + } + + for _, c := range str { + if !unicode.IsLetter(c) { + return false + } + } + return true + +} + +// IsAlphanumeric check if the string contains only letters and numbers. Empty string is valid. +func IsAlphanumeric(str string) bool { + if IsNull(str) { + return true + } + return rxAlphanumeric.MatchString(str) +} + +// IsUTFLetterNumeric check if the string contains only unicode letters and numbers. Empty string is valid. +func IsUTFLetterNumeric(str string) bool { + if IsNull(str) { + return true + } + for _, c := range str { + if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok + return false + } + } + return true + +} + +// IsNumeric check if the string contains only numbers. Empty string is valid. +func IsNumeric(str string) bool { + if IsNull(str) { + return true + } + return rxNumeric.MatchString(str) +} + +// IsUTFNumeric check if the string contains only unicode numbers of any kind. +// Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid. +func IsUTFNumeric(str string) bool { + if IsNull(str) { + return true + } + if strings.IndexAny(str, "+-") > 0 { + return false + } + if len(str) > 1 { + str = strings.TrimPrefix(str, "-") + str = strings.TrimPrefix(str, "+") + } + for _, c := range str { + if !unicode.IsNumber(c) { //numbers && minus sign are ok + return false + } + } + return true + +} + +// IsUTFDigit check if the string contains only unicode radix-10 decimal digits. Empty string is valid. +func IsUTFDigit(str string) bool { + if IsNull(str) { + return true + } + if strings.IndexAny(str, "+-") > 0 { + return false + } + if len(str) > 1 { + str = strings.TrimPrefix(str, "-") + str = strings.TrimPrefix(str, "+") + } + for _, c := range str { + if !unicode.IsDigit(c) { //digits && minus sign are ok + return false + } + } + return true + +} + +// IsHexadecimal check if the string is a hexadecimal number. +func IsHexadecimal(str string) bool { + return rxHexadecimal.MatchString(str) +} + +// IsHexcolor check if the string is a hexadecimal color. +func IsHexcolor(str string) bool { + return rxHexcolor.MatchString(str) +} + +// IsRGBcolor check if the string is a valid RGB color in form rgb(RRR, GGG, BBB). +func IsRGBcolor(str string) bool { + return rxRGBcolor.MatchString(str) +} + +// IsLowerCase check if the string is lowercase. Empty string is valid. +func IsLowerCase(str string) bool { + if IsNull(str) { + return true + } + return str == strings.ToLower(str) +} + +// IsUpperCase check if the string is uppercase. Empty string is valid. +func IsUpperCase(str string) bool { + if IsNull(str) { + return true + } + return str == strings.ToUpper(str) +} + +// HasLowerCase check if the string contains at least 1 lowercase. Empty string is valid. +func HasLowerCase(str string) bool { + if IsNull(str) { + return true + } + return rxHasLowerCase.MatchString(str) +} + +// HasUpperCase check if the string contians as least 1 uppercase. Empty string is valid. +func HasUpperCase(str string) bool { + if IsNull(str) { + return true + } + return rxHasUpperCase.MatchString(str) +} + +// IsInt check if the string is an integer. Empty string is valid. +func IsInt(str string) bool { + if IsNull(str) { + return true + } + return rxInt.MatchString(str) +} + +// IsFloat check if the string is a float. +func IsFloat(str string) bool { + return str != "" && rxFloat.MatchString(str) +} + +// IsDivisibleBy check if the string is a number that's divisible by another. +// If second argument is not valid integer or zero, it's return false. +// Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero). +func IsDivisibleBy(str, num string) bool { + f, _ := ToFloat(str) + p := int64(f) + q, _ := ToInt(num) + if q == 0 { + return false + } + return (p == 0) || (p%q == 0) +} + +// IsNull check if the string is null. +func IsNull(str string) bool { + return len(str) == 0 +} + +// HasWhitespaceOnly checks the string only contains whitespace +func HasWhitespaceOnly(str string) bool { + return len(str) > 0 && rxHasWhitespaceOnly.MatchString(str) +} + +// HasWhitespace checks if the string contains any whitespace +func HasWhitespace(str string) bool { + return len(str) > 0 && rxHasWhitespace.MatchString(str) +} + +// IsByteLength check if the string's length (in bytes) falls in a range. +func IsByteLength(str string, min, max int) bool { + return len(str) >= min && len(str) <= max +} + +// IsUUIDv3 check if the string is a UUID version 3. +func IsUUIDv3(str string) bool { + return rxUUID3.MatchString(str) +} + +// IsUUIDv4 check if the string is a UUID version 4. +func IsUUIDv4(str string) bool { + return rxUUID4.MatchString(str) +} + +// IsUUIDv5 check if the string is a UUID version 5. +func IsUUIDv5(str string) bool { + return rxUUID5.MatchString(str) +} + +// IsUUID check if the string is a UUID (version 3, 4 or 5). +func IsUUID(str string) bool { + return rxUUID.MatchString(str) +} + +// IsCreditCard check if the string is a credit card. +func IsCreditCard(str string) bool { + sanitized := notNumberRegexp.ReplaceAllString(str, "") + if !rxCreditCard.MatchString(sanitized) { + return false + } + var sum int64 + var digit string + var tmpNum int64 + var shouldDouble bool + for i := len(sanitized) - 1; i >= 0; i-- { + digit = sanitized[i:(i + 1)] + tmpNum, _ = ToInt(digit) + if shouldDouble { + tmpNum *= 2 + if tmpNum >= 10 { + sum += ((tmpNum % 10) + 1) + } else { + sum += tmpNum + } + } else { + sum += tmpNum + } + shouldDouble = !shouldDouble + } + + return sum%10 == 0 +} + +// IsISBN10 check if the string is an ISBN version 10. +func IsISBN10(str string) bool { + return IsISBN(str, 10) +} + +// IsISBN13 check if the string is an ISBN version 13. +func IsISBN13(str string) bool { + return IsISBN(str, 13) +} + +// IsISBN check if the string is an ISBN (version 10 or 13). +// If version value is not equal to 10 or 13, it will be check both variants. +func IsISBN(str string, version int) bool { + sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") + var checksum int32 + var i int32 + if version == 10 { + if !rxISBN10.MatchString(sanitized) { + return false + } + for i = 0; i < 9; i++ { + checksum += (i + 1) * int32(sanitized[i]-'0') + } + if sanitized[9] == 'X' { + checksum += 10 * 10 + } else { + checksum += 10 * int32(sanitized[9]-'0') + } + if checksum%11 == 0 { + return true + } + return false + } else if version == 13 { + if !rxISBN13.MatchString(sanitized) { + return false + } + factor := []int32{1, 3} + for i = 0; i < 12; i++ { + checksum += factor[i%2] * int32(sanitized[i]-'0') + } + return (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0 + } + return IsISBN(str, 10) || IsISBN(str, 13) +} + +// IsJSON check if the string is valid JSON (note: uses json.Unmarshal). +func IsJSON(str string) bool { + var js json.RawMessage + return json.Unmarshal([]byte(str), &js) == nil +} + +// IsMultibyte check if the string contains one or more multibyte chars. Empty string is valid. +func IsMultibyte(str string) bool { + if IsNull(str) { + return true + } + return rxMultibyte.MatchString(str) +} + +// IsASCII check if the string contains ASCII chars only. Empty string is valid. +func IsASCII(str string) bool { + if IsNull(str) { + return true + } + return rxASCII.MatchString(str) +} + +// IsPrintableASCII check if the string contains printable ASCII chars only. Empty string is valid. +func IsPrintableASCII(str string) bool { + if IsNull(str) { + return true + } + return rxPrintableASCII.MatchString(str) +} + +// IsFullWidth check if the string contains any full-width chars. Empty string is valid. +func IsFullWidth(str string) bool { + if IsNull(str) { + return true + } + return rxFullWidth.MatchString(str) +} + +// IsHalfWidth check if the string contains any half-width chars. Empty string is valid. +func IsHalfWidth(str string) bool { + if IsNull(str) { + return true + } + return rxHalfWidth.MatchString(str) +} + +// IsVariableWidth check if the string contains a mixture of full and half-width chars. Empty string is valid. +func IsVariableWidth(str string) bool { + if IsNull(str) { + return true + } + return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str) +} + +// IsBase64 check if a string is base64 encoded. +func IsBase64(str string) bool { + return rxBase64.MatchString(str) +} + +// IsFilePath check is a string is Win or Unix file path and returns it's type. +func IsFilePath(str string) (bool, int) { + if rxWinPath.MatchString(str) { + //check windows path limit see: + // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath + if len(str[3:]) > 32767 { + return false, Win + } + return true, Win + } else if rxUnixPath.MatchString(str) { + return true, Unix + } + return false, Unknown +} + +// IsDataURI checks if a string is base64 encoded data URI such as an image +func IsDataURI(str string) bool { + dataURI := strings.Split(str, ",") + if !rxDataURI.MatchString(dataURI[0]) { + return false + } + return IsBase64(dataURI[1]) +} + +// IsISO3166Alpha2 checks if a string is valid two-letter country code +func IsISO3166Alpha2(str string) bool { + for _, entry := range ISO3166List { + if str == entry.Alpha2Code { + return true + } + } + return false +} + +// IsISO3166Alpha3 checks if a string is valid three-letter country code +func IsISO3166Alpha3(str string) bool { + for _, entry := range ISO3166List { + if str == entry.Alpha3Code { + return true + } + } + return false +} + +// IsISO693Alpha2 checks if a string is valid two-letter language code +func IsISO693Alpha2(str string) bool { + for _, entry := range ISO693List { + if str == entry.Alpha2Code { + return true + } + } + return false +} + +// IsISO693Alpha3b checks if a string is valid three-letter language code +func IsISO693Alpha3b(str string) bool { + for _, entry := range ISO693List { + if str == entry.Alpha3bCode { + return true + } + } + return false +} + +// IsDNSName will validate the given string as a DNS name +func IsDNSName(str string) bool { + if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 { + // constraints already violated + return false + } + return !IsIP(str) && rxDNSName.MatchString(str) +} + +// IsHash checks if a string is a hash of type algorithm. +// Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b'] +func IsHash(str string, algorithm string) bool { + len := "0" + algo := strings.ToLower(algorithm) + + if algo == "crc32" || algo == "crc32b" { + len = "8" + } else if algo == "md5" || algo == "md4" || algo == "ripemd128" || algo == "tiger128" { + len = "32" + } else if algo == "sha1" || algo == "ripemd160" || algo == "tiger160" { + len = "40" + } else if algo == "tiger192" { + len = "48" + } else if algo == "sha256" { + len = "64" + } else if algo == "sha384" { + len = "96" + } else if algo == "sha512" { + len = "128" + } else { + return false + } + + return Matches(str, "^[a-f0-9]{"+len+"}$") +} + +// IsDialString validates the given string for usage with the various Dial() functions +func IsDialString(str string) bool { + + if h, p, err := net.SplitHostPort(str); err == nil && h != "" && p != "" && (IsDNSName(h) || IsIP(h)) && IsPort(p) { + return true + } + + return false +} + +// IsIP checks if a string is either IP version 4 or 6. +func IsIP(str string) bool { + return net.ParseIP(str) != nil +} + +// IsPort checks if a string represents a valid port +func IsPort(str string) bool { + if i, err := strconv.Atoi(str); err == nil && i > 0 && i < 65536 { + return true + } + return false +} + +// IsIPv4 check if the string is an IP version 4. +func IsIPv4(str string) bool { + ip := net.ParseIP(str) + return ip != nil && strings.Contains(str, ".") +} + +// IsIPv6 check if the string is an IP version 6. +func IsIPv6(str string) bool { + ip := net.ParseIP(str) + return ip != nil && strings.Contains(str, ":") +} + +// IsCIDR check if the string is an valid CIDR notiation (IPV4 & IPV6) +func IsCIDR(str string) bool { + _, _, err := net.ParseCIDR(str) + return err == nil +} + +// IsMAC check if a string is valid MAC address. +// Possible MAC formats: +// 01:23:45:67:89:ab +// 01:23:45:67:89:ab:cd:ef +// 01-23-45-67-89-ab +// 01-23-45-67-89-ab-cd-ef +// 0123.4567.89ab +// 0123.4567.89ab.cdef +func IsMAC(str string) bool { + _, err := net.ParseMAC(str) + return err == nil +} + +// IsHost checks if the string is a valid IP (both v4 and v6) or a valid DNS name +func IsHost(str string) bool { + return IsIP(str) || IsDNSName(str) +} + +// IsMongoID check if the string is a valid hex-encoded representation of a MongoDB ObjectId. +func IsMongoID(str string) bool { + return rxHexadecimal.MatchString(str) && (len(str) == 24) +} + +// IsLatitude check if a string is valid latitude. +func IsLatitude(str string) bool { + return rxLatitude.MatchString(str) +} + +// IsLongitude check if a string is valid longitude. +func IsLongitude(str string) bool { + return rxLongitude.MatchString(str) +} + +// IsRsaPublicKey check if a string is valid public key with provided length +func IsRsaPublicKey(str string, keylen int) bool { + bb := bytes.NewBufferString(str) + pemBytes, err := ioutil.ReadAll(bb) + if err != nil { + return false + } + block, _ := pem.Decode(pemBytes) + if block != nil && block.Type != "PUBLIC KEY" { + return false + } + var der []byte + + if block != nil { + der = block.Bytes + } else { + der, err = base64.StdEncoding.DecodeString(str) + if err != nil { + return false + } + } + + key, err := x509.ParsePKIXPublicKey(der) + if err != nil { + return false + } + pubkey, ok := key.(*rsa.PublicKey) + if !ok { + return false + } + bitlen := len(pubkey.N.Bytes()) * 8 + return bitlen == int(keylen) +} + +func toJSONName(tag string) string { + if tag == "" { + return "" + } + + // JSON name always comes first. If there's no options then split[0] is + // JSON name, if JSON name is not set, then split[0] is an empty string. + split := strings.SplitN(tag, ",", 2) + + name := split[0] + + // However it is possible that the field is skipped when + // (de-)serializing from/to JSON, in which case assume that there is no + // tag name to use + if name == "-" { + return "" + } + return name +} + +func PrependPathToErrors(err error, path string) error { + switch err2 := err.(type) { + case Error: + err2.Path = append([]string{path}, err2.Path...) + return err2 + case Errors: + errors := err2.Errors() + for i, err3 := range errors { + errors[i] = PrependPathToErrors(err3, path) + } + return err2 + } + fmt.Println(err) + return err +} + +// ValidateStruct use tags for fields. +// result will be equal to `false` if there are any errors. +func ValidateStruct(s interface{}) (bool, error) { + if s == nil { + return true, nil + } + result := true + var err error + val := reflect.ValueOf(s) + if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr { + val = val.Elem() + } + // we only accept structs + if val.Kind() != reflect.Struct { + return false, fmt.Errorf("function only accepts structs; got %s", val.Kind()) + } + var errs Errors + for i := 0; i < val.NumField(); i++ { + valueField := val.Field(i) + typeField := val.Type().Field(i) + if typeField.PkgPath != "" { + continue // Private field + } + structResult := true + if valueField.Kind() == reflect.Interface { + valueField = valueField.Elem() + } + if (valueField.Kind() == reflect.Struct || + (valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) && + typeField.Tag.Get(tagName) != "-" { + var err error + structResult, err = ValidateStruct(valueField.Interface()) + if err != nil { + err = PrependPathToErrors(err, typeField.Name) + errs = append(errs, err) + } + } + resultField, err2 := typeCheck(valueField, typeField, val, nil) + if err2 != nil { + + // Replace structure name with JSON name if there is a tag on the variable + jsonTag := toJSONName(typeField.Tag.Get("json")) + if jsonTag != "" { + switch jsonError := err2.(type) { + case Error: + jsonError.Name = jsonTag + err2 = jsonError + case Errors: + for i2, err3 := range jsonError { + switch customErr := err3.(type) { + case Error: + customErr.Name = jsonTag + jsonError[i2] = customErr + } + } + + err2 = jsonError + } + } + + errs = append(errs, err2) + } + result = result && resultField && structResult + } + if len(errs) > 0 { + err = errs + } + return result, err +} + +// parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""} +func parseTagIntoMap(tag string) tagOptionsMap { + optionsMap := make(tagOptionsMap) + options := strings.Split(tag, ",") + + for i, option := range options { + option = strings.TrimSpace(option) + + validationOptions := strings.Split(option, "~") + if !isValidTag(validationOptions[0]) { + continue + } + if len(validationOptions) == 2 { + optionsMap[validationOptions[0]] = tagOption{validationOptions[0], validationOptions[1], i} + } else { + optionsMap[validationOptions[0]] = tagOption{validationOptions[0], "", i} + } + } + return optionsMap +} + +func isValidTag(s string) bool { + if s == "" { + return false + } + for _, c := range s { + switch { + case strings.ContainsRune("\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ ", c): + // Backslash and quote chars are reserved, but + // otherwise any punctuation chars are allowed + // in a tag name. + default: + if !unicode.IsLetter(c) && !unicode.IsDigit(c) { + return false + } + } + } + return true +} + +// IsSSN will validate the given string as a U.S. Social Security Number +func IsSSN(str string) bool { + if str == "" || len(str) != 11 { + return false + } + return rxSSN.MatchString(str) +} + +// IsSemver check if string is valid semantic version +func IsSemver(str string) bool { + return rxSemver.MatchString(str) +} + +// IsTime check if string is valid according to given format +func IsTime(str string, format string) bool { + _, err := time.Parse(format, str) + return err == nil +} + +// IsRFC3339 check if string is valid timestamp value according to RFC3339 +func IsRFC3339(str string) bool { + return IsTime(str, time.RFC3339) +} + +// IsRFC3339WithoutZone check if string is valid timestamp value according to RFC3339 which excludes the timezone. +func IsRFC3339WithoutZone(str string) bool { + return IsTime(str, RF3339WithoutZone) +} + +// IsISO4217 check if string is valid ISO currency code +func IsISO4217(str string) bool { + for _, currency := range ISO4217List { + if str == currency { + return true + } + } + + return false +} + +// ByteLength check string's length +func ByteLength(str string, params ...string) bool { + if len(params) == 2 { + min, _ := ToInt(params[0]) + max, _ := ToInt(params[1]) + return len(str) >= int(min) && len(str) <= int(max) + } + + return false +} + +// RuneLength check string's length +// Alias for StringLength +func RuneLength(str string, params ...string) bool { + return StringLength(str, params...) +} + +// IsRsaPub check whether string is valid RSA key +// Alias for IsRsaPublicKey +func IsRsaPub(str string, params ...string) bool { + if len(params) == 1 { + len, _ := ToInt(params[0]) + return IsRsaPublicKey(str, int(len)) + } + + return false +} + +// StringMatches checks if a string matches a given pattern. +func StringMatches(s string, params ...string) bool { + if len(params) == 1 { + pattern := params[0] + return Matches(s, pattern) + } + return false +} + +// StringLength check string's length (including multi byte strings) +func StringLength(str string, params ...string) bool { + + if len(params) == 2 { + strLength := utf8.RuneCountInString(str) + min, _ := ToInt(params[0]) + max, _ := ToInt(params[1]) + return strLength >= int(min) && strLength <= int(max) + } + + return false +} + +// Range check string's length +func Range(str string, params ...string) bool { + if len(params) == 2 { + value, _ := ToFloat(str) + min, _ := ToFloat(params[0]) + max, _ := ToFloat(params[1]) + return InRange(value, min, max) + } + + return false +} + +func isInRaw(str string, params ...string) bool { + if len(params) == 1 { + rawParams := params[0] + + parsedParams := strings.Split(rawParams, "|") + + return IsIn(str, parsedParams...) + } + + return false +} + +// IsIn check if string str is a member of the set of strings params +func IsIn(str string, params ...string) bool { + for _, param := range params { + if str == param { + return true + } + } + + return false +} + +func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) { + if nilPtrAllowedByRequired { + k := v.Kind() + if (k == reflect.Ptr || k == reflect.Interface) && v.IsNil() { + return true, nil + } + } + + if requiredOption, isRequired := options["required"]; isRequired { + if len(requiredOption.customErrorMessage) > 0 { + return false, Error{t.Name, fmt.Errorf(requiredOption.customErrorMessage), true, "required", []string{}} + } + return false, Error{t.Name, fmt.Errorf("non zero value required"), false, "required", []string{}} + } else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional { + return false, Error{t.Name, fmt.Errorf("Missing required field"), false, "required", []string{}} + } + // not required and empty is valid + return true, nil +} + +func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) { + if !v.IsValid() { + return false, nil + } + + tag := t.Tag.Get(tagName) + + // Check if the field should be ignored + switch tag { + case "": + if v.Kind() != reflect.Slice && v.Kind() != reflect.Map { + if !fieldsRequiredByDefault { + return true, nil + } + return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required", []string{}} + } + case "-": + return true, nil + } + + isRootType := false + if options == nil { + isRootType = true + options = parseTagIntoMap(tag) + } + + if isEmptyValue(v) { + // an empty value is not validated, check only required + isValid, resultErr = checkRequired(v, t, options) + for key := range options { + delete(options, key) + } + return isValid, resultErr + } + + var customTypeErrors Errors + optionsOrder := options.orderedKeys() + for _, validatorName := range optionsOrder { + validatorStruct := options[validatorName] + if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok { + delete(options, validatorName) + + if result := validatefunc(v.Interface(), o.Interface()); !result { + if len(validatorStruct.customErrorMessage) > 0 { + customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: TruncatingErrorf(validatorStruct.customErrorMessage, fmt.Sprint(v), validatorName), CustomErrorMessageExists: true, Validator: stripParams(validatorName)}) + continue + } + customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false, Validator: stripParams(validatorName)}) + } + } + } + + if len(customTypeErrors.Errors()) > 0 { + return false, customTypeErrors + } + + if isRootType { + // Ensure that we've checked the value by all specified validators before report that the value is valid + defer func() { + delete(options, "optional") + delete(options, "required") + + if isValid && resultErr == nil && len(options) != 0 { + optionsOrder := options.orderedKeys() + for _, validator := range optionsOrder { + isValid = false + resultErr = Error{t.Name, fmt.Errorf( + "The following validator is invalid or can't be applied to the field: %q", validator), false, stripParams(validator), []string{}} + return + } + } + }() + } + + switch v.Kind() { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Float32, reflect.Float64, + reflect.String: + // for each tag option check the map of validator functions + for _, validatorSpec := range optionsOrder { + validatorStruct := options[validatorSpec] + var negate bool + validator := validatorSpec + customMsgExists := len(validatorStruct.customErrorMessage) > 0 + + // Check whether the tag looks like '!something' or 'something' + if validator[0] == '!' { + validator = validator[1:] + negate = true + } + + // Check for param validators + for key, value := range ParamTagRegexMap { + ps := value.FindStringSubmatch(validator) + if len(ps) == 0 { + continue + } + + validatefunc, ok := ParamTagMap[key] + if !ok { + continue + } + + delete(options, validatorSpec) + + switch v.Kind() { + case reflect.String, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + + field := fmt.Sprint(v) // make value into string, then validate with regex + if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) { + if customMsgExists { + return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + if negate { + return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + default: + // type not yet supported, fail + return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false, stripParams(validatorSpec), []string{}} + } + } + + if validatefunc, ok := TagMap[validator]; ok { + delete(options, validatorSpec) + + switch v.Kind() { + case reflect.String, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + field := fmt.Sprint(v) // make value into string, then validate with regex + if result := validatefunc(field); !result && !negate || result && negate { + if customMsgExists { + return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + if negate { + return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} + } + default: + //Not Yet Supported Types (Fail here!) + err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v) + return false, Error{t.Name, err, false, stripParams(validatorSpec), []string{}} + } + } + } + return true, nil + case reflect.Map: + if v.Type().Key().Kind() != reflect.String { + return false, &UnsupportedTypeError{v.Type()} + } + var sv stringValues + sv = v.MapKeys() + sort.Sort(sv) + result := true + for i, k := range sv { + var resultItem bool + var err error + if v.MapIndex(k).Kind() != reflect.Struct { + resultItem, err = typeCheck(v.MapIndex(k), t, o, options) + if err != nil { + return false, err + } + } else { + resultItem, err = ValidateStruct(v.MapIndex(k).Interface()) + if err != nil { + err = PrependPathToErrors(err, t.Name+"."+sv[i].Interface().(string)) + return false, err + } + } + result = result && resultItem + } + return result, nil + case reflect.Slice, reflect.Array: + result := true + for i := 0; i < v.Len(); i++ { + var resultItem bool + var err error + if v.Index(i).Kind() != reflect.Struct { + resultItem, err = typeCheck(v.Index(i), t, o, options) + if err != nil { + return false, err + } + } else { + resultItem, err = ValidateStruct(v.Index(i).Interface()) + if err != nil { + err = PrependPathToErrors(err, t.Name+"."+strconv.Itoa(i)) + return false, err + } + } + result = result && resultItem + } + return result, nil + case reflect.Interface: + // If the value is an interface then encode its element + if v.IsNil() { + return true, nil + } + return ValidateStruct(v.Interface()) + case reflect.Ptr: + // If the value is a pointer then check its element + if v.IsNil() { + return true, nil + } + return typeCheck(v.Elem(), t, o, options) + case reflect.Struct: + return ValidateStruct(v.Interface()) + default: + return false, &UnsupportedTypeError{v.Type()} + } +} + +func stripParams(validatorString string) string { + return paramsRegexp.ReplaceAllString(validatorString, "") +} + +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.String, reflect.Array: + return v.Len() == 0 + case reflect.Map, reflect.Slice: + return v.Len() == 0 || v.IsNil() + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + + return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) +} + +// ErrorByField returns error for specified field of the struct +// validated by ValidateStruct or empty string if there are no errors +// or this field doesn't exists or doesn't have any errors. +func ErrorByField(e error, field string) string { + if e == nil { + return "" + } + return ErrorsByField(e)[field] +} + +// ErrorsByField returns map of errors of the struct validated +// by ValidateStruct or empty map if there are no errors. +func ErrorsByField(e error) map[string]string { + m := make(map[string]string) + if e == nil { + return m + } + // prototype for ValidateStruct + + switch e.(type) { + case Error: + m[e.(Error).Name] = e.(Error).Err.Error() + case Errors: + for _, item := range e.(Errors).Errors() { + n := ErrorsByField(item) + for k, v := range n { + m[k] = v + } + } + } + + return m +} + +// Error returns string equivalent for reflect.Type +func (e *UnsupportedTypeError) Error() string { + return "validator: unsupported type: " + e.Type.String() +} + +func (sv stringValues) Len() int { return len(sv) } +func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } +func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) } +func (sv stringValues) get(i int) string { return sv[i].String() } diff --git a/src/runtime/vendor/github.com/asaskevich/govalidator/wercker.yml b/src/runtime/vendor/github.com/asaskevich/govalidator/wercker.yml new file mode 100644 index 000000000..cac7a5fcf --- /dev/null +++ b/src/runtime/vendor/github.com/asaskevich/govalidator/wercker.yml @@ -0,0 +1,15 @@ +box: golang +build: + steps: + - setup-go-workspace + + - script: + name: go get + code: | + go version + go get -t ./... + + - script: + name: go test + code: | + go test -race ./... diff --git a/src/runtime/vendor/github.com/blang/semver/.travis.yml b/src/runtime/vendor/github.com/blang/semver/.travis.yml new file mode 100644 index 000000000..7319bdeae --- /dev/null +++ b/src/runtime/vendor/github.com/blang/semver/.travis.yml @@ -0,0 +1,25 @@ +language: go +matrix: + include: + - go: 1.4.x + - go: 1.5.x + - go: 1.6.x + - go: 1.7.x + - go: 1.8.x + - go: 1.9.x + - go: 1.10.x + - go: 1.11.x + - go: tip + allow_failures: + - go: tip +install: +- go get golang.org/x/tools/cmd/cover +- go get github.com/mattn/goveralls +script: +- echo "Test and track coverage" ; $HOME/gopath/bin/goveralls -package "." -service=travis-ci + -repotoken=$COVERALLS_TOKEN +- echo "Build examples" ; cd examples && go build +- echo "Check if gofmt'd" ; diff -u <(echo -n) <(gofmt -d -s .) +env: + global: + secure: HroGEAUQpVq9zX1b1VIkraLiywhGbzvNnTZq2TMxgK7JHP8xqNplAeF1izrR2i4QLL9nsY+9WtYss4QuPvEtZcVHUobw6XnL6radF7jS1LgfYZ9Y7oF+zogZ2I5QUMRLGA7rcxQ05s7mKq3XZQfeqaNts4bms/eZRefWuaFZbkw= diff --git a/src/runtime/vendor/github.com/blang/semver/LICENSE b/src/runtime/vendor/github.com/blang/semver/LICENSE new file mode 100644 index 000000000..5ba5c86fc --- /dev/null +++ b/src/runtime/vendor/github.com/blang/semver/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2014 Benedikt Lang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/src/runtime/vendor/github.com/blang/semver/README.md b/src/runtime/vendor/github.com/blang/semver/README.md new file mode 100644 index 000000000..e05f98651 --- /dev/null +++ b/src/runtime/vendor/github.com/blang/semver/README.md @@ -0,0 +1,194 @@ +semver for golang [![Build Status](https://travis-ci.org/blang/semver.svg?branch=master)](https://travis-ci.org/blang/semver) [![GoDoc](https://godoc.org/github.com/blang/semver?status.svg)](https://godoc.org/github.com/blang/semver) [![Coverage Status](https://img.shields.io/coveralls/blang/semver.svg)](https://coveralls.io/r/blang/semver?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/blang/semver)](https://goreportcard.com/report/github.com/blang/semver) +====== + +semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`. + +Usage +----- +```bash +$ go get github.com/blang/semver +``` +Note: Always vendor your dependencies or fix on a specific version tag. + +```go +import github.com/blang/semver +v1, err := semver.Make("1.0.0-beta") +v2, err := semver.Make("2.0.0-beta") +v1.Compare(v2) +``` + +Also check the [GoDocs](http://godoc.org/github.com/blang/semver). + +Why should I use this lib? +----- + +- Fully spec compatible +- No reflection +- No regex +- Fully tested (Coverage >99%) +- Readable parsing/validation errors +- Fast (See [Benchmarks](#benchmarks)) +- Only Stdlib +- Uses values instead of pointers +- Many features, see below + + +Features +----- + +- Parsing and validation at all levels +- Comparator-like comparisons +- Compare Helper Methods +- InPlace manipulation +- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1` +- Wildcards `>=1.x`, `<=2.5.x` +- Sortable (implements sort.Interface) +- database/sql compatible (sql.Scanner/Valuer) +- encoding/json compatible (json.Marshaler/Unmarshaler) + +Ranges +------ + +A `Range` is a set of conditions which specify which versions satisfy the range. + +A condition is composed of an operator and a version. The supported operators are: + +- `<1.0.0` Less than `1.0.0` +- `<=1.0.0` Less than or equal to `1.0.0` +- `>1.0.0` Greater than `1.0.0` +- `>=1.0.0` Greater than or equal to `1.0.0` +- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0` +- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`. + +Note that spaces between the operator and the version will be gracefully tolerated. + +A `Range` can link multiple `Ranges` separated by space: + +Ranges can be linked by logical AND: + + - `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0` + - `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2` + +Ranges can also be linked by logical OR: + + - `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x` + +AND has a higher precedence than OR. It's not possible to use brackets. + +Ranges can be combined by both AND and OR + + - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1` + +Range usage: + +``` +v, err := semver.Parse("1.2.3") +expectedRange, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0") +if expectedRange(v) { + //valid +} + +``` + +Example +----- + +Have a look at full examples in [examples/main.go](examples/main.go) + +```go +import github.com/blang/semver + +v, err := semver.Make("0.0.1-alpha.preview+123.github") +fmt.Printf("Major: %d\n", v.Major) +fmt.Printf("Minor: %d\n", v.Minor) +fmt.Printf("Patch: %d\n", v.Patch) +fmt.Printf("Pre: %s\n", v.Pre) +fmt.Printf("Build: %s\n", v.Build) + +// Prerelease versions array +if len(v.Pre) > 0 { + fmt.Println("Prerelease versions:") + for i, pre := range v.Pre { + fmt.Printf("%d: %q\n", i, pre) + } +} + +// Build meta data array +if len(v.Build) > 0 { + fmt.Println("Build meta data:") + for i, build := range v.Build { + fmt.Printf("%d: %q\n", i, build) + } +} + +v001, err := semver.Make("0.0.1") +// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE +v001.GT(v) == true +v.LT(v001) == true +v.GTE(v) == true +v.LTE(v) == true + +// Or use v.Compare(v2) for comparisons (-1, 0, 1): +v001.Compare(v) == 1 +v.Compare(v001) == -1 +v.Compare(v) == 0 + +// Manipulate Version in place: +v.Pre[0], err = semver.NewPRVersion("beta") +if err != nil { + fmt.Printf("Error parsing pre release version: %q", err) +} + +fmt.Println("\nValidate versions:") +v.Build[0] = "?" + +err = v.Validate() +if err != nil { + fmt.Printf("Validation failed: %s\n", err) +} +``` + + +Benchmarks +----- + + BenchmarkParseSimple-4 5000000 390 ns/op 48 B/op 1 allocs/op + BenchmarkParseComplex-4 1000000 1813 ns/op 256 B/op 7 allocs/op + BenchmarkParseAverage-4 1000000 1171 ns/op 163 B/op 4 allocs/op + BenchmarkStringSimple-4 20000000 119 ns/op 16 B/op 1 allocs/op + BenchmarkStringLarger-4 10000000 206 ns/op 32 B/op 2 allocs/op + BenchmarkStringComplex-4 5000000 324 ns/op 80 B/op 3 allocs/op + BenchmarkStringAverage-4 5000000 273 ns/op 53 B/op 2 allocs/op + BenchmarkValidateSimple-4 200000000 9.33 ns/op 0 B/op 0 allocs/op + BenchmarkValidateComplex-4 3000000 469 ns/op 0 B/op 0 allocs/op + BenchmarkValidateAverage-4 5000000 256 ns/op 0 B/op 0 allocs/op + BenchmarkCompareSimple-4 100000000 11.8 ns/op 0 B/op 0 allocs/op + BenchmarkCompareComplex-4 50000000 30.8 ns/op 0 B/op 0 allocs/op + BenchmarkCompareAverage-4 30000000 41.5 ns/op 0 B/op 0 allocs/op + BenchmarkSort-4 3000000 419 ns/op 256 B/op 2 allocs/op + BenchmarkRangeParseSimple-4 2000000 850 ns/op 192 B/op 5 allocs/op + BenchmarkRangeParseAverage-4 1000000 1677 ns/op 400 B/op 10 allocs/op + BenchmarkRangeParseComplex-4 300000 5214 ns/op 1440 B/op 30 allocs/op + BenchmarkRangeMatchSimple-4 50000000 25.6 ns/op 0 B/op 0 allocs/op + BenchmarkRangeMatchAverage-4 30000000 56.4 ns/op 0 B/op 0 allocs/op + BenchmarkRangeMatchComplex-4 10000000 153 ns/op 0 B/op 0 allocs/op + +See benchmark cases at [semver_test.go](semver_test.go) + + +Motivation +----- + +I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like. + + +Contribution +----- + +Feel free to make a pull request. For bigger changes create a issue first to discuss about it. + + +License +----- + +See [LICENSE](LICENSE) file. diff --git a/src/runtime/vendor/github.com/blang/semver/go.mod b/src/runtime/vendor/github.com/blang/semver/go.mod new file mode 100644 index 000000000..d0083058e --- /dev/null +++ b/src/runtime/vendor/github.com/blang/semver/go.mod @@ -0,0 +1 @@ +module github.com/blang/semver diff --git a/src/runtime/vendor/github.com/blang/semver/json.go b/src/runtime/vendor/github.com/blang/semver/json.go new file mode 100644 index 000000000..a74bf7c44 --- /dev/null +++ b/src/runtime/vendor/github.com/blang/semver/json.go @@ -0,0 +1,23 @@ +package semver + +import ( + "encoding/json" +) + +// MarshalJSON implements the encoding/json.Marshaler interface. +func (v Version) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +// UnmarshalJSON implements the encoding/json.Unmarshaler interface. +func (v *Version) UnmarshalJSON(data []byte) (err error) { + var versionString string + + if err = json.Unmarshal(data, &versionString); err != nil { + return + } + + *v, err = Parse(versionString) + + return +} diff --git a/src/runtime/vendor/github.com/blang/semver/package.json b/src/runtime/vendor/github.com/blang/semver/package.json new file mode 100644 index 000000000..1cf8ebdd9 --- /dev/null +++ b/src/runtime/vendor/github.com/blang/semver/package.json @@ -0,0 +1,17 @@ +{ + "author": "blang", + "bugs": { + "URL": "https://github.com/blang/semver/issues", + "url": "https://github.com/blang/semver/issues" + }, + "gx": { + "dvcsimport": "github.com/blang/semver" + }, + "gxVersion": "0.10.0", + "language": "go", + "license": "MIT", + "name": "semver", + "releaseCmd": "git commit -a -m \"gx publish $VERSION\"", + "version": "3.5.1" +} + diff --git a/src/runtime/vendor/github.com/blang/semver/range.go b/src/runtime/vendor/github.com/blang/semver/range.go new file mode 100644 index 000000000..95f7139b9 --- /dev/null +++ b/src/runtime/vendor/github.com/blang/semver/range.go @@ -0,0 +1,416 @@ +package semver + +import ( + "fmt" + "strconv" + "strings" + "unicode" +) + +type wildcardType int + +const ( + noneWildcard wildcardType = iota + majorWildcard wildcardType = 1 + minorWildcard wildcardType = 2 + patchWildcard wildcardType = 3 +) + +func wildcardTypefromInt(i int) wildcardType { + switch i { + case 1: + return majorWildcard + case 2: + return minorWildcard + case 3: + return patchWildcard + default: + return noneWildcard + } +} + +type comparator func(Version, Version) bool + +var ( + compEQ comparator = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == 0 + } + compNE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) != 0 + } + compGT = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == 1 + } + compGE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) >= 0 + } + compLT = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == -1 + } + compLE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) <= 0 + } +) + +type versionRange struct { + v Version + c comparator +} + +// rangeFunc creates a Range from the given versionRange. +func (vr *versionRange) rangeFunc() Range { + return Range(func(v Version) bool { + return vr.c(v, vr.v) + }) +} + +// Range represents a range of versions. +// A Range can be used to check if a Version satisfies it: +// +// range, err := semver.ParseRange(">1.0.0 <2.0.0") +// range(semver.MustParse("1.1.1") // returns true +type Range func(Version) bool + +// OR combines the existing Range with another Range using logical OR. +func (rf Range) OR(f Range) Range { + return Range(func(v Version) bool { + return rf(v) || f(v) + }) +} + +// AND combines the existing Range with another Range using logical AND. +func (rf Range) AND(f Range) Range { + return Range(func(v Version) bool { + return rf(v) && f(v) + }) +} + +// ParseRange parses a range and returns a Range. +// If the range could not be parsed an error is returned. +// +// Valid ranges are: +// - "<1.0.0" +// - "<=1.0.0" +// - ">1.0.0" +// - ">=1.0.0" +// - "1.0.0", "=1.0.0", "==1.0.0" +// - "!1.0.0", "!=1.0.0" +// +// A Range can consist of multiple ranges separated by space: +// Ranges can be linked by logical AND: +// - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0" +// - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2 +// +// Ranges can also be linked by logical OR: +// - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x" +// +// AND has a higher precedence than OR. It's not possible to use brackets. +// +// Ranges can be combined by both AND and OR +// +// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1` +func ParseRange(s string) (Range, error) { + parts := splitAndTrim(s) + orParts, err := splitORParts(parts) + if err != nil { + return nil, err + } + expandedParts, err := expandWildcardVersion(orParts) + if err != nil { + return nil, err + } + var orFn Range + for _, p := range expandedParts { + var andFn Range + for _, ap := range p { + opStr, vStr, err := splitComparatorVersion(ap) + if err != nil { + return nil, err + } + vr, err := buildVersionRange(opStr, vStr) + if err != nil { + return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err) + } + rf := vr.rangeFunc() + + // Set function + if andFn == nil { + andFn = rf + } else { // Combine with existing function + andFn = andFn.AND(rf) + } + } + if orFn == nil { + orFn = andFn + } else { + orFn = orFn.OR(andFn) + } + + } + return orFn, nil +} + +// splitORParts splits the already cleaned parts by '||'. +// Checks for invalid positions of the operator and returns an +// error if found. +func splitORParts(parts []string) ([][]string, error) { + var ORparts [][]string + last := 0 + for i, p := range parts { + if p == "||" { + if i == 0 { + return nil, fmt.Errorf("First element in range is '||'") + } + ORparts = append(ORparts, parts[last:i]) + last = i + 1 + } + } + if last == len(parts) { + return nil, fmt.Errorf("Last element in range is '||'") + } + ORparts = append(ORparts, parts[last:]) + return ORparts, nil +} + +// buildVersionRange takes a slice of 2: operator and version +// and builds a versionRange, otherwise an error. +func buildVersionRange(opStr, vStr string) (*versionRange, error) { + c := parseComparator(opStr) + if c == nil { + return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, "")) + } + v, err := Parse(vStr) + if err != nil { + return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err) + } + + return &versionRange{ + v: v, + c: c, + }, nil + +} + +// inArray checks if a byte is contained in an array of bytes +func inArray(s byte, list []byte) bool { + for _, el := range list { + if el == s { + return true + } + } + return false +} + +// splitAndTrim splits a range string by spaces and cleans whitespaces +func splitAndTrim(s string) (result []string) { + last := 0 + var lastChar byte + excludeFromSplit := []byte{'>', '<', '='} + for i := 0; i < len(s); i++ { + if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) { + if last < i-1 { + result = append(result, s[last:i]) + } + last = i + 1 + } else if s[i] != ' ' { + lastChar = s[i] + } + } + if last < len(s)-1 { + result = append(result, s[last:]) + } + + for i, v := range result { + result[i] = strings.Replace(v, " ", "", -1) + } + + // parts := strings.Split(s, " ") + // for _, x := range parts { + // if s := strings.TrimSpace(x); len(s) != 0 { + // result = append(result, s) + // } + // } + return +} + +// splitComparatorVersion splits the comparator from the version. +// Input must be free of leading or trailing spaces. +func splitComparatorVersion(s string) (string, string, error) { + i := strings.IndexFunc(s, unicode.IsDigit) + if i == -1 { + return "", "", fmt.Errorf("Could not get version from string: %q", s) + } + return strings.TrimSpace(s[0:i]), s[i:], nil +} + +// getWildcardType will return the type of wildcard that the +// passed version contains +func getWildcardType(vStr string) wildcardType { + parts := strings.Split(vStr, ".") + nparts := len(parts) + wildcard := parts[nparts-1] + + possibleWildcardType := wildcardTypefromInt(nparts) + if wildcard == "x" { + return possibleWildcardType + } + + return noneWildcard +} + +// createVersionFromWildcard will convert a wildcard version +// into a regular version, replacing 'x's with '0's, handling +// special cases like '1.x.x' and '1.x' +func createVersionFromWildcard(vStr string) string { + // handle 1.x.x + vStr2 := strings.Replace(vStr, ".x.x", ".x", 1) + vStr2 = strings.Replace(vStr2, ".x", ".0", 1) + parts := strings.Split(vStr2, ".") + + // handle 1.x + if len(parts) == 2 { + return vStr2 + ".0" + } + + return vStr2 +} + +// incrementMajorVersion will increment the major version +// of the passed version +func incrementMajorVersion(vStr string) (string, error) { + parts := strings.Split(vStr, ".") + i, err := strconv.Atoi(parts[0]) + if err != nil { + return "", err + } + parts[0] = strconv.Itoa(i + 1) + + return strings.Join(parts, "."), nil +} + +// incrementMajorVersion will increment the minor version +// of the passed version +func incrementMinorVersion(vStr string) (string, error) { + parts := strings.Split(vStr, ".") + i, err := strconv.Atoi(parts[1]) + if err != nil { + return "", err + } + parts[1] = strconv.Itoa(i + 1) + + return strings.Join(parts, "."), nil +} + +// expandWildcardVersion will expand wildcards inside versions +// following these rules: +// +// * when dealing with patch wildcards: +// >= 1.2.x will become >= 1.2.0 +// <= 1.2.x will become < 1.3.0 +// > 1.2.x will become >= 1.3.0 +// < 1.2.x will become < 1.2.0 +// != 1.2.x will become < 1.2.0 >= 1.3.0 +// +// * when dealing with minor wildcards: +// >= 1.x will become >= 1.0.0 +// <= 1.x will become < 2.0.0 +// > 1.x will become >= 2.0.0 +// < 1.0 will become < 1.0.0 +// != 1.x will become < 1.0.0 >= 2.0.0 +// +// * when dealing with wildcards without +// version operator: +// 1.2.x will become >= 1.2.0 < 1.3.0 +// 1.x will become >= 1.0.0 < 2.0.0 +func expandWildcardVersion(parts [][]string) ([][]string, error) { + var expandedParts [][]string + for _, p := range parts { + var newParts []string + for _, ap := range p { + if strings.Contains(ap, "x") { + opStr, vStr, err := splitComparatorVersion(ap) + if err != nil { + return nil, err + } + + versionWildcardType := getWildcardType(vStr) + flatVersion := createVersionFromWildcard(vStr) + + var resultOperator string + var shouldIncrementVersion bool + switch opStr { + case ">": + resultOperator = ">=" + shouldIncrementVersion = true + case ">=": + resultOperator = ">=" + case "<": + resultOperator = "<" + case "<=": + resultOperator = "<" + shouldIncrementVersion = true + case "", "=", "==": + newParts = append(newParts, ">="+flatVersion) + resultOperator = "<" + shouldIncrementVersion = true + case "!=", "!": + newParts = append(newParts, "<"+flatVersion) + resultOperator = ">=" + shouldIncrementVersion = true + } + + var resultVersion string + if shouldIncrementVersion { + switch versionWildcardType { + case patchWildcard: + resultVersion, _ = incrementMinorVersion(flatVersion) + case minorWildcard: + resultVersion, _ = incrementMajorVersion(flatVersion) + } + } else { + resultVersion = flatVersion + } + + ap = resultOperator + resultVersion + } + newParts = append(newParts, ap) + } + expandedParts = append(expandedParts, newParts) + } + + return expandedParts, nil +} + +func parseComparator(s string) comparator { + switch s { + case "==": + fallthrough + case "": + fallthrough + case "=": + return compEQ + case ">": + return compGT + case ">=": + return compGE + case "<": + return compLT + case "<=": + return compLE + case "!": + fallthrough + case "!=": + return compNE + } + + return nil +} + +// MustParseRange is like ParseRange but panics if the range cannot be parsed. +func MustParseRange(s string) Range { + r, err := ParseRange(s) + if err != nil { + panic(`semver: ParseRange(` + s + `): ` + err.Error()) + } + return r +} diff --git a/src/runtime/vendor/github.com/blang/semver/semver.go b/src/runtime/vendor/github.com/blang/semver/semver.go new file mode 100644 index 000000000..4165bc791 --- /dev/null +++ b/src/runtime/vendor/github.com/blang/semver/semver.go @@ -0,0 +1,455 @@ +package semver + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +const ( + numbers string = "0123456789" + alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" + alphanum = alphas + numbers +) + +// SpecVersion is the latest fully supported spec version of semver +var SpecVersion = Version{ + Major: 2, + Minor: 0, + Patch: 0, +} + +// Version represents a semver compatible version +type Version struct { + Major uint64 + Minor uint64 + Patch uint64 + Pre []PRVersion + Build []string //No Precedence +} + +// Version to string +func (v Version) String() string { + b := make([]byte, 0, 5) + b = strconv.AppendUint(b, v.Major, 10) + b = append(b, '.') + b = strconv.AppendUint(b, v.Minor, 10) + b = append(b, '.') + b = strconv.AppendUint(b, v.Patch, 10) + + if len(v.Pre) > 0 { + b = append(b, '-') + b = append(b, v.Pre[0].String()...) + + for _, pre := range v.Pre[1:] { + b = append(b, '.') + b = append(b, pre.String()...) + } + } + + if len(v.Build) > 0 { + b = append(b, '+') + b = append(b, v.Build[0]...) + + for _, build := range v.Build[1:] { + b = append(b, '.') + b = append(b, build...) + } + } + + return string(b) +} + +// Equals checks if v is equal to o. +func (v Version) Equals(o Version) bool { + return (v.Compare(o) == 0) +} + +// EQ checks if v is equal to o. +func (v Version) EQ(o Version) bool { + return (v.Compare(o) == 0) +} + +// NE checks if v is not equal to o. +func (v Version) NE(o Version) bool { + return (v.Compare(o) != 0) +} + +// GT checks if v is greater than o. +func (v Version) GT(o Version) bool { + return (v.Compare(o) == 1) +} + +// GTE checks if v is greater than or equal to o. +func (v Version) GTE(o Version) bool { + return (v.Compare(o) >= 0) +} + +// GE checks if v is greater than or equal to o. +func (v Version) GE(o Version) bool { + return (v.Compare(o) >= 0) +} + +// LT checks if v is less than o. +func (v Version) LT(o Version) bool { + return (v.Compare(o) == -1) +} + +// LTE checks if v is less than or equal to o. +func (v Version) LTE(o Version) bool { + return (v.Compare(o) <= 0) +} + +// LE checks if v is less than or equal to o. +func (v Version) LE(o Version) bool { + return (v.Compare(o) <= 0) +} + +// Compare compares Versions v to o: +// -1 == v is less than o +// 0 == v is equal to o +// 1 == v is greater than o +func (v Version) Compare(o Version) int { + if v.Major != o.Major { + if v.Major > o.Major { + return 1 + } + return -1 + } + if v.Minor != o.Minor { + if v.Minor > o.Minor { + return 1 + } + return -1 + } + if v.Patch != o.Patch { + if v.Patch > o.Patch { + return 1 + } + return -1 + } + + // Quick comparison if a version has no prerelease versions + if len(v.Pre) == 0 && len(o.Pre) == 0 { + return 0 + } else if len(v.Pre) == 0 && len(o.Pre) > 0 { + return 1 + } else if len(v.Pre) > 0 && len(o.Pre) == 0 { + return -1 + } + + i := 0 + for ; i < len(v.Pre) && i < len(o.Pre); i++ { + if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 { + continue + } else if comp == 1 { + return 1 + } else { + return -1 + } + } + + // If all pr versions are the equal but one has further prversion, this one greater + if i == len(v.Pre) && i == len(o.Pre) { + return 0 + } else if i == len(v.Pre) && i < len(o.Pre) { + return -1 + } else { + return 1 + } + +} + +// IncrementPatch increments the patch version +func (v *Version) IncrementPatch() error { + if v.Major == 0 { + return fmt.Errorf("Patch version can not be incremented for %q", v.String()) + } + v.Patch += 1 + return nil +} + +// IncrementMinor increments the minor version +func (v *Version) IncrementMinor() error { + if v.Major == 0 { + return fmt.Errorf("Minor version can not be incremented for %q", v.String()) + } + v.Minor += 1 + v.Patch = 0 + return nil +} + +// IncrementMajor increments the major version +func (v *Version) IncrementMajor() error { + if v.Major == 0 { + return fmt.Errorf("Major version can not be incremented for %q", v.String()) + } + v.Major += 1 + v.Minor = 0 + v.Patch = 0 + return nil +} + +// Validate validates v and returns error in case +func (v Version) Validate() error { + // Major, Minor, Patch already validated using uint64 + + for _, pre := range v.Pre { + if !pre.IsNum { //Numeric prerelease versions already uint64 + if len(pre.VersionStr) == 0 { + return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr) + } + if !containsOnly(pre.VersionStr, alphanum) { + return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr) + } + } + } + + for _, build := range v.Build { + if len(build) == 0 { + return fmt.Errorf("Build meta data can not be empty %q", build) + } + if !containsOnly(build, alphanum) { + return fmt.Errorf("Invalid character(s) found in build meta data %q", build) + } + } + + return nil +} + +// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error +func New(s string) (vp *Version, err error) { + v, err := Parse(s) + vp = &v + return +} + +// Make is an alias for Parse, parses version string and returns a validated Version or error +func Make(s string) (Version, error) { + return Parse(s) +} + +// ParseTolerant allows for certain version specifications that do not strictly adhere to semver +// specs to be parsed by this library. It does so by normalizing versions before passing them to +// Parse(). It currently trims spaces, removes a "v" prefix, adds a 0 patch number to versions +// with only major and minor components specified, and removes leading 0s. +func ParseTolerant(s string) (Version, error) { + s = strings.TrimSpace(s) + s = strings.TrimPrefix(s, "v") + + // Split into major.minor.(patch+pr+meta) + parts := strings.SplitN(s, ".", 3) + // Remove leading zeros. + for i, p := range parts { + if len(p) > 1 { + parts[i] = strings.TrimPrefix(p, "0") + } + } + // Fill up shortened versions. + if len(parts) < 3 { + if strings.ContainsAny(parts[len(parts)-1], "+-") { + return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data") + } + for len(parts) < 3 { + parts = append(parts, "0") + } + } + s = strings.Join(parts, ".") + + return Parse(s) +} + +// Parse parses version string and returns a validated Version or error +func Parse(s string) (Version, error) { + if len(s) == 0 { + return Version{}, errors.New("Version string empty") + } + + // Split into major.minor.(patch+pr+meta) + parts := strings.SplitN(s, ".", 3) + if len(parts) != 3 { + return Version{}, errors.New("No Major.Minor.Patch elements found") + } + + // Major + if !containsOnly(parts[0], numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0]) + } + if hasLeadingZeroes(parts[0]) { + return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0]) + } + major, err := strconv.ParseUint(parts[0], 10, 64) + if err != nil { + return Version{}, err + } + + // Minor + if !containsOnly(parts[1], numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1]) + } + if hasLeadingZeroes(parts[1]) { + return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1]) + } + minor, err := strconv.ParseUint(parts[1], 10, 64) + if err != nil { + return Version{}, err + } + + v := Version{} + v.Major = major + v.Minor = minor + + var build, prerelease []string + patchStr := parts[2] + + if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 { + build = strings.Split(patchStr[buildIndex+1:], ".") + patchStr = patchStr[:buildIndex] + } + + if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 { + prerelease = strings.Split(patchStr[preIndex+1:], ".") + patchStr = patchStr[:preIndex] + } + + if !containsOnly(patchStr, numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr) + } + if hasLeadingZeroes(patchStr) { + return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr) + } + patch, err := strconv.ParseUint(patchStr, 10, 64) + if err != nil { + return Version{}, err + } + + v.Patch = patch + + // Prerelease + for _, prstr := range prerelease { + parsedPR, err := NewPRVersion(prstr) + if err != nil { + return Version{}, err + } + v.Pre = append(v.Pre, parsedPR) + } + + // Build meta data + for _, str := range build { + if len(str) == 0 { + return Version{}, errors.New("Build meta data is empty") + } + if !containsOnly(str, alphanum) { + return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str) + } + v.Build = append(v.Build, str) + } + + return v, nil +} + +// MustParse is like Parse but panics if the version cannot be parsed. +func MustParse(s string) Version { + v, err := Parse(s) + if err != nil { + panic(`semver: Parse(` + s + `): ` + err.Error()) + } + return v +} + +// PRVersion represents a PreRelease Version +type PRVersion struct { + VersionStr string + VersionNum uint64 + IsNum bool +} + +// NewPRVersion creates a new valid prerelease version +func NewPRVersion(s string) (PRVersion, error) { + if len(s) == 0 { + return PRVersion{}, errors.New("Prerelease is empty") + } + v := PRVersion{} + if containsOnly(s, numbers) { + if hasLeadingZeroes(s) { + return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s) + } + num, err := strconv.ParseUint(s, 10, 64) + + // Might never be hit, but just in case + if err != nil { + return PRVersion{}, err + } + v.VersionNum = num + v.IsNum = true + } else if containsOnly(s, alphanum) { + v.VersionStr = s + v.IsNum = false + } else { + return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s) + } + return v, nil +} + +// IsNumeric checks if prerelease-version is numeric +func (v PRVersion) IsNumeric() bool { + return v.IsNum +} + +// Compare compares two PreRelease Versions v and o: +// -1 == v is less than o +// 0 == v is equal to o +// 1 == v is greater than o +func (v PRVersion) Compare(o PRVersion) int { + if v.IsNum && !o.IsNum { + return -1 + } else if !v.IsNum && o.IsNum { + return 1 + } else if v.IsNum && o.IsNum { + if v.VersionNum == o.VersionNum { + return 0 + } else if v.VersionNum > o.VersionNum { + return 1 + } else { + return -1 + } + } else { // both are Alphas + if v.VersionStr == o.VersionStr { + return 0 + } else if v.VersionStr > o.VersionStr { + return 1 + } else { + return -1 + } + } +} + +// PreRelease version to string +func (v PRVersion) String() string { + if v.IsNum { + return strconv.FormatUint(v.VersionNum, 10) + } + return v.VersionStr +} + +func containsOnly(s string, set string) bool { + return strings.IndexFunc(s, func(r rune) bool { + return !strings.ContainsRune(set, r) + }) == -1 +} + +func hasLeadingZeroes(s string) bool { + return len(s) > 1 && s[0] == '0' +} + +// NewBuildVersion creates a new valid build version +func NewBuildVersion(s string) (string, error) { + if len(s) == 0 { + return "", errors.New("Buildversion is empty") + } + if !containsOnly(s, alphanum) { + return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s) + } + return s, nil +} diff --git a/src/runtime/vendor/github.com/blang/semver/sort.go b/src/runtime/vendor/github.com/blang/semver/sort.go new file mode 100644 index 000000000..e18f88082 --- /dev/null +++ b/src/runtime/vendor/github.com/blang/semver/sort.go @@ -0,0 +1,28 @@ +package semver + +import ( + "sort" +) + +// Versions represents multiple versions. +type Versions []Version + +// Len returns length of version collection +func (s Versions) Len() int { + return len(s) +} + +// Swap swaps two versions inside the collection by its indices +func (s Versions) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Less checks if version at index i is less than version at index j +func (s Versions) Less(i, j int) bool { + return s[i].LT(s[j]) +} + +// Sort sorts a slice of versions +func Sort(versions []Version) { + sort.Sort(Versions(versions)) +} diff --git a/src/runtime/vendor/github.com/blang/semver/sql.go b/src/runtime/vendor/github.com/blang/semver/sql.go new file mode 100644 index 000000000..db958134f --- /dev/null +++ b/src/runtime/vendor/github.com/blang/semver/sql.go @@ -0,0 +1,30 @@ +package semver + +import ( + "database/sql/driver" + "fmt" +) + +// Scan implements the database/sql.Scanner interface. +func (v *Version) Scan(src interface{}) (err error) { + var str string + switch src := src.(type) { + case string: + str = src + case []byte: + str = string(src) + default: + return fmt.Errorf("version.Scan: cannot convert %T to string", src) + } + + if t, err := Parse(str); err == nil { + *v = t + } + + return +} + +// Value implements the database/sql/driver.Valuer interface. +func (v Version) Value() (driver.Value, error) { + return v.String(), nil +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/.gitignore b/src/runtime/vendor/github.com/cilium/ebpf/.gitignore new file mode 100644 index 000000000..f1c181ec9 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/.gitignore @@ -0,0 +1,12 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out diff --git a/src/runtime/vendor/github.com/cilium/ebpf/CODE_OF_CONDUCT.md b/src/runtime/vendor/github.com/cilium/ebpf/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..8e42838c5 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at nathanjsweet at gmail dot com or i at lmb dot io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/src/runtime/vendor/github.com/cilium/ebpf/LICENSE b/src/runtime/vendor/github.com/cilium/ebpf/LICENSE new file mode 100644 index 000000000..c637ae99c --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2017 Nathan Sweet +Copyright (c) 2018, 2019 Cloudflare +Copyright (c) 2019 Authors of Cilium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/runtime/vendor/github.com/cilium/ebpf/abi.go b/src/runtime/vendor/github.com/cilium/ebpf/abi.go new file mode 100644 index 000000000..fd6139c59 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/abi.go @@ -0,0 +1,207 @@ +package ebpf + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "syscall" + + "github.com/cilium/ebpf/internal" + + "golang.org/x/xerrors" +) + +// MapABI are the attributes of a Map which are available across all supported kernels. +type MapABI struct { + Type MapType + KeySize uint32 + ValueSize uint32 + MaxEntries uint32 + Flags uint32 +} + +func newMapABIFromSpec(spec *MapSpec) *MapABI { + return &MapABI{ + spec.Type, + spec.KeySize, + spec.ValueSize, + spec.MaxEntries, + spec.Flags, + } +} + +func newMapABIFromFd(fd *internal.FD) (string, *MapABI, error) { + info, err := bpfGetMapInfoByFD(fd) + if err != nil { + if xerrors.Is(err, syscall.EINVAL) { + abi, err := newMapABIFromProc(fd) + return "", abi, err + } + return "", nil, err + } + + return "", &MapABI{ + MapType(info.mapType), + info.keySize, + info.valueSize, + info.maxEntries, + info.flags, + }, nil +} + +func newMapABIFromProc(fd *internal.FD) (*MapABI, error) { + var abi MapABI + err := scanFdInfo(fd, map[string]interface{}{ + "map_type": &abi.Type, + "key_size": &abi.KeySize, + "value_size": &abi.ValueSize, + "max_entries": &abi.MaxEntries, + "map_flags": &abi.Flags, + }) + if err != nil { + return nil, err + } + return &abi, nil +} + +// Equal returns true if two ABIs have the same values. +func (abi *MapABI) Equal(other *MapABI) bool { + switch { + case abi.Type != other.Type: + return false + case abi.KeySize != other.KeySize: + return false + case abi.ValueSize != other.ValueSize: + return false + case abi.MaxEntries != other.MaxEntries: + return false + case abi.Flags != other.Flags: + return false + default: + return true + } +} + +// ProgramABI are the attributes of a Program which are available across all supported kernels. +type ProgramABI struct { + Type ProgramType +} + +func newProgramABIFromSpec(spec *ProgramSpec) *ProgramABI { + return &ProgramABI{ + spec.Type, + } +} + +func newProgramABIFromFd(fd *internal.FD) (string, *ProgramABI, error) { + info, err := bpfGetProgInfoByFD(fd) + if err != nil { + if xerrors.Is(err, syscall.EINVAL) { + return newProgramABIFromProc(fd) + } + + return "", nil, err + } + + var name string + if bpfName := internal.CString(info.name[:]); bpfName != "" { + name = bpfName + } else { + name = internal.CString(info.tag[:]) + } + + return name, &ProgramABI{ + Type: ProgramType(info.progType), + }, nil +} + +func newProgramABIFromProc(fd *internal.FD) (string, *ProgramABI, error) { + var ( + abi ProgramABI + name string + ) + + err := scanFdInfo(fd, map[string]interface{}{ + "prog_type": &abi.Type, + "prog_tag": &name, + }) + if xerrors.Is(err, errMissingFields) { + return "", nil, &internal.UnsupportedFeatureError{ + Name: "reading ABI from /proc/self/fdinfo", + MinimumVersion: internal.Version{4, 11, 0}, + } + } + if err != nil { + return "", nil, err + } + + return name, &abi, nil +} + +func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error { + raw, err := fd.Value() + if err != nil { + return err + } + + fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", raw)) + if err != nil { + return err + } + defer fh.Close() + + if err := scanFdInfoReader(fh, fields); err != nil { + return xerrors.Errorf("%s: %w", fh.Name(), err) + } + return nil +} + +var errMissingFields = xerrors.New("missing fields") + +func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error { + var ( + scanner = bufio.NewScanner(r) + scanned int + ) + + for scanner.Scan() { + parts := bytes.SplitN(scanner.Bytes(), []byte("\t"), 2) + if len(parts) != 2 { + continue + } + + name := bytes.TrimSuffix(parts[0], []byte(":")) + field, ok := fields[string(name)] + if !ok { + continue + } + + if n, err := fmt.Fscanln(bytes.NewReader(parts[1]), field); err != nil || n != 1 { + return xerrors.Errorf("can't parse field %s: %v", name, err) + } + + scanned++ + } + + if err := scanner.Err(); err != nil { + return err + } + + if scanned != len(fields) { + return errMissingFields + } + + return nil +} + +// Equal returns true if two ABIs have the same values. +func (abi *ProgramABI) Equal(other *ProgramABI) bool { + switch { + case abi.Type != other.Type: + return false + default: + return true + } +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/alu.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/alu.go new file mode 100644 index 000000000..70ccc4d15 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/alu.go @@ -0,0 +1,149 @@ +package asm + +//go:generate stringer -output alu_string.go -type=Source,Endianness,ALUOp + +// Source of ALU / ALU64 / Branch operations +// +// msb lsb +// +----+-+---+ +// |op |S|cls| +// +----+-+---+ +type Source uint8 + +const sourceMask OpCode = 0x08 + +// Source bitmask +const ( + // InvalidSource is returned by getters when invoked + // on non ALU / branch OpCodes. + InvalidSource Source = 0xff + // ImmSource src is from constant + ImmSource Source = 0x00 + // RegSource src is from register + RegSource Source = 0x08 +) + +// The Endianness of a byte swap instruction. +type Endianness uint8 + +const endianMask = sourceMask + +// Endian flags +const ( + InvalidEndian Endianness = 0xff + // Convert to little endian + LE Endianness = 0x00 + // Convert to big endian + BE Endianness = 0x08 +) + +// ALUOp are ALU / ALU64 operations +// +// msb lsb +// +----+-+---+ +// |OP |s|cls| +// +----+-+---+ +type ALUOp uint8 + +const aluMask OpCode = 0xf0 + +const ( + // InvalidALUOp is returned by getters when invoked + // on non ALU OpCodes + InvalidALUOp ALUOp = 0xff + // Add - addition + Add ALUOp = 0x00 + // Sub - subtraction + Sub ALUOp = 0x10 + // Mul - multiplication + Mul ALUOp = 0x20 + // Div - division + Div ALUOp = 0x30 + // Or - bitwise or + Or ALUOp = 0x40 + // And - bitwise and + And ALUOp = 0x50 + // LSh - bitwise shift left + LSh ALUOp = 0x60 + // RSh - bitwise shift right + RSh ALUOp = 0x70 + // Neg - sign/unsign signing bit + Neg ALUOp = 0x80 + // Mod - modulo + Mod ALUOp = 0x90 + // Xor - bitwise xor + Xor ALUOp = 0xa0 + // Mov - move value from one place to another + Mov ALUOp = 0xb0 + // ArSh - arithmatic shift + ArSh ALUOp = 0xc0 + // Swap - endian conversions + Swap ALUOp = 0xd0 +) + +// HostTo converts from host to another endianness. +func HostTo(endian Endianness, dst Register, size Size) Instruction { + var imm int64 + switch size { + case Half: + imm = 16 + case Word: + imm = 32 + case DWord: + imm = 64 + default: + return Instruction{OpCode: InvalidOpCode} + } + + return Instruction{ + OpCode: OpCode(ALUClass).SetALUOp(Swap).SetSource(Source(endian)), + Dst: dst, + Constant: imm, + } +} + +// Op returns the OpCode for an ALU operation with a given source. +func (op ALUOp) Op(source Source) OpCode { + return OpCode(ALU64Class).SetALUOp(op).SetSource(source) +} + +// Reg emits `dst (op) src`. +func (op ALUOp) Reg(dst, src Register) Instruction { + return Instruction{ + OpCode: op.Op(RegSource), + Dst: dst, + Src: src, + } +} + +// Imm emits `dst (op) value`. +func (op ALUOp) Imm(dst Register, value int32) Instruction { + return Instruction{ + OpCode: op.Op(ImmSource), + Dst: dst, + Constant: int64(value), + } +} + +// Op32 returns the OpCode for a 32-bit ALU operation with a given source. +func (op ALUOp) Op32(source Source) OpCode { + return OpCode(ALUClass).SetALUOp(op).SetSource(source) +} + +// Reg32 emits `dst (op) src`, zeroing the upper 32 bit of dst. +func (op ALUOp) Reg32(dst, src Register) Instruction { + return Instruction{ + OpCode: op.Op32(RegSource), + Dst: dst, + Src: src, + } +} + +// Imm32 emits `dst (op) value`, zeroing the upper 32 bit of dst. +func (op ALUOp) Imm32(dst Register, value int32) Instruction { + return Instruction{ + OpCode: op.Op32(ImmSource), + Dst: dst, + Constant: int64(value), + } +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/alu_string.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/alu_string.go new file mode 100644 index 000000000..72d3fe629 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/alu_string.go @@ -0,0 +1,107 @@ +// Code generated by "stringer -output alu_string.go -type=Source,Endianness,ALUOp"; DO NOT EDIT. + +package asm + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidSource-255] + _ = x[ImmSource-0] + _ = x[RegSource-8] +} + +const ( + _Source_name_0 = "ImmSource" + _Source_name_1 = "RegSource" + _Source_name_2 = "InvalidSource" +) + +func (i Source) String() string { + switch { + case i == 0: + return _Source_name_0 + case i == 8: + return _Source_name_1 + case i == 255: + return _Source_name_2 + default: + return "Source(" + strconv.FormatInt(int64(i), 10) + ")" + } +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidEndian-255] + _ = x[LE-0] + _ = x[BE-8] +} + +const ( + _Endianness_name_0 = "LE" + _Endianness_name_1 = "BE" + _Endianness_name_2 = "InvalidEndian" +) + +func (i Endianness) String() string { + switch { + case i == 0: + return _Endianness_name_0 + case i == 8: + return _Endianness_name_1 + case i == 255: + return _Endianness_name_2 + default: + return "Endianness(" + strconv.FormatInt(int64(i), 10) + ")" + } +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidALUOp-255] + _ = x[Add-0] + _ = x[Sub-16] + _ = x[Mul-32] + _ = x[Div-48] + _ = x[Or-64] + _ = x[And-80] + _ = x[LSh-96] + _ = x[RSh-112] + _ = x[Neg-128] + _ = x[Mod-144] + _ = x[Xor-160] + _ = x[Mov-176] + _ = x[ArSh-192] + _ = x[Swap-208] +} + +const _ALUOp_name = "AddSubMulDivOrAndLShRShNegModXorMovArShSwapInvalidALUOp" + +var _ALUOp_map = map[ALUOp]string{ + 0: _ALUOp_name[0:3], + 16: _ALUOp_name[3:6], + 32: _ALUOp_name[6:9], + 48: _ALUOp_name[9:12], + 64: _ALUOp_name[12:14], + 80: _ALUOp_name[14:17], + 96: _ALUOp_name[17:20], + 112: _ALUOp_name[20:23], + 128: _ALUOp_name[23:26], + 144: _ALUOp_name[26:29], + 160: _ALUOp_name[29:32], + 176: _ALUOp_name[32:35], + 192: _ALUOp_name[35:39], + 208: _ALUOp_name[39:43], + 255: _ALUOp_name[43:55], +} + +func (i ALUOp) String() string { + if str, ok := _ALUOp_map[i]; ok { + return str + } + return "ALUOp(" + strconv.FormatInt(int64(i), 10) + ")" +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/doc.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/doc.go new file mode 100644 index 000000000..7031bdc27 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/doc.go @@ -0,0 +1,2 @@ +// Package asm is an assembler for eBPF bytecode. +package asm diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/func.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/func.go new file mode 100644 index 000000000..97f794cdb --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/func.go @@ -0,0 +1,143 @@ +package asm + +//go:generate stringer -output func_string.go -type=BuiltinFunc + +// BuiltinFunc is a built-in eBPF function. +type BuiltinFunc int32 + +// eBPF built-in functions +// +// You can renegerate this list using the following gawk script: +// +// /FN\(.+\),/ { +// match($1, /\((.+)\)/, r) +// split(r[1], p, "_") +// printf "Fn" +// for (i in p) { +// printf "%s%s", toupper(substr(p[i], 1, 1)), substr(p[i], 2) +// } +// print "" +// } +// +// The script expects include/uapi/linux/bpf.h as it's input. +const ( + FnUnspec BuiltinFunc = iota + FnMapLookupElem + FnMapUpdateElem + FnMapDeleteElem + FnProbeRead + FnKtimeGetNs + FnTracePrintk + FnGetPrandomU32 + FnGetSmpProcessorId + FnSkbStoreBytes + FnL3CsumReplace + FnL4CsumReplace + FnTailCall + FnCloneRedirect + FnGetCurrentPidTgid + FnGetCurrentUidGid + FnGetCurrentComm + FnGetCgroupClassid + FnSkbVlanPush + FnSkbVlanPop + FnSkbGetTunnelKey + FnSkbSetTunnelKey + FnPerfEventRead + FnRedirect + FnGetRouteRealm + FnPerfEventOutput + FnSkbLoadBytes + FnGetStackid + FnCsumDiff + FnSkbGetTunnelOpt + FnSkbSetTunnelOpt + FnSkbChangeProto + FnSkbChangeType + FnSkbUnderCgroup + FnGetHashRecalc + FnGetCurrentTask + FnProbeWriteUser + FnCurrentTaskUnderCgroup + FnSkbChangeTail + FnSkbPullData + FnCsumUpdate + FnSetHashInvalid + FnGetNumaNodeId + FnSkbChangeHead + FnXdpAdjustHead + FnProbeReadStr + FnGetSocketCookie + FnGetSocketUid + FnSetHash + FnSetsockopt + FnSkbAdjustRoom + FnRedirectMap + FnSkRedirectMap + FnSockMapUpdate + FnXdpAdjustMeta + FnPerfEventReadValue + FnPerfProgReadValue + FnGetsockopt + FnOverrideReturn + FnSockOpsCbFlagsSet + FnMsgRedirectMap + FnMsgApplyBytes + FnMsgCorkBytes + FnMsgPullData + FnBind + FnXdpAdjustTail + FnSkbGetXfrmState + FnGetStack + FnSkbLoadBytesRelative + FnFibLookup + FnSockHashUpdate + FnMsgRedirectHash + FnSkRedirectHash + FnLwtPushEncap + FnLwtSeg6StoreBytes + FnLwtSeg6AdjustSrh + FnLwtSeg6Action + FnRcRepeat + FnRcKeydown + FnSkbCgroupId + FnGetCurrentCgroupId + FnGetLocalStorage + FnSkSelectReuseport + FnSkbAncestorCgroupId + FnSkLookupTcp + FnSkLookupUdp + FnSkRelease + FnMapPushElem + FnMapPopElem + FnMapPeekElem + FnMsgPushData + FnMsgPopData + FnRcPointerRel + FnSpinLock + FnSpinUnlock + FnSkFullsock + FnTcpSock + FnSkbEcnSetCe + FnGetListenerSock + FnSkcLookupTcp + FnTcpCheckSyncookie + FnSysctlGetName + FnSysctlGetCurrentValue + FnSysctlGetNewValue + FnSysctlSetNewValue + FnStrtol + FnStrtoul + FnSkStorageGet + FnSkStorageDelete + FnSendSignal + FnTcpGenSyncookie +) + +// Call emits a function call. +func (fn BuiltinFunc) Call() Instruction { + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(Call), + Constant: int64(fn), + } +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/func_string.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/func_string.go new file mode 100644 index 000000000..8860b9fdb --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/func_string.go @@ -0,0 +1,133 @@ +// Code generated by "stringer -output func_string.go -type=BuiltinFunc"; DO NOT EDIT. + +package asm + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[FnUnspec-0] + _ = x[FnMapLookupElem-1] + _ = x[FnMapUpdateElem-2] + _ = x[FnMapDeleteElem-3] + _ = x[FnProbeRead-4] + _ = x[FnKtimeGetNs-5] + _ = x[FnTracePrintk-6] + _ = x[FnGetPrandomU32-7] + _ = x[FnGetSmpProcessorId-8] + _ = x[FnSkbStoreBytes-9] + _ = x[FnL3CsumReplace-10] + _ = x[FnL4CsumReplace-11] + _ = x[FnTailCall-12] + _ = x[FnCloneRedirect-13] + _ = x[FnGetCurrentPidTgid-14] + _ = x[FnGetCurrentUidGid-15] + _ = x[FnGetCurrentComm-16] + _ = x[FnGetCgroupClassid-17] + _ = x[FnSkbVlanPush-18] + _ = x[FnSkbVlanPop-19] + _ = x[FnSkbGetTunnelKey-20] + _ = x[FnSkbSetTunnelKey-21] + _ = x[FnPerfEventRead-22] + _ = x[FnRedirect-23] + _ = x[FnGetRouteRealm-24] + _ = x[FnPerfEventOutput-25] + _ = x[FnSkbLoadBytes-26] + _ = x[FnGetStackid-27] + _ = x[FnCsumDiff-28] + _ = x[FnSkbGetTunnelOpt-29] + _ = x[FnSkbSetTunnelOpt-30] + _ = x[FnSkbChangeProto-31] + _ = x[FnSkbChangeType-32] + _ = x[FnSkbUnderCgroup-33] + _ = x[FnGetHashRecalc-34] + _ = x[FnGetCurrentTask-35] + _ = x[FnProbeWriteUser-36] + _ = x[FnCurrentTaskUnderCgroup-37] + _ = x[FnSkbChangeTail-38] + _ = x[FnSkbPullData-39] + _ = x[FnCsumUpdate-40] + _ = x[FnSetHashInvalid-41] + _ = x[FnGetNumaNodeId-42] + _ = x[FnSkbChangeHead-43] + _ = x[FnXdpAdjustHead-44] + _ = x[FnProbeReadStr-45] + _ = x[FnGetSocketCookie-46] + _ = x[FnGetSocketUid-47] + _ = x[FnSetHash-48] + _ = x[FnSetsockopt-49] + _ = x[FnSkbAdjustRoom-50] + _ = x[FnRedirectMap-51] + _ = x[FnSkRedirectMap-52] + _ = x[FnSockMapUpdate-53] + _ = x[FnXdpAdjustMeta-54] + _ = x[FnPerfEventReadValue-55] + _ = x[FnPerfProgReadValue-56] + _ = x[FnGetsockopt-57] + _ = x[FnOverrideReturn-58] + _ = x[FnSockOpsCbFlagsSet-59] + _ = x[FnMsgRedirectMap-60] + _ = x[FnMsgApplyBytes-61] + _ = x[FnMsgCorkBytes-62] + _ = x[FnMsgPullData-63] + _ = x[FnBind-64] + _ = x[FnXdpAdjustTail-65] + _ = x[FnSkbGetXfrmState-66] + _ = x[FnGetStack-67] + _ = x[FnSkbLoadBytesRelative-68] + _ = x[FnFibLookup-69] + _ = x[FnSockHashUpdate-70] + _ = x[FnMsgRedirectHash-71] + _ = x[FnSkRedirectHash-72] + _ = x[FnLwtPushEncap-73] + _ = x[FnLwtSeg6StoreBytes-74] + _ = x[FnLwtSeg6AdjustSrh-75] + _ = x[FnLwtSeg6Action-76] + _ = x[FnRcRepeat-77] + _ = x[FnRcKeydown-78] + _ = x[FnSkbCgroupId-79] + _ = x[FnGetCurrentCgroupId-80] + _ = x[FnGetLocalStorage-81] + _ = x[FnSkSelectReuseport-82] + _ = x[FnSkbAncestorCgroupId-83] + _ = x[FnSkLookupTcp-84] + _ = x[FnSkLookupUdp-85] + _ = x[FnSkRelease-86] + _ = x[FnMapPushElem-87] + _ = x[FnMapPopElem-88] + _ = x[FnMapPeekElem-89] + _ = x[FnMsgPushData-90] + _ = x[FnMsgPopData-91] + _ = x[FnRcPointerRel-92] + _ = x[FnSpinLock-93] + _ = x[FnSpinUnlock-94] + _ = x[FnSkFullsock-95] + _ = x[FnTcpSock-96] + _ = x[FnSkbEcnSetCe-97] + _ = x[FnGetListenerSock-98] + _ = x[FnSkcLookupTcp-99] + _ = x[FnTcpCheckSyncookie-100] + _ = x[FnSysctlGetName-101] + _ = x[FnSysctlGetCurrentValue-102] + _ = x[FnSysctlGetNewValue-103] + _ = x[FnSysctlSetNewValue-104] + _ = x[FnStrtol-105] + _ = x[FnStrtoul-106] + _ = x[FnSkStorageGet-107] + _ = x[FnSkStorageDelete-108] + _ = x[FnSendSignal-109] + _ = x[FnTcpGenSyncookie-110] +} + +const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookie" + +var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632} + +func (i BuiltinFunc) String() string { + if i < 0 || i >= BuiltinFunc(len(_BuiltinFunc_index)-1) { + return "BuiltinFunc(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _BuiltinFunc_name[_BuiltinFunc_index[i]:_BuiltinFunc_index[i+1]] +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/instruction.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/instruction.go new file mode 100644 index 000000000..644de428c --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/instruction.go @@ -0,0 +1,460 @@ +package asm + +import ( + "encoding/binary" + "fmt" + "io" + "math" + "strings" + + "golang.org/x/xerrors" +) + +// InstructionSize is the size of a BPF instruction in bytes +const InstructionSize = 8 + +// Instruction is a single eBPF instruction. +type Instruction struct { + OpCode OpCode + Dst Register + Src Register + Offset int16 + Constant int64 + Reference string + Symbol string +} + +// Sym creates a symbol. +func (ins Instruction) Sym(name string) Instruction { + ins.Symbol = name + return ins +} + +// Unmarshal decodes a BPF instruction. +func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, error) { + var bi bpfInstruction + err := binary.Read(r, bo, &bi) + if err != nil { + return 0, err + } + + ins.OpCode = bi.OpCode + ins.Dst = bi.Registers.Dst() + ins.Src = bi.Registers.Src() + ins.Offset = bi.Offset + ins.Constant = int64(bi.Constant) + + if !bi.OpCode.isDWordLoad() { + return InstructionSize, nil + } + + var bi2 bpfInstruction + if err := binary.Read(r, bo, &bi2); err != nil { + // No Wrap, to avoid io.EOF clash + return 0, xerrors.New("64bit immediate is missing second half") + } + if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 { + return 0, xerrors.New("64bit immediate has non-zero fields") + } + ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant))) + + return 2 * InstructionSize, nil +} + +// Marshal encodes a BPF instruction. +func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) { + if ins.OpCode == InvalidOpCode { + return 0, xerrors.New("invalid opcode") + } + + isDWordLoad := ins.OpCode.isDWordLoad() + + cons := int32(ins.Constant) + if isDWordLoad { + // Encode least significant 32bit first for 64bit operations. + cons = int32(uint32(ins.Constant)) + } + + bpfi := bpfInstruction{ + ins.OpCode, + newBPFRegisters(ins.Dst, ins.Src), + ins.Offset, + cons, + } + + if err := binary.Write(w, bo, &bpfi); err != nil { + return 0, err + } + + if !isDWordLoad { + return InstructionSize, nil + } + + bpfi = bpfInstruction{ + Constant: int32(ins.Constant >> 32), + } + + if err := binary.Write(w, bo, &bpfi); err != nil { + return 0, err + } + + return 2 * InstructionSize, nil +} + +// RewriteMapPtr changes an instruction to use a new map fd. +// +// Returns an error if the instruction doesn't load a map. +func (ins *Instruction) RewriteMapPtr(fd int) error { + if !ins.OpCode.isDWordLoad() { + return xerrors.Errorf("%s is not a 64 bit load", ins.OpCode) + } + + if ins.Src != PseudoMapFD && ins.Src != PseudoMapValue { + return xerrors.New("not a load from a map") + } + + // Preserve the offset value for direct map loads. + offset := uint64(ins.Constant) & (math.MaxUint32 << 32) + rawFd := uint64(uint32(fd)) + ins.Constant = int64(offset | rawFd) + return nil +} + +func (ins *Instruction) mapPtr() uint32 { + return uint32(uint64(ins.Constant) & math.MaxUint32) +} + +// RewriteMapOffset changes the offset of a direct load from a map. +// +// Returns an error if the instruction is not a direct load. +func (ins *Instruction) RewriteMapOffset(offset uint32) error { + if !ins.OpCode.isDWordLoad() { + return xerrors.Errorf("%s is not a 64 bit load", ins.OpCode) + } + + if ins.Src != PseudoMapValue { + return xerrors.New("not a direct load from a map") + } + + fd := uint64(ins.Constant) & math.MaxUint32 + ins.Constant = int64(uint64(offset)<<32 | fd) + return nil +} + +func (ins *Instruction) mapOffset() uint32 { + return uint32(uint64(ins.Constant) >> 32) +} + +func (ins *Instruction) isLoadFromMap() bool { + return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue) +} + +// Format implements fmt.Formatter. +func (ins Instruction) Format(f fmt.State, c rune) { + if c != 'v' { + fmt.Fprintf(f, "{UNRECOGNIZED: %c}", c) + return + } + + op := ins.OpCode + + if op == InvalidOpCode { + fmt.Fprint(f, "INVALID") + return + } + + // Omit trailing space for Exit + if op.JumpOp() == Exit { + fmt.Fprint(f, op) + return + } + + if ins.isLoadFromMap() { + fd := int32(ins.mapPtr()) + switch ins.Src { + case PseudoMapFD: + fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd) + + case PseudoMapValue: + fmt.Fprintf(f, "LoadMapValue dst: %s, fd: %d off: %d", ins.Dst, fd, ins.mapOffset()) + } + + goto ref + } + + fmt.Fprintf(f, "%v ", op) + switch cls := op.Class(); cls { + case LdClass, LdXClass, StClass, StXClass: + switch op.Mode() { + case ImmMode: + fmt.Fprintf(f, "dst: %s imm: %d", ins.Dst, ins.Constant) + case AbsMode: + fmt.Fprintf(f, "imm: %d", ins.Constant) + case IndMode: + fmt.Fprintf(f, "dst: %s src: %s imm: %d", ins.Dst, ins.Src, ins.Constant) + case MemMode: + fmt.Fprintf(f, "dst: %s src: %s off: %d imm: %d", ins.Dst, ins.Src, ins.Offset, ins.Constant) + case XAddMode: + fmt.Fprintf(f, "dst: %s src: %s", ins.Dst, ins.Src) + } + + case ALU64Class, ALUClass: + fmt.Fprintf(f, "dst: %s ", ins.Dst) + if op.ALUOp() == Swap || op.Source() == ImmSource { + fmt.Fprintf(f, "imm: %d", ins.Constant) + } else { + fmt.Fprintf(f, "src: %s", ins.Src) + } + + case JumpClass: + switch jop := op.JumpOp(); jop { + case Call: + if ins.Src == PseudoCall { + // bpf-to-bpf call + fmt.Fprint(f, ins.Constant) + } else { + fmt.Fprint(f, BuiltinFunc(ins.Constant)) + } + + default: + fmt.Fprintf(f, "dst: %s off: %d ", ins.Dst, ins.Offset) + if op.Source() == ImmSource { + fmt.Fprintf(f, "imm: %d", ins.Constant) + } else { + fmt.Fprintf(f, "src: %s", ins.Src) + } + } + } + +ref: + if ins.Reference != "" { + fmt.Fprintf(f, " <%s>", ins.Reference) + } +} + +// Instructions is an eBPF program. +type Instructions []Instruction + +func (insns Instructions) String() string { + return fmt.Sprint(insns) +} + +// RewriteMapPtr rewrites all loads of a specific map pointer to a new fd. +// +// Returns an error if the symbol isn't used, see IsUnreferencedSymbol. +func (insns Instructions) RewriteMapPtr(symbol string, fd int) error { + if symbol == "" { + return xerrors.New("empty symbol") + } + + found := false + for i := range insns { + ins := &insns[i] + if ins.Reference != symbol { + continue + } + + if err := ins.RewriteMapPtr(fd); err != nil { + return err + } + + found = true + } + + if !found { + return &unreferencedSymbolError{symbol} + } + + return nil +} + +// SymbolOffsets returns the set of symbols and their offset in +// the instructions. +func (insns Instructions) SymbolOffsets() (map[string]int, error) { + offsets := make(map[string]int) + + for i, ins := range insns { + if ins.Symbol == "" { + continue + } + + if _, ok := offsets[ins.Symbol]; ok { + return nil, xerrors.Errorf("duplicate symbol %s", ins.Symbol) + } + + offsets[ins.Symbol] = i + } + + return offsets, nil +} + +// ReferenceOffsets returns the set of references and their offset in +// the instructions. +func (insns Instructions) ReferenceOffsets() map[string][]int { + offsets := make(map[string][]int) + + for i, ins := range insns { + if ins.Reference == "" { + continue + } + + offsets[ins.Reference] = append(offsets[ins.Reference], i) + } + + return offsets +} + +func (insns Instructions) marshalledOffsets() (map[string]int, error) { + symbols := make(map[string]int) + + marshalledPos := 0 + for _, ins := range insns { + currentPos := marshalledPos + marshalledPos += ins.OpCode.marshalledInstructions() + + if ins.Symbol == "" { + continue + } + + if _, ok := symbols[ins.Symbol]; ok { + return nil, xerrors.Errorf("duplicate symbol %s", ins.Symbol) + } + + symbols[ins.Symbol] = currentPos + } + + return symbols, nil +} + +// Format implements fmt.Formatter. +// +// You can control indentation of symbols by +// specifying a width. Setting a precision controls the indentation of +// instructions. +// The default character is a tab, which can be overriden by specifying +// the ' ' space flag. +func (insns Instructions) Format(f fmt.State, c rune) { + if c != 's' && c != 'v' { + fmt.Fprintf(f, "{UNKNOWN FORMAT '%c'}", c) + return + } + + // Precision is better in this case, because it allows + // specifying 0 padding easily. + padding, ok := f.Precision() + if !ok { + padding = 1 + } + + indent := strings.Repeat("\t", padding) + if f.Flag(' ') { + indent = strings.Repeat(" ", padding) + } + + symPadding, ok := f.Width() + if !ok { + symPadding = padding - 1 + } + if symPadding < 0 { + symPadding = 0 + } + + symIndent := strings.Repeat("\t", symPadding) + if f.Flag(' ') { + symIndent = strings.Repeat(" ", symPadding) + } + + // Figure out how many digits we need to represent the highest + // offset. + highestOffset := 0 + for _, ins := range insns { + highestOffset += ins.OpCode.marshalledInstructions() + } + offsetWidth := int(math.Ceil(math.Log10(float64(highestOffset)))) + + offset := 0 + for _, ins := range insns { + if ins.Symbol != "" { + fmt.Fprintf(f, "%s%s:\n", symIndent, ins.Symbol) + } + fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, offset, ins) + offset += ins.OpCode.marshalledInstructions() + } + + return +} + +// Marshal encodes a BPF program into the kernel format. +func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error { + absoluteOffsets, err := insns.marshalledOffsets() + if err != nil { + return err + } + + num := 0 + for i, ins := range insns { + switch { + case ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall && ins.Constant == -1: + // Rewrite bpf to bpf call + offset, ok := absoluteOffsets[ins.Reference] + if !ok { + return xerrors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference) + } + + ins.Constant = int64(offset - num - 1) + + case ins.OpCode.Class() == JumpClass && ins.Offset == -1: + // Rewrite jump to label + offset, ok := absoluteOffsets[ins.Reference] + if !ok { + return xerrors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference) + } + + ins.Offset = int16(offset - num - 1) + } + + n, err := ins.Marshal(w, bo) + if err != nil { + return xerrors.Errorf("instruction %d: %w", i, err) + } + + num += int(n / InstructionSize) + } + return nil +} + +type bpfInstruction struct { + OpCode OpCode + Registers bpfRegisters + Offset int16 + Constant int32 +} + +type bpfRegisters uint8 + +func newBPFRegisters(dst, src Register) bpfRegisters { + return bpfRegisters((src << 4) | (dst & 0xF)) +} + +func (r bpfRegisters) Dst() Register { + return Register(r & 0xF) +} + +func (r bpfRegisters) Src() Register { + return Register(r >> 4) +} + +type unreferencedSymbolError struct { + symbol string +} + +func (use *unreferencedSymbolError) Error() string { + return fmt.Sprintf("unreferenced symbol %s", use.symbol) +} + +// IsUnreferencedSymbol returns true if err was caused by +// an unreferenced symbol. +func IsUnreferencedSymbol(err error) bool { + _, ok := err.(*unreferencedSymbolError) + return ok +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/jump.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/jump.go new file mode 100644 index 000000000..7757179de --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/jump.go @@ -0,0 +1,109 @@ +package asm + +//go:generate stringer -output jump_string.go -type=JumpOp + +// JumpOp affect control flow. +// +// msb lsb +// +----+-+---+ +// |OP |s|cls| +// +----+-+---+ +type JumpOp uint8 + +const jumpMask OpCode = aluMask + +const ( + // InvalidJumpOp is returned by getters when invoked + // on non branch OpCodes + InvalidJumpOp JumpOp = 0xff + // Ja jumps by offset unconditionally + Ja JumpOp = 0x00 + // JEq jumps by offset if r == imm + JEq JumpOp = 0x10 + // JGT jumps by offset if r > imm + JGT JumpOp = 0x20 + // JGE jumps by offset if r >= imm + JGE JumpOp = 0x30 + // JSet jumps by offset if r & imm + JSet JumpOp = 0x40 + // JNE jumps by offset if r != imm + JNE JumpOp = 0x50 + // JSGT jumps by offset if signed r > signed imm + JSGT JumpOp = 0x60 + // JSGE jumps by offset if signed r >= signed imm + JSGE JumpOp = 0x70 + // Call builtin or user defined function from imm + Call JumpOp = 0x80 + // Exit ends execution, with value in r0 + Exit JumpOp = 0x90 + // JLT jumps by offset if r < imm + JLT JumpOp = 0xa0 + // JLE jumps by offset if r <= imm + JLE JumpOp = 0xb0 + // JSLT jumps by offset if signed r < signed imm + JSLT JumpOp = 0xc0 + // JSLE jumps by offset if signed r <= signed imm + JSLE JumpOp = 0xd0 +) + +// Return emits an exit instruction. +// +// Requires a return value in R0. +func Return() Instruction { + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(Exit), + } +} + +// Op returns the OpCode for a given jump source. +func (op JumpOp) Op(source Source) OpCode { + return OpCode(JumpClass).SetJumpOp(op).SetSource(source) +} + +// Imm compares dst to value, and adjusts PC by offset if the condition is fulfilled. +func (op JumpOp) Imm(dst Register, value int32, label string) Instruction { + if op == Exit || op == Call || op == Ja { + return Instruction{OpCode: InvalidOpCode} + } + + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(op).SetSource(ImmSource), + Dst: dst, + Offset: -1, + Constant: int64(value), + Reference: label, + } +} + +// Reg compares dst to src, and adjusts PC by offset if the condition is fulfilled. +func (op JumpOp) Reg(dst, src Register, label string) Instruction { + if op == Exit || op == Call || op == Ja { + return Instruction{OpCode: InvalidOpCode} + } + + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(op).SetSource(RegSource), + Dst: dst, + Src: src, + Offset: -1, + Reference: label, + } +} + +// Label adjusts PC to the address of the label. +func (op JumpOp) Label(label string) Instruction { + if op == Call { + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(Call), + Src: PseudoCall, + Constant: -1, + Reference: label, + } + } + + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(op), + Offset: -1, + Reference: label, + } +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/jump_string.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/jump_string.go new file mode 100644 index 000000000..85a4aaffa --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/jump_string.go @@ -0,0 +1,53 @@ +// Code generated by "stringer -output jump_string.go -type=JumpOp"; DO NOT EDIT. + +package asm + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidJumpOp-255] + _ = x[Ja-0] + _ = x[JEq-16] + _ = x[JGT-32] + _ = x[JGE-48] + _ = x[JSet-64] + _ = x[JNE-80] + _ = x[JSGT-96] + _ = x[JSGE-112] + _ = x[Call-128] + _ = x[Exit-144] + _ = x[JLT-160] + _ = x[JLE-176] + _ = x[JSLT-192] + _ = x[JSLE-208] +} + +const _JumpOp_name = "JaJEqJGTJGEJSetJNEJSGTJSGECallExitJLTJLEJSLTJSLEInvalidJumpOp" + +var _JumpOp_map = map[JumpOp]string{ + 0: _JumpOp_name[0:2], + 16: _JumpOp_name[2:5], + 32: _JumpOp_name[5:8], + 48: _JumpOp_name[8:11], + 64: _JumpOp_name[11:15], + 80: _JumpOp_name[15:18], + 96: _JumpOp_name[18:22], + 112: _JumpOp_name[22:26], + 128: _JumpOp_name[26:30], + 144: _JumpOp_name[30:34], + 160: _JumpOp_name[34:37], + 176: _JumpOp_name[37:40], + 192: _JumpOp_name[40:44], + 208: _JumpOp_name[44:48], + 255: _JumpOp_name[48:61], +} + +func (i JumpOp) String() string { + if str, ok := _JumpOp_map[i]; ok { + return str + } + return "JumpOp(" + strconv.FormatInt(int64(i), 10) + ")" +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/load_store.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/load_store.go new file mode 100644 index 000000000..2d0ec648e --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/load_store.go @@ -0,0 +1,204 @@ +package asm + +//go:generate stringer -output load_store_string.go -type=Mode,Size + +// Mode for load and store operations +// +// msb lsb +// +---+--+---+ +// |MDE|sz|cls| +// +---+--+---+ +type Mode uint8 + +const modeMask OpCode = 0xe0 + +const ( + // InvalidMode is returned by getters when invoked + // on non load / store OpCodes + InvalidMode Mode = 0xff + // ImmMode - immediate value + ImmMode Mode = 0x00 + // AbsMode - immediate value + offset + AbsMode Mode = 0x20 + // IndMode - indirect (imm+src) + IndMode Mode = 0x40 + // MemMode - load from memory + MemMode Mode = 0x60 + // XAddMode - add atomically across processors. + XAddMode Mode = 0xc0 +) + +// Size of load and store operations +// +// msb lsb +// +---+--+---+ +// |mde|SZ|cls| +// +---+--+---+ +type Size uint8 + +const sizeMask OpCode = 0x18 + +const ( + // InvalidSize is returned by getters when invoked + // on non load / store OpCodes + InvalidSize Size = 0xff + // DWord - double word; 64 bits + DWord Size = 0x18 + // Word - word; 32 bits + Word Size = 0x00 + // Half - half-word; 16 bits + Half Size = 0x08 + // Byte - byte; 8 bits + Byte Size = 0x10 +) + +// Sizeof returns the size in bytes. +func (s Size) Sizeof() int { + switch s { + case DWord: + return 8 + case Word: + return 4 + case Half: + return 2 + case Byte: + return 1 + default: + return -1 + } +} + +// LoadMemOp returns the OpCode to load a value of given size from memory. +func LoadMemOp(size Size) OpCode { + return OpCode(LdXClass).SetMode(MemMode).SetSize(size) +} + +// LoadMem emits `dst = *(size *)(src + offset)`. +func LoadMem(dst, src Register, offset int16, size Size) Instruction { + return Instruction{ + OpCode: LoadMemOp(size), + Dst: dst, + Src: src, + Offset: offset, + } +} + +// LoadImmOp returns the OpCode to load an immediate of given size. +// +// As of kernel 4.20, only DWord size is accepted. +func LoadImmOp(size Size) OpCode { + return OpCode(LdClass).SetMode(ImmMode).SetSize(size) +} + +// LoadImm emits `dst = (size)value`. +// +// As of kernel 4.20, only DWord size is accepted. +func LoadImm(dst Register, value int64, size Size) Instruction { + return Instruction{ + OpCode: LoadImmOp(size), + Dst: dst, + Constant: value, + } +} + +// LoadMapPtr stores a pointer to a map in dst. +func LoadMapPtr(dst Register, fd int) Instruction { + if fd < 0 { + return Instruction{OpCode: InvalidOpCode} + } + + return Instruction{ + OpCode: LoadImmOp(DWord), + Dst: dst, + Src: PseudoMapFD, + Constant: int64(fd), + } +} + +// LoadMapValue stores a pointer to the value at a certain offset of a map. +func LoadMapValue(dst Register, fd int, offset uint32) Instruction { + if fd < 0 { + return Instruction{OpCode: InvalidOpCode} + } + + fdAndOffset := (uint64(offset) << 32) | uint64(uint32(fd)) + return Instruction{ + OpCode: LoadImmOp(DWord), + Dst: dst, + Src: PseudoMapValue, + Constant: int64(fdAndOffset), + } +} + +// LoadIndOp returns the OpCode for loading a value of given size from an sk_buff. +func LoadIndOp(size Size) OpCode { + return OpCode(LdClass).SetMode(IndMode).SetSize(size) +} + +// LoadInd emits `dst = ntoh(*(size *)(((sk_buff *)R6)->data + src + offset))`. +func LoadInd(dst, src Register, offset int32, size Size) Instruction { + return Instruction{ + OpCode: LoadIndOp(size), + Dst: dst, + Src: src, + Constant: int64(offset), + } +} + +// LoadAbsOp returns the OpCode for loading a value of given size from an sk_buff. +func LoadAbsOp(size Size) OpCode { + return OpCode(LdClass).SetMode(AbsMode).SetSize(size) +} + +// LoadAbs emits `r0 = ntoh(*(size *)(((sk_buff *)R6)->data + offset))`. +func LoadAbs(offset int32, size Size) Instruction { + return Instruction{ + OpCode: LoadAbsOp(size), + Dst: R0, + Constant: int64(offset), + } +} + +// StoreMemOp returns the OpCode for storing a register of given size in memory. +func StoreMemOp(size Size) OpCode { + return OpCode(StXClass).SetMode(MemMode).SetSize(size) +} + +// StoreMem emits `*(size *)(dst + offset) = src` +func StoreMem(dst Register, offset int16, src Register, size Size) Instruction { + return Instruction{ + OpCode: StoreMemOp(size), + Dst: dst, + Src: src, + Offset: offset, + } +} + +// StoreImmOp returns the OpCode for storing an immediate of given size in memory. +func StoreImmOp(size Size) OpCode { + return OpCode(StClass).SetMode(MemMode).SetSize(size) +} + +// StoreImm emits `*(size *)(dst + offset) = value`. +func StoreImm(dst Register, offset int16, value int64, size Size) Instruction { + return Instruction{ + OpCode: StoreImmOp(size), + Dst: dst, + Offset: offset, + Constant: value, + } +} + +// StoreXAddOp returns the OpCode to atomically add a register to a value in memory. +func StoreXAddOp(size Size) OpCode { + return OpCode(StXClass).SetMode(XAddMode).SetSize(size) +} + +// StoreXAdd atomically adds src to *dst. +func StoreXAdd(dst, src Register, size Size) Instruction { + return Instruction{ + OpCode: StoreXAddOp(size), + Dst: dst, + Src: src, + } +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/load_store_string.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/load_store_string.go new file mode 100644 index 000000000..76d29a075 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/load_store_string.go @@ -0,0 +1,80 @@ +// Code generated by "stringer -output load_store_string.go -type=Mode,Size"; DO NOT EDIT. + +package asm + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidMode-255] + _ = x[ImmMode-0] + _ = x[AbsMode-32] + _ = x[IndMode-64] + _ = x[MemMode-96] + _ = x[XAddMode-192] +} + +const ( + _Mode_name_0 = "ImmMode" + _Mode_name_1 = "AbsMode" + _Mode_name_2 = "IndMode" + _Mode_name_3 = "MemMode" + _Mode_name_4 = "XAddMode" + _Mode_name_5 = "InvalidMode" +) + +func (i Mode) String() string { + switch { + case i == 0: + return _Mode_name_0 + case i == 32: + return _Mode_name_1 + case i == 64: + return _Mode_name_2 + case i == 96: + return _Mode_name_3 + case i == 192: + return _Mode_name_4 + case i == 255: + return _Mode_name_5 + default: + return "Mode(" + strconv.FormatInt(int64(i), 10) + ")" + } +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidSize-255] + _ = x[DWord-24] + _ = x[Word-0] + _ = x[Half-8] + _ = x[Byte-16] +} + +const ( + _Size_name_0 = "Word" + _Size_name_1 = "Half" + _Size_name_2 = "Byte" + _Size_name_3 = "DWord" + _Size_name_4 = "InvalidSize" +) + +func (i Size) String() string { + switch { + case i == 0: + return _Size_name_0 + case i == 8: + return _Size_name_1 + case i == 16: + return _Size_name_2 + case i == 24: + return _Size_name_3 + case i == 255: + return _Size_name_4 + default: + return "Size(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/opcode.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/opcode.go new file mode 100644 index 000000000..d796de3fe --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/opcode.go @@ -0,0 +1,237 @@ +package asm + +import ( + "fmt" + "strings" +) + +//go:generate stringer -output opcode_string.go -type=Class + +type encoding int + +const ( + unknownEncoding encoding = iota + loadOrStore + jumpOrALU +) + +// Class of operations +// +// msb lsb +// +---+--+---+ +// | ?? |CLS| +// +---+--+---+ +type Class uint8 + +const classMask OpCode = 0x07 + +const ( + // LdClass load memory + LdClass Class = 0x00 + // LdXClass load memory from constant + LdXClass Class = 0x01 + // StClass load register from memory + StClass Class = 0x02 + // StXClass load register from constant + StXClass Class = 0x03 + // ALUClass arithmetic operators + ALUClass Class = 0x04 + // JumpClass jump operators + JumpClass Class = 0x05 + // ALU64Class arithmetic in 64 bit mode + ALU64Class Class = 0x07 +) + +func (cls Class) encoding() encoding { + switch cls { + case LdClass, LdXClass, StClass, StXClass: + return loadOrStore + case ALU64Class, ALUClass, JumpClass: + return jumpOrALU + default: + return unknownEncoding + } +} + +// OpCode is a packed eBPF opcode. +// +// Its encoding is defined by a Class value: +// +// msb lsb +// +----+-+---+ +// | ???? |CLS| +// +----+-+---+ +type OpCode uint8 + +// InvalidOpCode is returned by setters on OpCode +const InvalidOpCode OpCode = 0xff + +// marshalledInstructions returns the number of BPF instructions required +// to encode this opcode. +func (op OpCode) marshalledInstructions() int { + if op == LoadImmOp(DWord) { + return 2 + } + return 1 +} + +func (op OpCode) isDWordLoad() bool { + return op == LoadImmOp(DWord) +} + +// Class returns the class of operation. +func (op OpCode) Class() Class { + return Class(op & classMask) +} + +// Mode returns the mode for load and store operations. +func (op OpCode) Mode() Mode { + if op.Class().encoding() != loadOrStore { + return InvalidMode + } + return Mode(op & modeMask) +} + +// Size returns the size for load and store operations. +func (op OpCode) Size() Size { + if op.Class().encoding() != loadOrStore { + return InvalidSize + } + return Size(op & sizeMask) +} + +// Source returns the source for branch and ALU operations. +func (op OpCode) Source() Source { + if op.Class().encoding() != jumpOrALU || op.ALUOp() == Swap { + return InvalidSource + } + return Source(op & sourceMask) +} + +// ALUOp returns the ALUOp. +func (op OpCode) ALUOp() ALUOp { + if op.Class().encoding() != jumpOrALU { + return InvalidALUOp + } + return ALUOp(op & aluMask) +} + +// Endianness returns the Endianness for a byte swap instruction. +func (op OpCode) Endianness() Endianness { + if op.ALUOp() != Swap { + return InvalidEndian + } + return Endianness(op & endianMask) +} + +// JumpOp returns the JumpOp. +func (op OpCode) JumpOp() JumpOp { + if op.Class().encoding() != jumpOrALU { + return InvalidJumpOp + } + return JumpOp(op & jumpMask) +} + +// SetMode sets the mode on load and store operations. +// +// Returns InvalidOpCode if op is of the wrong class. +func (op OpCode) SetMode(mode Mode) OpCode { + if op.Class().encoding() != loadOrStore || !valid(OpCode(mode), modeMask) { + return InvalidOpCode + } + return (op & ^modeMask) | OpCode(mode) +} + +// SetSize sets the size on load and store operations. +// +// Returns InvalidOpCode if op is of the wrong class. +func (op OpCode) SetSize(size Size) OpCode { + if op.Class().encoding() != loadOrStore || !valid(OpCode(size), sizeMask) { + return InvalidOpCode + } + return (op & ^sizeMask) | OpCode(size) +} + +// SetSource sets the source on jump and ALU operations. +// +// Returns InvalidOpCode if op is of the wrong class. +func (op OpCode) SetSource(source Source) OpCode { + if op.Class().encoding() != jumpOrALU || !valid(OpCode(source), sourceMask) { + return InvalidOpCode + } + return (op & ^sourceMask) | OpCode(source) +} + +// SetALUOp sets the ALUOp on ALU operations. +// +// Returns InvalidOpCode if op is of the wrong class. +func (op OpCode) SetALUOp(alu ALUOp) OpCode { + class := op.Class() + if (class != ALUClass && class != ALU64Class) || !valid(OpCode(alu), aluMask) { + return InvalidOpCode + } + return (op & ^aluMask) | OpCode(alu) +} + +// SetJumpOp sets the JumpOp on jump operations. +// +// Returns InvalidOpCode if op is of the wrong class. +func (op OpCode) SetJumpOp(jump JumpOp) OpCode { + if op.Class() != JumpClass || !valid(OpCode(jump), jumpMask) { + return InvalidOpCode + } + return (op & ^jumpMask) | OpCode(jump) +} + +func (op OpCode) String() string { + var f strings.Builder + + switch class := op.Class(); class { + case LdClass, LdXClass, StClass, StXClass: + f.WriteString(strings.TrimSuffix(class.String(), "Class")) + + mode := op.Mode() + f.WriteString(strings.TrimSuffix(mode.String(), "Mode")) + + switch op.Size() { + case DWord: + f.WriteString("DW") + case Word: + f.WriteString("W") + case Half: + f.WriteString("H") + case Byte: + f.WriteString("B") + } + + case ALU64Class, ALUClass: + f.WriteString(op.ALUOp().String()) + + if op.ALUOp() == Swap { + // Width for Endian is controlled by Constant + f.WriteString(op.Endianness().String()) + } else { + if class == ALUClass { + f.WriteString("32") + } + + f.WriteString(strings.TrimSuffix(op.Source().String(), "Source")) + } + + case JumpClass: + f.WriteString(op.JumpOp().String()) + if jop := op.JumpOp(); jop != Exit && jop != Call { + f.WriteString(strings.TrimSuffix(op.Source().String(), "Source")) + } + + default: + fmt.Fprintf(&f, "%#x", op) + } + + return f.String() +} + +// valid returns true if all bits in value are covered by mask. +func valid(value, mask OpCode) bool { + return value & ^mask == 0 +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/opcode_string.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/opcode_string.go new file mode 100644 index 000000000..079ce1db0 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/opcode_string.go @@ -0,0 +1,38 @@ +// Code generated by "stringer -output opcode_string.go -type=Class"; DO NOT EDIT. + +package asm + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[LdClass-0] + _ = x[LdXClass-1] + _ = x[StClass-2] + _ = x[StXClass-3] + _ = x[ALUClass-4] + _ = x[JumpClass-5] + _ = x[ALU64Class-7] +} + +const ( + _Class_name_0 = "LdClassLdXClassStClassStXClassALUClassJumpClass" + _Class_name_1 = "ALU64Class" +) + +var ( + _Class_index_0 = [...]uint8{0, 7, 15, 22, 30, 38, 47} +) + +func (i Class) String() string { + switch { + case 0 <= i && i <= 5: + return _Class_name_0[_Class_index_0[i]:_Class_index_0[i+1]] + case i == 7: + return _Class_name_1 + default: + return "Class(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/asm/register.go b/src/runtime/vendor/github.com/cilium/ebpf/asm/register.go new file mode 100644 index 000000000..76cb44bff --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/asm/register.go @@ -0,0 +1,49 @@ +package asm + +import ( + "fmt" +) + +// Register is the source or destination of most operations. +type Register uint8 + +// R0 contains return values. +const R0 Register = 0 + +// Registers for function arguments. +const ( + R1 Register = R0 + 1 + iota + R2 + R3 + R4 + R5 +) + +// Callee saved registers preserved by function calls. +const ( + R6 Register = R5 + 1 + iota + R7 + R8 + R9 +) + +// Read-only frame pointer to access stack. +const ( + R10 Register = R9 + 1 + RFP = R10 +) + +// Pseudo registers used by 64bit loads and jumps +const ( + PseudoMapFD = R1 // BPF_PSEUDO_MAP_FD + PseudoMapValue = R2 // BPF_PSEUDO_MAP_VALUE + PseudoCall = R1 // BPF_PSEUDO_CALL +) + +func (r Register) String() string { + v := uint8(r) + if v == 10 { + return "rfp" + } + return fmt.Sprintf("r%d", v) +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/collection.go b/src/runtime/vendor/github.com/cilium/ebpf/collection.go new file mode 100644 index 000000000..5b3ed58f1 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/collection.go @@ -0,0 +1,293 @@ +package ebpf + +import ( + "math" + + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" + "golang.org/x/xerrors" +) + +// CollectionOptions control loading a collection into the kernel. +type CollectionOptions struct { + Programs ProgramOptions +} + +// CollectionSpec describes a collection. +type CollectionSpec struct { + Maps map[string]*MapSpec + Programs map[string]*ProgramSpec +} + +// Copy returns a recursive copy of the spec. +func (cs *CollectionSpec) Copy() *CollectionSpec { + if cs == nil { + return nil + } + + cpy := CollectionSpec{ + Maps: make(map[string]*MapSpec, len(cs.Maps)), + Programs: make(map[string]*ProgramSpec, len(cs.Programs)), + } + + for name, spec := range cs.Maps { + cpy.Maps[name] = spec.Copy() + } + + for name, spec := range cs.Programs { + cpy.Programs[name] = spec.Copy() + } + + return &cpy +} + +// RewriteMaps replaces all references to specific maps. +// +// Use this function to use pre-existing maps instead of creating new ones +// when calling NewCollection. Any named maps are removed from CollectionSpec.Maps. +// +// Returns an error if a named map isn't used in at least one program. +func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error { + for symbol, m := range maps { + // have we seen a program that uses this symbol / map + seen := false + fd := m.FD() + for progName, progSpec := range cs.Programs { + err := progSpec.Instructions.RewriteMapPtr(symbol, fd) + + switch { + case err == nil: + seen = true + + case asm.IsUnreferencedSymbol(err): + // Not all programs need to use the map + + default: + return xerrors.Errorf("program %s: %w", progName, err) + } + } + + if !seen { + return xerrors.Errorf("map %s not referenced by any programs", symbol) + } + + // Prevent NewCollection from creating rewritten maps + delete(cs.Maps, symbol) + } + + return nil +} + +// RewriteConstants replaces the value of multiple constants. +// +// The constant must be defined like so in the C program: +// +// static volatile const type foobar; +// static volatile const type foobar = default; +// +// Replacement values must be of the same length as the C sizeof(type). +// If necessary, they are marshalled according to the same rules as +// map values. +// +// From Linux 5.5 the verifier will use constants to eliminate dead code. +// +// Returns an error if a constant doesn't exist. +func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error { + rodata := cs.Maps[".rodata"] + if rodata == nil { + return xerrors.New("missing .rodata section") + } + + if rodata.BTF == nil { + return xerrors.New(".rodata section has no BTF") + } + + if n := len(rodata.Contents); n != 1 { + return xerrors.Errorf("expected one key in .rodata, found %d", n) + } + + kv := rodata.Contents[0] + value, ok := kv.Value.([]byte) + if !ok { + return xerrors.Errorf("first value in .rodata is %T not []byte", kv.Value) + } + + buf := make([]byte, len(value)) + copy(buf, value) + + err := patchValue(buf, btf.MapValue(rodata.BTF), consts) + if err != nil { + return err + } + + rodata.Contents[0] = MapKV{kv.Key, buf} + return nil +} + +// Collection is a collection of Programs and Maps associated +// with their symbols +type Collection struct { + Programs map[string]*Program + Maps map[string]*Map +} + +// NewCollection creates a Collection from a specification. +// +// Only maps referenced by at least one of the programs are initialized. +func NewCollection(spec *CollectionSpec) (*Collection, error) { + return NewCollectionWithOptions(spec, CollectionOptions{}) +} + +// NewCollectionWithOptions creates a Collection from a specification. +// +// Only maps referenced by at least one of the programs are initialized. +func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (coll *Collection, err error) { + var ( + maps = make(map[string]*Map) + progs = make(map[string]*Program) + btfs = make(map[*btf.Spec]*btf.Handle) + ) + + defer func() { + for _, btf := range btfs { + btf.Close() + } + + if err == nil { + return + } + + for _, m := range maps { + m.Close() + } + + for _, p := range progs { + p.Close() + } + }() + + loadBTF := func(spec *btf.Spec) (*btf.Handle, error) { + if btfs[spec] != nil { + return btfs[spec], nil + } + + handle, err := btf.NewHandle(spec) + if err != nil { + return nil, err + } + + btfs[spec] = handle + return handle, nil + } + + for mapName, mapSpec := range spec.Maps { + var handle *btf.Handle + if mapSpec.BTF != nil { + handle, err = loadBTF(btf.MapSpec(mapSpec.BTF)) + if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { + return nil, err + } + } + + m, err := newMapWithBTF(mapSpec, handle) + if err != nil { + return nil, xerrors.Errorf("map %s: %w", mapName, err) + } + maps[mapName] = m + } + + for progName, origProgSpec := range spec.Programs { + progSpec := origProgSpec.Copy() + + // Rewrite any reference to a valid map. + for i := range progSpec.Instructions { + ins := &progSpec.Instructions[i] + + if ins.OpCode != asm.LoadImmOp(asm.DWord) || ins.Reference == "" { + continue + } + + if uint32(ins.Constant) != math.MaxUint32 { + // Don't overwrite maps already rewritten, users can + // rewrite programs in the spec themselves + continue + } + + m := maps[ins.Reference] + if m == nil { + return nil, xerrors.Errorf("program %s: missing map %s", progName, ins.Reference) + } + + fd := m.FD() + if fd < 0 { + return nil, xerrors.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd) + } + if err := ins.RewriteMapPtr(m.FD()); err != nil { + return nil, xerrors.Errorf("progam %s: map %s: %w", progName, ins.Reference, err) + } + } + + var handle *btf.Handle + if progSpec.BTF != nil { + handle, err = loadBTF(btf.ProgramSpec(progSpec.BTF)) + if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { + return nil, err + } + } + + prog, err := newProgramWithBTF(progSpec, handle, opts.Programs) + if err != nil { + return nil, xerrors.Errorf("program %s: %w", progName, err) + } + progs[progName] = prog + } + + return &Collection{ + progs, + maps, + }, nil +} + +// LoadCollection parses an object file and converts it to a collection. +func LoadCollection(file string) (*Collection, error) { + spec, err := LoadCollectionSpec(file) + if err != nil { + return nil, err + } + return NewCollection(spec) +} + +// Close frees all maps and programs associated with the collection. +// +// The collection mustn't be used afterwards. +func (coll *Collection) Close() { + for _, prog := range coll.Programs { + prog.Close() + } + for _, m := range coll.Maps { + m.Close() + } +} + +// DetachMap removes the named map from the Collection. +// +// This means that a later call to Close() will not affect this map. +// +// Returns nil if no map of that name exists. +func (coll *Collection) DetachMap(name string) *Map { + m := coll.Maps[name] + delete(coll.Maps, name) + return m +} + +// DetachProgram removes the named program from the Collection. +// +// This means that a later call to Close() will not affect this program. +// +// Returns nil if no program of that name exists. +func (coll *Collection) DetachProgram(name string) *Program { + p := coll.Programs[name] + delete(coll.Programs, name) + return p +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/doc.go b/src/runtime/vendor/github.com/cilium/ebpf/doc.go new file mode 100644 index 000000000..d96e6b1e6 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/doc.go @@ -0,0 +1,17 @@ +// Package ebpf is a toolkit for working with eBPF programs. +// +// eBPF programs are small snippets of code which are executed directly +// in a VM in the Linux kernel, which makes them very fast and flexible. +// Many Linux subsystems now accept eBPF programs. This makes it possible +// to implement highly application specific logic inside the kernel, +// without having to modify the actual kernel itself. +// +// This package is designed for long-running processes which +// want to use eBPF to implement part of their application logic. It has no +// run-time dependencies outside of the library and the Linux kernel itself. +// eBPF code should be compiled ahead of time using clang, and shipped with +// your application as any other resource. +// +// This package doesn't include code required to attach eBPF to Linux +// subsystems, since this varies per subsystem. +package ebpf diff --git a/src/runtime/vendor/github.com/cilium/ebpf/elf_reader.go b/src/runtime/vendor/github.com/cilium/ebpf/elf_reader.go new file mode 100644 index 000000000..344615eaf --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/elf_reader.go @@ -0,0 +1,709 @@ +package ebpf + +import ( + "bytes" + "debug/elf" + "encoding/binary" + "io" + "math" + "os" + "strings" + + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" + "github.com/cilium/ebpf/internal/unix" + + "golang.org/x/xerrors" +) + +type elfCode struct { + *elf.File + symbols []elf.Symbol + symbolsPerSection map[elf.SectionIndex]map[uint64]elf.Symbol + license string + version uint32 +} + +// LoadCollectionSpec parses an ELF file into a CollectionSpec. +func LoadCollectionSpec(file string) (*CollectionSpec, error) { + f, err := os.Open(file) + if err != nil { + return nil, err + } + defer f.Close() + + spec, err := LoadCollectionSpecFromReader(f) + if err != nil { + return nil, xerrors.Errorf("file %s: %w", file, err) + } + return spec, nil +} + +// LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec. +func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { + f, err := elf.NewFile(rd) + if err != nil { + return nil, err + } + defer f.Close() + + symbols, err := f.Symbols() + if err != nil { + return nil, xerrors.Errorf("load symbols: %v", err) + } + + ec := &elfCode{f, symbols, symbolsPerSection(symbols), "", 0} + + var ( + licenseSection *elf.Section + versionSection *elf.Section + btfMaps = make(map[elf.SectionIndex]*elf.Section) + progSections = make(map[elf.SectionIndex]*elf.Section) + relSections = make(map[elf.SectionIndex]*elf.Section) + mapSections = make(map[elf.SectionIndex]*elf.Section) + dataSections = make(map[elf.SectionIndex]*elf.Section) + ) + + for i, sec := range ec.Sections { + switch { + case strings.HasPrefix(sec.Name, "license"): + licenseSection = sec + case strings.HasPrefix(sec.Name, "version"): + versionSection = sec + case strings.HasPrefix(sec.Name, "maps"): + mapSections[elf.SectionIndex(i)] = sec + case sec.Name == ".maps": + btfMaps[elf.SectionIndex(i)] = sec + case sec.Name == ".bss" || sec.Name == ".rodata" || sec.Name == ".data": + dataSections[elf.SectionIndex(i)] = sec + case sec.Type == elf.SHT_REL: + if int(sec.Info) >= len(ec.Sections) { + return nil, xerrors.Errorf("found relocation section %v for missing section %v", i, sec.Info) + } + + // Store relocations under the section index of the target + idx := elf.SectionIndex(sec.Info) + if relSections[idx] != nil { + return nil, xerrors.Errorf("section %d has multiple relocation sections", sec.Info) + } + relSections[idx] = sec + case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0: + progSections[elf.SectionIndex(i)] = sec + } + } + + ec.license, err = loadLicense(licenseSection) + if err != nil { + return nil, xerrors.Errorf("load license: %w", err) + } + + ec.version, err = loadVersion(versionSection, ec.ByteOrder) + if err != nil { + return nil, xerrors.Errorf("load version: %w", err) + } + + btfSpec, err := btf.LoadSpecFromReader(rd) + if err != nil { + return nil, xerrors.Errorf("load BTF: %w", err) + } + + maps := make(map[string]*MapSpec) + if err := ec.loadMaps(maps, mapSections); err != nil { + return nil, xerrors.Errorf("load maps: %w", err) + } + + if len(btfMaps) > 0 { + if err := ec.loadBTFMaps(maps, btfMaps, btfSpec); err != nil { + return nil, xerrors.Errorf("load BTF maps: %w", err) + } + } + + if len(dataSections) > 0 { + if err := ec.loadDataSections(maps, dataSections, btfSpec); err != nil { + return nil, xerrors.Errorf("load data sections: %w", err) + } + } + + relocations, err := ec.loadRelocations(relSections) + if err != nil { + return nil, xerrors.Errorf("load relocations: %w", err) + } + + progs, err := ec.loadPrograms(progSections, relocations, btfSpec) + if err != nil { + return nil, xerrors.Errorf("load programs: %w", err) + } + + return &CollectionSpec{maps, progs}, nil +} + +func loadLicense(sec *elf.Section) (string, error) { + if sec == nil { + return "", xerrors.New("missing license section") + } + data, err := sec.Data() + if err != nil { + return "", xerrors.Errorf("section %s: %v", sec.Name, err) + } + return string(bytes.TrimRight(data, "\000")), nil +} + +func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) { + if sec == nil { + return 0, nil + } + + var version uint32 + if err := binary.Read(sec.Open(), bo, &version); err != nil { + return 0, xerrors.Errorf("section %s: %v", sec.Name, err) + } + return version, nil +} + +func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section, relocations map[elf.SectionIndex]map[uint64]elf.Symbol, btf *btf.Spec) (map[string]*ProgramSpec, error) { + var ( + progs []*ProgramSpec + libs []*ProgramSpec + ) + + for idx, sec := range progSections { + syms := ec.symbolsPerSection[idx] + if len(syms) == 0 { + return nil, xerrors.Errorf("section %v: missing symbols", sec.Name) + } + + funcSym, ok := syms[0] + if !ok { + return nil, xerrors.Errorf("section %v: no label at start", sec.Name) + } + + insns, length, err := ec.loadInstructions(sec, syms, relocations[idx]) + if err != nil { + return nil, xerrors.Errorf("program %s: can't unmarshal instructions: %w", funcSym.Name, err) + } + + progType, attachType := getProgType(sec.Name) + + spec := &ProgramSpec{ + Name: funcSym.Name, + Type: progType, + AttachType: attachType, + License: ec.license, + KernelVersion: ec.version, + Instructions: insns, + } + + if btf != nil { + spec.BTF, err = btf.Program(sec.Name, length) + if err != nil { + return nil, xerrors.Errorf("BTF for section %s (program %s): %w", sec.Name, funcSym.Name, err) + } + } + + if spec.Type == UnspecifiedProgram { + // There is no single name we can use for "library" sections, + // since they may contain multiple functions. We'll decode the + // labels they contain later on, and then link sections that way. + libs = append(libs, spec) + } else { + progs = append(progs, spec) + } + } + + res := make(map[string]*ProgramSpec, len(progs)) + for _, prog := range progs { + err := link(prog, libs) + if err != nil { + return nil, xerrors.Errorf("program %s: %w", prog.Name, err) + } + res[prog.Name] = prog + } + + return res, nil +} + +func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations map[uint64]elf.Symbol) (asm.Instructions, uint64, error) { + var ( + r = section.Open() + insns asm.Instructions + offset uint64 + ) + for { + var ins asm.Instruction + n, err := ins.Unmarshal(r, ec.ByteOrder) + if err == io.EOF { + return insns, offset, nil + } + if err != nil { + return nil, 0, xerrors.Errorf("offset %d: %w", offset, err) + } + + ins.Symbol = symbols[offset].Name + + if rel, ok := relocations[offset]; ok { + if err = ec.relocateInstruction(&ins, rel); err != nil { + return nil, 0, xerrors.Errorf("offset %d: can't relocate instruction: %w", offset, err) + } + } + + insns = append(insns, ins) + offset += n + } +} + +func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) error { + var ( + typ = elf.ST_TYPE(rel.Info) + bind = elf.ST_BIND(rel.Info) + name = rel.Name + ) + + if typ == elf.STT_SECTION { + // Symbols with section type do not have a name set. Get it + // from the section itself. + idx := int(rel.Section) + if idx > len(ec.Sections) { + return xerrors.New("out-of-bounds section index") + } + + name = ec.Sections[idx].Name + } + +outer: + switch { + case ins.OpCode == asm.LoadImmOp(asm.DWord): + // There are two distinct types of a load from a map: + // a direct one, where the value is extracted without + // a call to map_lookup_elem in eBPF, and an indirect one + // that goes via the helper. They are distinguished by + // different relocations. + switch typ { + case elf.STT_SECTION: + // This is a direct load since the referenced symbol is a + // section. Weirdly, the offset of the real symbol in the + // section is encoded in the instruction stream. + if bind != elf.STB_LOCAL { + return xerrors.Errorf("direct load: %s: unsupported relocation %s", name, bind) + } + + // For some reason, clang encodes the offset of the symbol its + // section in the first basic BPF instruction, while the kernel + // expects it in the second one. + ins.Constant <<= 32 + ins.Src = asm.PseudoMapValue + + case elf.STT_NOTYPE: + if bind == elf.STB_GLOBAL && rel.Section == elf.SHN_UNDEF { + // This is a relocation generated by inline assembly. + // We can't do more than assigning ins.Reference. + break outer + } + + // This is an ELF generated on clang < 8, which doesn't tag + // relocations appropriately. + fallthrough + + case elf.STT_OBJECT: + if bind != elf.STB_GLOBAL { + return xerrors.Errorf("load: %s: unsupported binding: %s", name, bind) + } + + ins.Src = asm.PseudoMapFD + + default: + return xerrors.Errorf("load: %s: unsupported relocation: %s", name, typ) + } + + // Mark the instruction as needing an update when creating the + // collection. + if err := ins.RewriteMapPtr(-1); err != nil { + return err + } + + case ins.OpCode.JumpOp() == asm.Call: + if ins.Src != asm.PseudoCall { + return xerrors.Errorf("call: %s: incorrect source register", name) + } + + switch typ { + case elf.STT_NOTYPE, elf.STT_FUNC: + if bind != elf.STB_GLOBAL { + return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind) + } + + case elf.STT_SECTION: + if bind != elf.STB_LOCAL { + return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind) + } + + // The function we want to call is in the indicated section, + // at the offset encoded in the instruction itself. Reverse + // the calculation to find the real function we're looking for. + // A value of -1 references the first instruction in the section. + offset := int64(int32(ins.Constant)+1) * asm.InstructionSize + if offset < 0 { + return xerrors.Errorf("call: %s: invalid offset %d", name, offset) + } + + sym, ok := ec.symbolsPerSection[rel.Section][uint64(offset)] + if !ok { + return xerrors.Errorf("call: %s: no symbol at offset %d", name, offset) + } + + ins.Constant = -1 + name = sym.Name + + default: + return xerrors.Errorf("call: %s: invalid symbol type %s", name, typ) + } + + default: + return xerrors.Errorf("relocation for unsupported instruction: %s", ins.OpCode) + } + + ins.Reference = name + return nil +} + +func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.SectionIndex]*elf.Section) error { + for idx, sec := range mapSections { + syms := ec.symbolsPerSection[idx] + if len(syms) == 0 { + return xerrors.Errorf("section %v: no symbols", sec.Name) + } + + if sec.Size%uint64(len(syms)) != 0 { + return xerrors.Errorf("section %v: map descriptors are not of equal size", sec.Name) + } + + var ( + r = sec.Open() + size = sec.Size / uint64(len(syms)) + ) + for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size { + mapSym, ok := syms[offset] + if !ok { + return xerrors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset) + } + + if maps[mapSym.Name] != nil { + return xerrors.Errorf("section %v: map %v already exists", sec.Name, mapSym) + } + + lr := io.LimitReader(r, int64(size)) + + spec := MapSpec{ + Name: SanitizeName(mapSym.Name, -1), + } + switch { + case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil: + return xerrors.Errorf("map %v: missing type", mapSym) + case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil: + return xerrors.Errorf("map %v: missing key size", mapSym) + case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil: + return xerrors.Errorf("map %v: missing value size", mapSym) + case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil: + return xerrors.Errorf("map %v: missing max entries", mapSym) + case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil: + return xerrors.Errorf("map %v: missing flags", mapSym) + } + + if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil { + return xerrors.Errorf("map %v: unknown and non-zero fields in definition", mapSym) + } + + maps[mapSym.Name] = &spec + } + } + + return nil +} + +func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec, mapSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error { + if spec == nil { + return xerrors.Errorf("missing BTF") + } + + for idx, sec := range mapSections { + syms := ec.symbolsPerSection[idx] + if len(syms) == 0 { + return xerrors.Errorf("section %v: no symbols", sec.Name) + } + + for _, sym := range syms { + name := sym.Name + if maps[name] != nil { + return xerrors.Errorf("section %v: map %v already exists", sec.Name, sym) + } + + btfMap, btfMapMembers, err := spec.Map(name) + if err != nil { + return xerrors.Errorf("map %v: can't get BTF: %w", name, err) + } + + spec, err := mapSpecFromBTF(btfMap, btfMapMembers) + if err != nil { + return xerrors.Errorf("map %v: %w", name, err) + } + + maps[name] = spec + } + } + + return nil +} + +func mapSpecFromBTF(btfMap *btf.Map, btfMapMembers []btf.Member) (*MapSpec, error) { + var ( + mapType, flags, maxEntries uint32 + err error + ) + for _, member := range btfMapMembers { + switch member.Name { + case "type": + mapType, err = uintFromBTF(member.Type) + if err != nil { + return nil, xerrors.Errorf("can't get type: %w", err) + } + + case "map_flags": + flags, err = uintFromBTF(member.Type) + if err != nil { + return nil, xerrors.Errorf("can't get BTF map flags: %w", err) + } + + case "max_entries": + maxEntries, err = uintFromBTF(member.Type) + if err != nil { + return nil, xerrors.Errorf("can't get BTF map max entries: %w", err) + } + + case "key": + case "value": + default: + return nil, xerrors.Errorf("unrecognized field %s in BTF map definition", member.Name) + } + } + + keySize, err := btf.Sizeof(btf.MapKey(btfMap)) + if err != nil { + return nil, xerrors.Errorf("can't get size of BTF key: %w", err) + } + + valueSize, err := btf.Sizeof(btf.MapValue(btfMap)) + if err != nil { + return nil, xerrors.Errorf("can't get size of BTF value: %w", err) + } + + return &MapSpec{ + Type: MapType(mapType), + KeySize: uint32(keySize), + ValueSize: uint32(valueSize), + MaxEntries: maxEntries, + Flags: flags, + BTF: btfMap, + }, nil +} + +// uintFromBTF resolves the __uint macro, which is a pointer to a sized +// array, e.g. for int (*foo)[10], this function will return 10. +func uintFromBTF(typ btf.Type) (uint32, error) { + ptr, ok := typ.(*btf.Pointer) + if !ok { + return 0, xerrors.Errorf("not a pointer: %v", typ) + } + + arr, ok := ptr.Target.(*btf.Array) + if !ok { + return 0, xerrors.Errorf("not a pointer to array: %v", typ) + } + + return arr.Nelems, nil +} + +func (ec *elfCode) loadDataSections(maps map[string]*MapSpec, dataSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error { + if spec == nil { + return xerrors.New("data sections require BTF") + } + + for _, sec := range dataSections { + btfMap, err := spec.Datasec(sec.Name) + if err != nil { + return err + } + + data, err := sec.Data() + if err != nil { + return xerrors.Errorf("data section %s: can't get contents: %w", sec.Name, err) + } + + if uint64(len(data)) > math.MaxUint32 { + return xerrors.Errorf("data section %s: contents exceed maximum size", sec.Name) + } + + mapSpec := &MapSpec{ + Name: SanitizeName(sec.Name, -1), + Type: Array, + KeySize: 4, + ValueSize: uint32(len(data)), + MaxEntries: 1, + Contents: []MapKV{{uint32(0), data}}, + BTF: btfMap, + } + + switch sec.Name { + case ".rodata": + mapSpec.Flags = unix.BPF_F_RDONLY_PROG + mapSpec.Freeze = true + case ".bss": + // The kernel already zero-initializes the map + mapSpec.Contents = nil + } + + maps[sec.Name] = mapSpec + } + return nil +} + +func getProgType(v string) (ProgramType, AttachType) { + types := map[string]ProgramType{ + // From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c#n3568 + "socket": SocketFilter, + "seccomp": SocketFilter, + "kprobe/": Kprobe, + "uprobe/": Kprobe, + "kretprobe/": Kprobe, + "uretprobe/": Kprobe, + "tracepoint/": TracePoint, + "raw_tracepoint/": RawTracepoint, + "xdp": XDP, + "perf_event": PerfEvent, + "lwt_in": LWTIn, + "lwt_out": LWTOut, + "lwt_xmit": LWTXmit, + "lwt_seg6local": LWTSeg6Local, + "sockops": SockOps, + "sk_skb": SkSKB, + "sk_msg": SkMsg, + "lirc_mode2": LircMode2, + "flow_dissector": FlowDissector, + + "cgroup_skb/": CGroupSKB, + "cgroup/dev": CGroupDevice, + "cgroup/skb": CGroupSKB, + "cgroup/sock": CGroupSock, + "cgroup/post_bind": CGroupSock, + "cgroup/bind": CGroupSockAddr, + "cgroup/connect": CGroupSockAddr, + "cgroup/sendmsg": CGroupSockAddr, + "cgroup/recvmsg": CGroupSockAddr, + "cgroup/sysctl": CGroupSysctl, + "cgroup/getsockopt": CGroupSockopt, + "cgroup/setsockopt": CGroupSockopt, + "classifier": SchedCLS, + "action": SchedACT, + } + attachTypes := map[string]AttachType{ + "cgroup_skb/ingress": AttachCGroupInetIngress, + "cgroup_skb/egress": AttachCGroupInetEgress, + "cgroup/sock": AttachCGroupInetSockCreate, + "cgroup/post_bind4": AttachCGroupInet4PostBind, + "cgroup/post_bind6": AttachCGroupInet6PostBind, + "cgroup/dev": AttachCGroupDevice, + "sockops": AttachCGroupSockOps, + "sk_skb/stream_parser": AttachSkSKBStreamParser, + "sk_skb/stream_verdict": AttachSkSKBStreamVerdict, + "sk_msg": AttachSkSKBStreamVerdict, + "lirc_mode2": AttachLircMode2, + "flow_dissector": AttachFlowDissector, + "cgroup/bind4": AttachCGroupInet4Bind, + "cgroup/bind6": AttachCGroupInet6Bind, + "cgroup/connect4": AttachCGroupInet4Connect, + "cgroup/connect6": AttachCGroupInet6Connect, + "cgroup/sendmsg4": AttachCGroupUDP4Sendmsg, + "cgroup/sendmsg6": AttachCGroupUDP6Sendmsg, + "cgroup/recvmsg4": AttachCGroupUDP4Recvmsg, + "cgroup/recvmsg6": AttachCGroupUDP6Recvmsg, + "cgroup/sysctl": AttachCGroupSysctl, + "cgroup/getsockopt": AttachCGroupGetsockopt, + "cgroup/setsockopt": AttachCGroupSetsockopt, + } + attachType := AttachNone + for k, t := range attachTypes { + if strings.HasPrefix(v, k) { + attachType = t + } + } + + for k, t := range types { + if strings.HasPrefix(v, k) { + return t, attachType + } + } + return UnspecifiedProgram, AttachNone +} + +func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) (map[elf.SectionIndex]map[uint64]elf.Symbol, error) { + result := make(map[elf.SectionIndex]map[uint64]elf.Symbol) + for idx, sec := range sections { + rels := make(map[uint64]elf.Symbol) + + if sec.Entsize < 16 { + return nil, xerrors.Errorf("section %s: relocations are less than 16 bytes", sec.Name) + } + + r := sec.Open() + for off := uint64(0); off < sec.Size; off += sec.Entsize { + ent := io.LimitReader(r, int64(sec.Entsize)) + + var rel elf.Rel64 + if binary.Read(ent, ec.ByteOrder, &rel) != nil { + return nil, xerrors.Errorf("can't parse relocation at offset %v", off) + } + + symNo := int(elf.R_SYM64(rel.Info) - 1) + if symNo >= len(ec.symbols) { + return nil, xerrors.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo) + } + + rels[rel.Off] = ec.symbols[symNo] + } + + result[idx] = rels + } + return result, nil +} + +func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]elf.Symbol { + result := make(map[elf.SectionIndex]map[uint64]elf.Symbol) + for _, sym := range symbols { + switch elf.ST_TYPE(sym.Info) { + case elf.STT_NOTYPE: + // Older versions of LLVM doesn't tag + // symbols correctly. + break + case elf.STT_OBJECT: + break + case elf.STT_FUNC: + break + default: + continue + } + + if sym.Section == elf.SHN_UNDEF || sym.Section >= elf.SHN_LORESERVE { + continue + } + + if sym.Name == "" { + continue + } + + idx := sym.Section + if _, ok := result[idx]; !ok { + result[idx] = make(map[uint64]elf.Symbol) + } + result[idx][sym.Value] = sym + } + return result +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/go.mod b/src/runtime/vendor/github.com/cilium/ebpf/go.mod new file mode 100644 index 000000000..1d3420b85 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/go.mod @@ -0,0 +1,8 @@ +module github.com/cilium/ebpf + +go 1.12 + +require ( + golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 +) diff --git a/src/runtime/vendor/github.com/cilium/ebpf/go.sum b/src/runtime/vendor/github.com/cilium/ebpf/go.sum new file mode 100644 index 000000000..c4e24f505 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/go.sum @@ -0,0 +1,6 @@ +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/btf.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/btf.go new file mode 100644 index 000000000..0103f4c2d --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/btf.go @@ -0,0 +1,613 @@ +package btf + +import ( + "bytes" + "debug/elf" + "encoding/binary" + "io" + "io/ioutil" + "math" + "reflect" + "unsafe" + + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/unix" + + "golang.org/x/xerrors" +) + +const btfMagic = 0xeB9F + +// Errors returned by BTF functions. +var ( + ErrNotSupported = internal.ErrNotSupported +) + +// Spec represents decoded BTF. +type Spec struct { + rawTypes []rawType + strings stringTable + types map[string][]Type + funcInfos map[string]extInfo + lineInfos map[string]extInfo +} + +type btfHeader struct { + Magic uint16 + Version uint8 + Flags uint8 + HdrLen uint32 + + TypeOff uint32 + TypeLen uint32 + StringOff uint32 + StringLen uint32 +} + +// LoadSpecFromReader reads BTF sections from an ELF. +// +// Returns a nil Spec and no error if no BTF was present. +func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { + file, err := elf.NewFile(rd) + if err != nil { + return nil, err + } + defer file.Close() + + var ( + btfSection *elf.Section + btfExtSection *elf.Section + sectionSizes = make(map[string]uint32) + ) + + for _, sec := range file.Sections { + switch sec.Name { + case ".BTF": + btfSection = sec + case ".BTF.ext": + btfExtSection = sec + default: + if sec.Type != elf.SHT_PROGBITS && sec.Type != elf.SHT_NOBITS { + break + } + + if sec.Size > math.MaxUint32 { + return nil, xerrors.Errorf("section %s exceeds maximum size", sec.Name) + } + + sectionSizes[sec.Name] = uint32(sec.Size) + } + } + + if btfSection == nil { + return nil, nil + } + + symbols, err := file.Symbols() + if err != nil { + return nil, xerrors.Errorf("can't read symbols: %v", err) + } + + variableOffsets := make(map[variable]uint32) + for _, symbol := range symbols { + if idx := symbol.Section; idx >= elf.SHN_LORESERVE && idx <= elf.SHN_HIRESERVE { + // Ignore things like SHN_ABS + continue + } + + secName := file.Sections[symbol.Section].Name + if _, ok := sectionSizes[secName]; !ok { + continue + } + + if symbol.Value > math.MaxUint32 { + return nil, xerrors.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name) + } + + variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value) + } + + rawTypes, rawStrings, err := parseBTF(btfSection.Open(), file.ByteOrder) + if err != nil { + return nil, err + } + + err = fixupDatasec(rawTypes, rawStrings, sectionSizes, variableOffsets) + if err != nil { + return nil, err + } + + types, err := inflateRawTypes(rawTypes, rawStrings) + if err != nil { + return nil, err + } + + var ( + funcInfos = make(map[string]extInfo) + lineInfos = make(map[string]extInfo) + ) + if btfExtSection != nil { + funcInfos, lineInfos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, rawStrings) + if err != nil { + return nil, xerrors.Errorf("can't read ext info: %w", err) + } + } + + return &Spec{ + rawTypes: rawTypes, + types: types, + strings: rawStrings, + funcInfos: funcInfos, + lineInfos: lineInfos, + }, nil +} + +func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) { + rawBTF, err := ioutil.ReadAll(btf) + if err != nil { + return nil, nil, xerrors.Errorf("can't read BTF: %v", err) + } + + rd := bytes.NewReader(rawBTF) + + var header btfHeader + if err := binary.Read(rd, bo, &header); err != nil { + return nil, nil, xerrors.Errorf("can't read header: %v", err) + } + + if header.Magic != btfMagic { + return nil, nil, xerrors.Errorf("incorrect magic value %v", header.Magic) + } + + if header.Version != 1 { + return nil, nil, xerrors.Errorf("unexpected version %v", header.Version) + } + + if header.Flags != 0 { + return nil, nil, xerrors.Errorf("unsupported flags %v", header.Flags) + } + + remainder := int64(header.HdrLen) - int64(binary.Size(&header)) + if remainder < 0 { + return nil, nil, xerrors.New("header is too short") + } + + if _, err := io.CopyN(internal.DiscardZeroes{}, rd, remainder); err != nil { + return nil, nil, xerrors.Errorf("header padding: %v", err) + } + + if _, err := rd.Seek(int64(header.HdrLen+header.StringOff), io.SeekStart); err != nil { + return nil, nil, xerrors.Errorf("can't seek to start of string section: %v", err) + } + + rawStrings, err := readStringTable(io.LimitReader(rd, int64(header.StringLen))) + if err != nil { + return nil, nil, xerrors.Errorf("can't read type names: %w", err) + } + + if _, err := rd.Seek(int64(header.HdrLen+header.TypeOff), io.SeekStart); err != nil { + return nil, nil, xerrors.Errorf("can't seek to start of type section: %v", err) + } + + rawTypes, err := readTypes(io.LimitReader(rd, int64(header.TypeLen)), bo) + if err != nil { + return nil, nil, xerrors.Errorf("can't read types: %w", err) + } + + return rawTypes, rawStrings, nil +} + +type variable struct { + section string + name string +} + +func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) error { + for i, rawType := range rawTypes { + if rawType.Kind() != kindDatasec { + continue + } + + name, err := rawStrings.Lookup(rawType.NameOff) + if err != nil { + return err + } + + size, ok := sectionSizes[name] + if !ok { + return xerrors.Errorf("data section %s: missing size", name) + } + + rawTypes[i].SizeType = size + + secinfos := rawType.data.([]btfVarSecinfo) + for j, secInfo := range secinfos { + id := int(secInfo.Type - 1) + if id >= len(rawTypes) { + return xerrors.Errorf("data section %s: invalid type id %d for variable %d", name, id, j) + } + + varName, err := rawStrings.Lookup(rawTypes[id].NameOff) + if err != nil { + return xerrors.Errorf("data section %s: can't get name for type %d: %w", name, id, err) + } + + offset, ok := variableOffsets[variable{name, varName}] + if !ok { + return xerrors.Errorf("data section %s: missing offset for variable %s", name, varName) + } + + secinfos[j].Offset = offset + } + } + + return nil +} + +func (s *Spec) marshal(bo binary.ByteOrder) ([]byte, error) { + var ( + buf bytes.Buffer + header = new(btfHeader) + headerLen = binary.Size(header) + ) + + // Reserve space for the header. We have to write it last since + // we don't know the size of the type section yet. + _, _ = buf.Write(make([]byte, headerLen)) + + // Write type section, just after the header. + for _, typ := range s.rawTypes { + if err := typ.Marshal(&buf, bo); err != nil { + return nil, xerrors.Errorf("can't marshal BTF: %w", err) + } + } + + typeLen := uint32(buf.Len() - headerLen) + + // Write string section after type section. + _, _ = buf.Write(s.strings) + + // Fill out the header, and write it out. + header = &btfHeader{ + Magic: btfMagic, + Version: 1, + Flags: 0, + HdrLen: uint32(headerLen), + TypeOff: 0, + TypeLen: typeLen, + StringOff: typeLen, + StringLen: uint32(len(s.strings)), + } + + raw := buf.Bytes() + err := binary.Write(sliceWriter(raw[:headerLen]), bo, header) + if err != nil { + return nil, xerrors.Errorf("can't write header: %v", err) + } + + return raw, nil +} + +type sliceWriter []byte + +func (sw sliceWriter) Write(p []byte) (int, error) { + if len(p) != len(sw) { + return 0, xerrors.New("size doesn't match") + } + + return copy(sw, p), nil +} + +// Program finds the BTF for a specific section. +// +// Length is the number of bytes in the raw BPF instruction stream. +// +// Returns an error if there is no BTF. +func (s *Spec) Program(name string, length uint64) (*Program, error) { + if length == 0 { + return nil, xerrors.New("length musn't be zero") + } + + funcInfos, funcOK := s.funcInfos[name] + lineInfos, lineOK := s.lineInfos[name] + + if !funcOK && !lineOK { + return nil, xerrors.Errorf("no BTF for program %s", name) + } + + return &Program{s, length, funcInfos, lineInfos}, nil +} + +// Map finds the BTF for a map. +// +// Returns an error if there is no BTF for the given name. +func (s *Spec) Map(name string) (*Map, []Member, error) { + var mapVar Var + if err := s.FindType(name, &mapVar); err != nil { + return nil, nil, err + } + + mapStruct, ok := mapVar.Type.(*Struct) + if !ok { + return nil, nil, xerrors.Errorf("expected struct, have %s", mapVar.Type) + } + + var key, value Type + for _, member := range mapStruct.Members { + switch member.Name { + case "key": + key = member.Type + + case "value": + value = member.Type + } + } + + if key == nil { + return nil, nil, xerrors.Errorf("map %s: missing 'key' in type", name) + } + + if value == nil { + return nil, nil, xerrors.Errorf("map %s: missing 'value' in type", name) + } + + return &Map{s, key, value}, mapStruct.Members, nil +} + +// Datasec returns the BTF required to create maps which represent data sections. +func (s *Spec) Datasec(name string) (*Map, error) { + var datasec Datasec + if err := s.FindType(name, &datasec); err != nil { + return nil, xerrors.Errorf("data section %s: can't get BTF: %w", name, err) + } + + return &Map{s, &Void{}, &datasec}, nil +} + +var errNotFound = xerrors.New("not found") + +// FindType searches for a type with a specific name. +// +// hint determines the type of the returned Type. +// +// Returns an error if there is no or multiple matches. +func (s *Spec) FindType(name string, typ Type) error { + var ( + wanted = reflect.TypeOf(typ) + candidate Type + ) + + for _, typ := range s.types[name] { + if reflect.TypeOf(typ) != wanted { + continue + } + + if candidate != nil { + return xerrors.Errorf("type %s: multiple candidates for %T", name, typ) + } + + candidate = typ + } + + if candidate == nil { + return xerrors.Errorf("type %s: %w", name, errNotFound) + } + + value := reflect.Indirect(reflect.ValueOf(copyType(candidate))) + reflect.Indirect(reflect.ValueOf(typ)).Set(value) + return nil +} + +// Handle is a reference to BTF loaded into the kernel. +type Handle struct { + fd *internal.FD +} + +// NewHandle loads BTF into the kernel. +// +// Returns ErrNotSupported if BTF is not supported. +func NewHandle(spec *Spec) (*Handle, error) { + if err := haveBTF(); err != nil { + return nil, err + } + + btf, err := spec.marshal(internal.NativeEndian) + if err != nil { + return nil, xerrors.Errorf("can't marshal BTF: %w", err) + } + + if uint64(len(btf)) > math.MaxUint32 { + return nil, xerrors.New("BTF exceeds the maximum size") + } + + attr := &bpfLoadBTFAttr{ + btf: internal.NewSlicePointer(btf), + btfSize: uint32(len(btf)), + } + + fd, err := bpfLoadBTF(attr) + if err != nil { + logBuf := make([]byte, 64*1024) + attr.logBuf = internal.NewSlicePointer(logBuf) + attr.btfLogSize = uint32(len(logBuf)) + attr.btfLogLevel = 1 + _, logErr := bpfLoadBTF(attr) + return nil, internal.ErrorWithLog(err, logBuf, logErr) + } + + return &Handle{fd}, nil +} + +// Close destroys the handle. +// +// Subsequent calls to FD will return an invalid value. +func (h *Handle) Close() error { + return h.fd.Close() +} + +// FD returns the file descriptor for the handle. +func (h *Handle) FD() int { + value, err := h.fd.Value() + if err != nil { + return -1 + } + + return int(value) +} + +// Map is the BTF for a map. +type Map struct { + spec *Spec + key, value Type +} + +// MapSpec should be a method on Map, but is a free function +// to hide it from users of the ebpf package. +func MapSpec(m *Map) *Spec { + return m.spec +} + +// MapKey should be a method on Map, but is a free function +// to hide it from users of the ebpf package. +func MapKey(m *Map) Type { + return m.key +} + +// MapValue should be a method on Map, but is a free function +// to hide it from users of the ebpf package. +func MapValue(m *Map) Type { + return m.value +} + +// Program is the BTF information for a stream of instructions. +type Program struct { + spec *Spec + length uint64 + funcInfos, lineInfos extInfo +} + +// ProgramSpec returns the Spec needed for loading function and line infos into the kernel. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramSpec(s *Program) *Spec { + return s.spec +} + +// ProgramAppend the information from other to the Program. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramAppend(s, other *Program) error { + funcInfos, err := s.funcInfos.append(other.funcInfos, s.length) + if err != nil { + return xerrors.Errorf("func infos: %w", err) + } + + lineInfos, err := s.lineInfos.append(other.lineInfos, s.length) + if err != nil { + return xerrors.Errorf("line infos: %w", err) + } + + s.length += other.length + s.funcInfos = funcInfos + s.lineInfos = lineInfos + return nil +} + +// ProgramFuncInfos returns the binary form of BTF function infos. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramFuncInfos(s *Program) (recordSize uint32, bytes []byte, err error) { + bytes, err = s.funcInfos.MarshalBinary() + if err != nil { + return 0, nil, err + } + + return s.funcInfos.recordSize, bytes, nil +} + +// ProgramLineInfos returns the binary form of BTF line infos. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramLineInfos(s *Program) (recordSize uint32, bytes []byte, err error) { + bytes, err = s.lineInfos.MarshalBinary() + if err != nil { + return 0, nil, err + } + + return s.lineInfos.recordSize, bytes, nil +} + +type bpfLoadBTFAttr struct { + btf internal.Pointer + logBuf internal.Pointer + btfSize uint32 + btfLogSize uint32 + btfLogLevel uint32 +} + +func bpfLoadBTF(attr *bpfLoadBTFAttr) (*internal.FD, error) { + const _BTFLoad = 18 + + fd, err := internal.BPF(_BTFLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if err != nil { + return nil, err + } + + return internal.NewFD(uint32(fd)), nil +} + +func minimalBTF(bo binary.ByteOrder) []byte { + const minHeaderLength = 24 + + var ( + types struct { + Integer btfType + Var btfType + btfVar struct{ Linkage uint32 } + } + typLen = uint32(binary.Size(&types)) + strings = []byte{0, 'a', 0} + header = btfHeader{ + Magic: btfMagic, + Version: 1, + HdrLen: minHeaderLength, + TypeOff: 0, + TypeLen: typLen, + StringOff: typLen, + StringLen: uint32(len(strings)), + } + ) + + // We use a BTF_KIND_VAR here, to make sure that + // the kernel understands BTF at least as well as we + // do. BTF_KIND_VAR was introduced ~5.1. + types.Integer.SetKind(kindPointer) + types.Var.NameOff = 1 + types.Var.SetKind(kindVar) + types.Var.SizeType = 1 + + buf := new(bytes.Buffer) + _ = binary.Write(buf, bo, &header) + _ = binary.Write(buf, bo, &types) + buf.Write(strings) + + return buf.Bytes() +} + +var haveBTF = internal.FeatureTest("BTF", "5.1", func() bool { + btf := minimalBTF(internal.NativeEndian) + fd, err := bpfLoadBTF(&bpfLoadBTFAttr{ + btf: internal.NewSlicePointer(btf), + btfSize: uint32(len(btf)), + }) + if err == nil { + fd.Close() + } + // Check for EINVAL specifically, rather than err != nil since we + // otherwise misdetect due to insufficient permissions. + return !xerrors.Is(err, unix.EINVAL) +}) diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go new file mode 100644 index 000000000..37c940bbd --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go @@ -0,0 +1,238 @@ +package btf + +import ( + "encoding/binary" + "fmt" + "io" + + "golang.org/x/xerrors" +) + +// btfKind describes a Type. +type btfKind uint8 + +// Equivalents of the BTF_KIND_* constants. +const ( + kindUnknown btfKind = iota + kindInt + kindPointer + kindArray + kindStruct + kindUnion + kindEnum + kindForward + kindTypedef + kindVolatile + kindConst + kindRestrict + // Added ~4.20 + kindFunc + kindFuncProto + // Added ~5.1 + kindVar + kindDatasec +) + +const ( + btfTypeKindShift = 24 + btfTypeKindLen = 4 + btfTypeVlenShift = 0 + btfTypeVlenMask = 16 +) + +// btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst. +type btfType struct { + NameOff uint32 + /* "info" bits arrangement + * bits 0-15: vlen (e.g. # of struct's members) + * bits 16-23: unused + * bits 24-27: kind (e.g. int, ptr, array...etc) + * bits 28-30: unused + * bit 31: kind_flag, currently used by + * struct, union and fwd + */ + Info uint32 + /* "size" is used by INT, ENUM, STRUCT and UNION. + * "size" tells the size of the type it is describing. + * + * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, + * FUNC and FUNC_PROTO. + * "type" is a type_id referring to another type. + */ + SizeType uint32 +} + +func (k btfKind) String() string { + switch k { + case kindUnknown: + return "Unknown" + case kindInt: + return "Integer" + case kindPointer: + return "Pointer" + case kindArray: + return "Array" + case kindStruct: + return "Struct" + case kindUnion: + return "Union" + case kindEnum: + return "Enumeration" + case kindForward: + return "Forward" + case kindTypedef: + return "Typedef" + case kindVolatile: + return "Volatile" + case kindConst: + return "Const" + case kindRestrict: + return "Restrict" + case kindFunc: + return "Function" + case kindFuncProto: + return "Function Proto" + case kindVar: + return "Variable" + case kindDatasec: + return "Section" + default: + return fmt.Sprintf("Unknown (%d)", k) + } +} + +func mask(len uint32) uint32 { + return (1 << len) - 1 +} + +func (bt *btfType) info(len, shift uint32) uint32 { + return (bt.Info >> shift) & mask(len) +} + +func (bt *btfType) setInfo(value, len, shift uint32) { + bt.Info &^= mask(len) << shift + bt.Info |= (value & mask(len)) << shift +} + +func (bt *btfType) Kind() btfKind { + return btfKind(bt.info(btfTypeKindLen, btfTypeKindShift)) +} + +func (bt *btfType) SetKind(kind btfKind) { + bt.setInfo(uint32(kind), btfTypeKindLen, btfTypeKindShift) +} + +func (bt *btfType) Vlen() int { + return int(bt.info(btfTypeVlenMask, btfTypeVlenShift)) +} + +func (bt *btfType) SetVlen(vlen int) { + bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift) +} + +func (bt *btfType) Type() TypeID { + // TODO: Panic here if wrong kind? + return TypeID(bt.SizeType) +} + +func (bt *btfType) Size() uint32 { + // TODO: Panic here if wrong kind? + return bt.SizeType +} + +type rawType struct { + btfType + data interface{} +} + +func (rt *rawType) Marshal(w io.Writer, bo binary.ByteOrder) error { + if err := binary.Write(w, bo, &rt.btfType); err != nil { + return err + } + + if rt.data == nil { + return nil + } + + return binary.Write(w, bo, rt.data) +} + +type btfArray struct { + Type TypeID + IndexType TypeID + Nelems uint32 +} + +type btfMember struct { + NameOff uint32 + Type TypeID + Offset uint32 +} + +type btfVarSecinfo struct { + Type TypeID + Offset uint32 + Size uint32 +} + +type btfVariable struct { + Linkage uint32 +} + +func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) { + var ( + header btfType + types []rawType + ) + + for id := TypeID(1); ; id++ { + if err := binary.Read(r, bo, &header); err == io.EOF { + return types, nil + } else if err != nil { + return nil, xerrors.Errorf("can't read type info for id %v: %v", id, err) + } + + var data interface{} + switch header.Kind() { + case kindInt: + // sizeof(uint32) + data = make([]byte, 4) + case kindPointer: + case kindArray: + data = new(btfArray) + case kindStruct: + fallthrough + case kindUnion: + data = make([]btfMember, header.Vlen()) + case kindEnum: + // sizeof(struct btf_enum) + data = make([]byte, header.Vlen()*4*2) + case kindForward: + case kindTypedef: + case kindVolatile: + case kindConst: + case kindRestrict: + case kindFunc: + case kindFuncProto: + // sizeof(struct btf_param) + data = make([]byte, header.Vlen()*4*2) + case kindVar: + data = new(btfVariable) + case kindDatasec: + data = make([]btfVarSecinfo, header.Vlen()) + default: + return nil, xerrors.Errorf("type id %v: unknown kind: %v", id, header.Kind()) + } + + if data == nil { + types = append(types, rawType{header, nil}) + continue + } + + if err := binary.Read(r, bo, data); err != nil { + return nil, xerrors.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err) + } + + types = append(types, rawType{header, data}) + } +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/doc.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/doc.go new file mode 100644 index 000000000..ad2576cb2 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/doc.go @@ -0,0 +1,8 @@ +// Package btf handles data encoded according to the BPF Type Format. +// +// The canonical documentation lives in the Linux kernel repository and is +// available at https://www.kernel.org/doc/html/latest/bpf/btf.html +// +// The API is very much unstable. You should only use this via the main +// ebpf library. +package btf diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go new file mode 100644 index 000000000..1b1e33c4d --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go @@ -0,0 +1,182 @@ +package btf + +import ( + "bytes" + "encoding/binary" + "io" + "io/ioutil" + + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal" + + "golang.org/x/xerrors" +) + +type btfExtHeader struct { + Magic uint16 + Version uint8 + Flags uint8 + HdrLen uint32 + + FuncInfoOff uint32 + FuncInfoLen uint32 + LineInfoOff uint32 + LineInfoLen uint32 +} + +func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, err error) { + var header btfExtHeader + if err := binary.Read(r, bo, &header); err != nil { + return nil, nil, xerrors.Errorf("can't read header: %v", err) + } + + if header.Magic != btfMagic { + return nil, nil, xerrors.Errorf("incorrect magic value %v", header.Magic) + } + + if header.Version != 1 { + return nil, nil, xerrors.Errorf("unexpected version %v", header.Version) + } + + if header.Flags != 0 { + return nil, nil, xerrors.Errorf("unsupported flags %v", header.Flags) + } + + remainder := int64(header.HdrLen) - int64(binary.Size(&header)) + if remainder < 0 { + return nil, nil, xerrors.New("header is too short") + } + + // Of course, the .BTF.ext header has different semantics than the + // .BTF ext header. We need to ignore non-null values. + _, err = io.CopyN(ioutil.Discard, r, remainder) + if err != nil { + return nil, nil, xerrors.Errorf("header padding: %v", err) + } + + if _, err := r.Seek(int64(header.HdrLen+header.FuncInfoOff), io.SeekStart); err != nil { + return nil, nil, xerrors.Errorf("can't seek to function info section: %v", err) + } + + funcInfo, err = parseExtInfo(io.LimitReader(r, int64(header.FuncInfoLen)), bo, strings) + if err != nil { + return nil, nil, xerrors.Errorf("function info: %w", err) + } + + if _, err := r.Seek(int64(header.HdrLen+header.LineInfoOff), io.SeekStart); err != nil { + return nil, nil, xerrors.Errorf("can't seek to line info section: %v", err) + } + + lineInfo, err = parseExtInfo(io.LimitReader(r, int64(header.LineInfoLen)), bo, strings) + if err != nil { + return nil, nil, xerrors.Errorf("line info: %w", err) + } + + return funcInfo, lineInfo, nil +} + +type btfExtInfoSec struct { + SecNameOff uint32 + NumInfo uint32 +} + +type extInfoRecord struct { + InsnOff uint64 + Opaque []byte +} + +type extInfo struct { + recordSize uint32 + records []extInfoRecord +} + +func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) { + if other.recordSize != ei.recordSize { + return extInfo{}, xerrors.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize) + } + + records := make([]extInfoRecord, 0, len(ei.records)+len(other.records)) + records = append(records, ei.records...) + for _, info := range other.records { + records = append(records, extInfoRecord{ + InsnOff: info.InsnOff + offset, + Opaque: info.Opaque, + }) + } + return extInfo{ei.recordSize, records}, nil +} + +func (ei extInfo) MarshalBinary() ([]byte, error) { + if len(ei.records) == 0 { + return nil, nil + } + + buf := bytes.NewBuffer(make([]byte, 0, int(ei.recordSize)*len(ei.records))) + for _, info := range ei.records { + // The kernel expects offsets in number of raw bpf instructions, + // while the ELF tracks it in bytes. + insnOff := uint32(info.InsnOff / asm.InstructionSize) + if err := binary.Write(buf, internal.NativeEndian, insnOff); err != nil { + return nil, xerrors.Errorf("can't write instruction offset: %v", err) + } + + buf.Write(info.Opaque) + } + + return buf.Bytes(), nil +} + +func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) { + var recordSize uint32 + if err := binary.Read(r, bo, &recordSize); err != nil { + return nil, xerrors.Errorf("can't read record size: %v", err) + } + + if recordSize < 4 { + // Need at least insnOff + return nil, xerrors.New("record size too short") + } + + result := make(map[string]extInfo) + for { + var infoHeader btfExtInfoSec + if err := binary.Read(r, bo, &infoHeader); err == io.EOF { + return result, nil + } else if err != nil { + return nil, xerrors.Errorf("can't read ext info header: %v", err) + } + + secName, err := strings.Lookup(infoHeader.SecNameOff) + if err != nil { + return nil, xerrors.Errorf("can't get section name: %w", err) + } + + if infoHeader.NumInfo == 0 { + return nil, xerrors.Errorf("section %s has invalid number of records", secName) + } + + var records []extInfoRecord + for i := uint32(0); i < infoHeader.NumInfo; i++ { + var byteOff uint32 + if err := binary.Read(r, bo, &byteOff); err != nil { + return nil, xerrors.Errorf("section %v: can't read extended info offset: %v", secName, err) + } + + buf := make([]byte, int(recordSize-4)) + if _, err := io.ReadFull(r, buf); err != nil { + return nil, xerrors.Errorf("section %v: can't read record: %v", secName, err) + } + + if byteOff%asm.InstructionSize != 0 { + return nil, xerrors.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff) + } + + records = append(records, extInfoRecord{uint64(byteOff), buf}) + } + + result[secName] = extInfo{ + recordSize, + records, + } + } +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/strings.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/strings.go new file mode 100644 index 000000000..a5f905f56 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/strings.go @@ -0,0 +1,60 @@ +package btf + +import ( + "bytes" + "io" + "io/ioutil" + + "golang.org/x/xerrors" +) + +type stringTable []byte + +func readStringTable(r io.Reader) (stringTable, error) { + contents, err := ioutil.ReadAll(r) + if err != nil { + return nil, xerrors.Errorf("can't read string table: %v", err) + } + + if len(contents) < 1 { + return nil, xerrors.New("string table is empty") + } + + if contents[0] != '\x00' { + return nil, xerrors.New("first item in string table is non-empty") + } + + if contents[len(contents)-1] != '\x00' { + return nil, xerrors.New("string table isn't null terminated") + } + + return stringTable(contents), nil +} + +func (st stringTable) Lookup(offset uint32) (string, error) { + if int64(offset) > int64(^uint(0)>>1) { + return "", xerrors.Errorf("offset %d overflows int", offset) + } + + pos := int(offset) + if pos >= len(st) { + return "", xerrors.Errorf("offset %d is out of bounds", offset) + } + + if pos > 0 && st[pos-1] != '\x00' { + return "", xerrors.Errorf("offset %d isn't start of a string", offset) + } + + str := st[pos:] + end := bytes.IndexByte(str, '\x00') + if end == -1 { + return "", xerrors.Errorf("offset %d isn't null terminated", offset) + } + + return string(str[:end]), nil +} + +func (st stringTable) LookupName(offset uint32) (Name, error) { + str, err := st.Lookup(offset) + return Name(str), err +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/types.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/types.go new file mode 100644 index 000000000..101a40f32 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/btf/types.go @@ -0,0 +1,586 @@ +package btf + +import ( + "math" + + "golang.org/x/xerrors" +) + +const maxTypeDepth = 32 + +// TypeID identifies a type in a BTF section. +type TypeID uint32 + +// ID implements part of the Type interface. +func (tid TypeID) ID() TypeID { + return tid +} + +// Type represents a type described by BTF. +type Type interface { + ID() TypeID + + // Make a copy of the type, without copying Type members. + copy() Type + + walk(*copyStack) +} + +// Name identifies a type. +// +// Anonymous types have an empty name. +type Name string + +func (n Name) name() string { + return string(n) +} + +// Void is the unit type of BTF. +type Void struct{} + +func (v Void) ID() TypeID { return 0 } +func (v Void) copy() Type { return Void{} } +func (v Void) walk(*copyStack) {} + +// Int is an integer of a given length. +type Int struct { + TypeID + Name + + // The size of the integer in bytes. + Size uint32 +} + +func (i *Int) size() uint32 { return i.Size } +func (i *Int) walk(*copyStack) {} +func (i *Int) copy() Type { + cpy := *i + return &cpy +} + +// Pointer is a pointer to another type. +type Pointer struct { + TypeID + Target Type +} + +func (p *Pointer) size() uint32 { return 8 } +func (p *Pointer) walk(cs *copyStack) { cs.push(&p.Target) } +func (p *Pointer) copy() Type { + cpy := *p + return &cpy +} + +// Array is an array with a fixed number of elements. +type Array struct { + TypeID + Type Type + Nelems uint32 +} + +func (arr *Array) walk(cs *copyStack) { cs.push(&arr.Type) } +func (arr *Array) copy() Type { + cpy := *arr + return &cpy +} + +// Struct is a compound type of consecutive members. +type Struct struct { + TypeID + Name + // The size of the struct including padding, in bytes + Size uint32 + Members []Member +} + +func (s *Struct) size() uint32 { return s.Size } + +func (s *Struct) walk(cs *copyStack) { + for i := range s.Members { + cs.push(&s.Members[i].Type) + } +} + +func (s *Struct) copy() Type { + cpy := *s + cpy.Members = make([]Member, len(s.Members)) + copy(cpy.Members, s.Members) + return &cpy +} + +// Union is a compound type where members occupy the same memory. +type Union struct { + TypeID + Name + // The size of the union including padding, in bytes. + Size uint32 + Members []Member +} + +func (u *Union) size() uint32 { return u.Size } + +func (u *Union) walk(cs *copyStack) { + for i := range u.Members { + cs.push(&u.Members[i].Type) + } +} + +func (u *Union) copy() Type { + cpy := *u + cpy.Members = make([]Member, len(u.Members)) + copy(cpy.Members, u.Members) + return &cpy +} + +// Member is part of a Struct or Union. +// +// It is not a valid Type. +type Member struct { + Name + Type Type + Offset uint32 +} + +// Enum lists possible values. +type Enum struct { + TypeID + Name +} + +func (e *Enum) size() uint32 { return 4 } +func (e *Enum) walk(*copyStack) {} +func (e *Enum) copy() Type { + cpy := *e + return &cpy +} + +// Fwd is a forward declaration of a Type. +type Fwd struct { + TypeID + Name +} + +func (f *Fwd) walk(*copyStack) {} +func (f *Fwd) copy() Type { + cpy := *f + return &cpy +} + +// Typedef is an alias of a Type. +type Typedef struct { + TypeID + Name + Type Type +} + +func (td *Typedef) walk(cs *copyStack) { cs.push(&td.Type) } +func (td *Typedef) copy() Type { + cpy := *td + return &cpy +} + +// Volatile is a modifier. +type Volatile struct { + TypeID + Type Type +} + +func (v *Volatile) walk(cs *copyStack) { cs.push(&v.Type) } +func (v *Volatile) copy() Type { + cpy := *v + return &cpy +} + +// Const is a modifier. +type Const struct { + TypeID + Type Type +} + +func (c *Const) walk(cs *copyStack) { cs.push(&c.Type) } +func (c *Const) copy() Type { + cpy := *c + return &cpy +} + +// Restrict is a modifier. +type Restrict struct { + TypeID + Type Type +} + +func (r *Restrict) walk(cs *copyStack) { cs.push(&r.Type) } +func (r *Restrict) copy() Type { + cpy := *r + return &cpy +} + +// Func is a function definition. +type Func struct { + TypeID + Name + Type Type +} + +func (f *Func) walk(cs *copyStack) { cs.push(&f.Type) } +func (f *Func) copy() Type { + cpy := *f + return &cpy +} + +// FuncProto is a function declaration. +type FuncProto struct { + TypeID + Return Type + // Parameters not supported yet +} + +func (fp *FuncProto) walk(cs *copyStack) { cs.push(&fp.Return) } +func (fp *FuncProto) copy() Type { + cpy := *fp + return &cpy +} + +// Var is a global variable. +type Var struct { + TypeID + Name + Type Type +} + +func (v *Var) walk(cs *copyStack) { cs.push(&v.Type) } +func (v *Var) copy() Type { + cpy := *v + return &cpy +} + +// Datasec is a global program section containing data. +type Datasec struct { + TypeID + Name + Size uint32 + Vars []VarSecinfo +} + +func (ds *Datasec) size() uint32 { return ds.Size } + +func (ds *Datasec) walk(cs *copyStack) { + for i := range ds.Vars { + cs.push(&ds.Vars[i].Type) + } +} + +func (ds *Datasec) copy() Type { + cpy := *ds + cpy.Vars = make([]VarSecinfo, len(ds.Vars)) + copy(cpy.Vars, ds.Vars) + return &cpy +} + +// VarSecinfo describes variable in a Datasec +type VarSecinfo struct { + Type Type + Offset uint32 + Size uint32 +} + +type sizer interface { + size() uint32 +} + +var ( + _ sizer = (*Int)(nil) + _ sizer = (*Pointer)(nil) + _ sizer = (*Struct)(nil) + _ sizer = (*Union)(nil) + _ sizer = (*Enum)(nil) + _ sizer = (*Datasec)(nil) +) + +// Sizeof returns the size of a type in bytes. +// +// Returns an error if the size can't be computed. +func Sizeof(typ Type) (int, error) { + var ( + n = int64(1) + elem int64 + ) + + for i := 0; i < maxTypeDepth; i++ { + switch v := typ.(type) { + case *Array: + if n > 0 && int64(v.Nelems) > math.MaxInt64/n { + return 0, xerrors.New("overflow") + } + + // Arrays may be of zero length, which allows + // n to be zero as well. + n *= int64(v.Nelems) + typ = v.Type + continue + + case sizer: + elem = int64(v.size()) + + case *Typedef: + typ = v.Type + continue + case *Volatile: + typ = v.Type + continue + case *Const: + typ = v.Type + continue + case *Restrict: + typ = v.Type + continue + + default: + return 0, xerrors.Errorf("unrecognized type %T", typ) + } + + if n > 0 && elem > math.MaxInt64/n { + return 0, xerrors.New("overflow") + } + + size := n * elem + if int64(int(size)) != size { + return 0, xerrors.New("overflow") + } + + return int(size), nil + } + + return 0, xerrors.New("exceeded type depth") +} + +// copy a Type recursively. +// +// typ may form a cycle. +func copyType(typ Type) Type { + var ( + copies = make(map[Type]Type) + work copyStack + ) + + for t := &typ; t != nil; t = work.pop() { + // *t is the identity of the type. + if cpy := copies[*t]; cpy != nil { + *t = cpy + continue + } + + cpy := (*t).copy() + copies[*t] = cpy + *t = cpy + + // Mark any nested types for copying. + cpy.walk(&work) + } + + return typ +} + +// copyStack keeps track of pointers to types which still +// need to be copied. +type copyStack []*Type + +// push adds a type to the stack. +func (cs *copyStack) push(t *Type) { + *cs = append(*cs, t) +} + +// pop returns the topmost Type, or nil. +func (cs *copyStack) pop() *Type { + n := len(*cs) + if n == 0 { + return nil + } + + t := (*cs)[n-1] + *cs = (*cs)[:n-1] + return t +} + +type namer interface { + name() string +} + +var _ namer = Name("") + +// inflateRawTypes takes a list of raw btf types linked via type IDs, and turns +// it into a graph of Types connected via pointers. +// +// Returns a map of named types (so, where NameOff is non-zero). Since BTF ignores +// compilation units, multiple types may share the same name. A Type may form a +// cyclic graph by pointing at itself. +func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map[string][]Type, err error) { + type fixupDef struct { + id TypeID + expectedKind btfKind + typ *Type + } + + var fixups []fixupDef + fixup := func(id TypeID, expectedKind btfKind, typ *Type) { + fixups = append(fixups, fixupDef{id, expectedKind, typ}) + } + + convertMembers := func(raw []btfMember) ([]Member, error) { + // NB: The fixup below relies on pre-allocating this array to + // work, since otherwise append might re-allocate members. + members := make([]Member, 0, len(raw)) + for i, btfMember := range raw { + name, err := rawStrings.LookupName(btfMember.NameOff) + if err != nil { + return nil, xerrors.Errorf("can't get name for member %d: %w", i, err) + } + members = append(members, Member{ + Name: name, + Offset: btfMember.Offset, + }) + } + for i := range members { + fixup(raw[i].Type, kindUnknown, &members[i].Type) + } + return members, nil + } + + types := make([]Type, 0, len(rawTypes)) + types = append(types, Void{}) + namedTypes = make(map[string][]Type) + + for i, raw := range rawTypes { + var ( + // Void is defined to always be type ID 0, and is thus + // omitted from BTF. + id = TypeID(i + 1) + typ Type + ) + + name, err := rawStrings.LookupName(raw.NameOff) + if err != nil { + return nil, xerrors.Errorf("can't get name for type id %d: %w", id, err) + } + + switch raw.Kind() { + case kindInt: + typ = &Int{id, name, raw.Size()} + + case kindPointer: + ptr := &Pointer{id, nil} + fixup(raw.Type(), kindUnknown, &ptr.Target) + typ = ptr + + case kindArray: + btfArr := raw.data.(*btfArray) + + // IndexType is unused according to btf.rst. + // Don't make it available right now. + arr := &Array{id, nil, btfArr.Nelems} + fixup(btfArr.Type, kindUnknown, &arr.Type) + typ = arr + + case kindStruct: + members, err := convertMembers(raw.data.([]btfMember)) + if err != nil { + return nil, xerrors.Errorf("struct %s (id %d): %w", name, id, err) + } + typ = &Struct{id, name, raw.Size(), members} + + case kindUnion: + members, err := convertMembers(raw.data.([]btfMember)) + if err != nil { + return nil, xerrors.Errorf("union %s (id %d): %w", name, id, err) + } + typ = &Union{id, name, raw.Size(), members} + + case kindEnum: + typ = &Enum{id, name} + + case kindForward: + typ = &Fwd{id, name} + + case kindTypedef: + typedef := &Typedef{id, name, nil} + fixup(raw.Type(), kindUnknown, &typedef.Type) + typ = typedef + + case kindVolatile: + volatile := &Volatile{id, nil} + fixup(raw.Type(), kindUnknown, &volatile.Type) + typ = volatile + + case kindConst: + cnst := &Const{id, nil} + fixup(raw.Type(), kindUnknown, &cnst.Type) + typ = cnst + + case kindRestrict: + restrict := &Restrict{id, nil} + fixup(raw.Type(), kindUnknown, &restrict.Type) + typ = restrict + + case kindFunc: + fn := &Func{id, name, nil} + fixup(raw.Type(), kindFuncProto, &fn.Type) + typ = fn + + case kindFuncProto: + fp := &FuncProto{id, nil} + fixup(raw.Type(), kindUnknown, &fp.Return) + typ = fp + + case kindVar: + v := &Var{id, name, nil} + fixup(raw.Type(), kindUnknown, &v.Type) + typ = v + + case kindDatasec: + btfVars := raw.data.([]btfVarSecinfo) + vars := make([]VarSecinfo, 0, len(btfVars)) + for _, btfVar := range btfVars { + vars = append(vars, VarSecinfo{ + Offset: btfVar.Offset, + Size: btfVar.Size, + }) + } + for i := range vars { + fixup(btfVars[i].Type, kindVar, &vars[i].Type) + } + typ = &Datasec{id, name, raw.SizeType, vars} + + default: + return nil, xerrors.Errorf("type id %d: unknown kind: %v", id, raw.Kind()) + } + + types = append(types, typ) + + if namer, ok := typ.(namer); ok { + if name := namer.name(); name != "" { + namedTypes[name] = append(namedTypes[name], typ) + } + } + } + + for _, fixup := range fixups { + i := int(fixup.id) + if i >= len(types) { + return nil, xerrors.Errorf("reference to invalid type id: %d", fixup.id) + } + + // Default void (id 0) to unknown + rawKind := kindUnknown + if i > 0 { + rawKind = rawTypes[i-1].Kind() + } + + if expected := fixup.expectedKind; expected != kindUnknown && rawKind != expected { + return nil, xerrors.Errorf("expected type id %d to have kind %s, found %s", fixup.id, expected, rawKind) + } + + *fixup.typ = types[i] + } + + return namedTypes, nil +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/cpu.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/cpu.go new file mode 100644 index 000000000..d3424ba43 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/cpu.go @@ -0,0 +1,62 @@ +package internal + +import ( + "fmt" + "io/ioutil" + "strings" + "sync" +) + +var sysCPU struct { + once sync.Once + err error + num int +} + +// PossibleCPUs returns the max number of CPUs a system may possibly have +// Logical CPU numbers must be of the form 0-n +func PossibleCPUs() (int, error) { + sysCPU.once.Do(func() { + sysCPU.num, sysCPU.err = parseCPUsFromFile("/sys/devices/system/cpu/possible") + }) + + return sysCPU.num, sysCPU.err +} + +func parseCPUsFromFile(path string) (int, error) { + spec, err := ioutil.ReadFile(path) + if err != nil { + return 0, err + } + + n, err := parseCPUs(string(spec)) + if err != nil { + return 0, fmt.Errorf("can't parse %s: %v", path, err) + } + + return n, nil +} + +// parseCPUs parses the number of cpus from a string produced +// by bitmap_list_string() in the Linux kernel. +// Multiple ranges are rejected, since they can't be unified +// into a single number. +// This is the format of /sys/devices/system/cpu/possible, it +// is not suitable for /sys/devices/system/cpu/online, etc. +func parseCPUs(spec string) (int, error) { + if strings.Trim(spec, "\n") == "0" { + return 1, nil + } + + var low, high int + n, err := fmt.Sscanf(spec, "%d-%d\n", &low, &high) + if n != 2 || err != nil { + return 0, fmt.Errorf("invalid format: %s", spec) + } + if low != 0 { + return 0, fmt.Errorf("CPU spec doesn't start at zero: %s", spec) + } + + // cpus is 0 indexed + return high + 1, nil +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/endian.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/endian.go new file mode 100644 index 000000000..ac8a94e51 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/endian.go @@ -0,0 +1,24 @@ +package internal + +import ( + "encoding/binary" + "unsafe" +) + +// NativeEndian is set to either binary.BigEndian or binary.LittleEndian, +// depending on the host's endianness. +var NativeEndian binary.ByteOrder + +func init() { + if isBigEndian() { + NativeEndian = binary.BigEndian + } else { + NativeEndian = binary.LittleEndian + } +} + +func isBigEndian() (ret bool) { + i := int(0x1) + bs := (*[int(unsafe.Sizeof(i))]byte)(unsafe.Pointer(&i)) + return bs[0] == 0 +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/errors.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/errors.go new file mode 100644 index 000000000..72dedb477 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/errors.go @@ -0,0 +1,47 @@ +package internal + +import ( + "bytes" + "fmt" + "strings" + + "github.com/cilium/ebpf/internal/unix" + "golang.org/x/xerrors" +) + +// ErrorWithLog returns an error that includes logs from the +// kernel verifier. +// +// logErr should be the error returned by the syscall that generated +// the log. It is used to check for truncation of the output. +func ErrorWithLog(err error, log []byte, logErr error) error { + logStr := strings.Trim(CString(log), "\t\r\n ") + if xerrors.Is(logErr, unix.ENOSPC) { + logStr += " (truncated...)" + } + + return &VerifierError{err, logStr} +} + +// VerifierError includes information from the eBPF verifier. +type VerifierError struct { + cause error + log string +} + +func (le *VerifierError) Error() string { + if le.log == "" { + return le.cause.Error() + } + + return fmt.Sprintf("%s: %s", le.cause, le.log) +} + +// CString turns a NUL / zero terminated byte buffer into a string. +func CString(in []byte) string { + inLen := bytes.IndexByte(in, 0) + if inLen == -1 { + return "" + } + return string(in[:inLen]) +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/fd.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/fd.go new file mode 100644 index 000000000..f27a1f260 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/fd.go @@ -0,0 +1,63 @@ +package internal + +import ( + "runtime" + "strconv" + + "github.com/cilium/ebpf/internal/unix" + + "golang.org/x/xerrors" +) + +var ErrClosedFd = xerrors.New("use of closed file descriptor") + +type FD struct { + raw int64 +} + +func NewFD(value uint32) *FD { + fd := &FD{int64(value)} + runtime.SetFinalizer(fd, (*FD).Close) + return fd +} + +func (fd *FD) String() string { + return strconv.FormatInt(fd.raw, 10) +} + +func (fd *FD) Value() (uint32, error) { + if fd.raw < 0 { + return 0, ErrClosedFd + } + + return uint32(fd.raw), nil +} + +func (fd *FD) Close() error { + if fd.raw < 0 { + return nil + } + + value := int(fd.raw) + fd.raw = -1 + + fd.Forget() + return unix.Close(value) +} + +func (fd *FD) Forget() { + runtime.SetFinalizer(fd, nil) +} + +func (fd *FD) Dup() (*FD, error) { + if fd.raw < 0 { + return nil, ErrClosedFd + } + + dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0) + if err != nil { + return nil, xerrors.Errorf("can't dup fd: %v", err) + } + + return NewFD(uint32(dup)), nil +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/feature.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/feature.go new file mode 100644 index 000000000..5ecf95477 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/feature.go @@ -0,0 +1,93 @@ +package internal + +import ( + "fmt" + "sync" + + "golang.org/x/xerrors" +) + +// ErrNotSupported indicates that a feature is not supported by the current kernel. +var ErrNotSupported = xerrors.New("not supported") + +// UnsupportedFeatureError is returned by FeatureTest() functions. +type UnsupportedFeatureError struct { + // The minimum Linux mainline version required for this feature. + // Used for the error string, and for sanity checking during testing. + MinimumVersion Version + + // The name of the feature that isn't supported. + Name string +} + +func (ufe *UnsupportedFeatureError) Error() string { + return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion) +} + +// Is indicates that UnsupportedFeatureError is ErrNotSupported. +func (ufe *UnsupportedFeatureError) Is(target error) bool { + return target == ErrNotSupported +} + +// FeatureTest wraps a function so that it is run at most once. +// +// name should identify the tested feature, while version must be in the +// form Major.Minor[.Patch]. +// +// Returns a descriptive UnsupportedFeatureError if the feature is not available. +func FeatureTest(name, version string, fn func() bool) func() error { + v, err := NewVersion(version) + if err != nil { + return func() error { return err } + } + + var ( + once sync.Once + result error + ) + + return func() error { + once.Do(func() { + if !fn() { + result = &UnsupportedFeatureError{ + MinimumVersion: v, + Name: name, + } + } + }) + return result + } +} + +// A Version in the form Major.Minor.Patch. +type Version [3]uint16 + +// NewVersion creates a version from a string like "Major.Minor.Patch". +// +// Patch is optional. +func NewVersion(ver string) (Version, error) { + var major, minor, patch uint16 + n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch) + if n < 2 { + return Version{}, xerrors.Errorf("invalid version: %s", ver) + } + return Version{major, minor, patch}, nil +} + +func (v Version) String() string { + if v[2] == 0 { + return fmt.Sprintf("v%d.%d", v[0], v[1]) + } + return fmt.Sprintf("v%d.%d.%d", v[0], v[1], v[2]) +} + +// Less returns true if the version is less than another version. +func (v Version) Less(other Version) bool { + for i, a := range v { + if a == other[i] { + continue + } + return a < other[i] + } + return false +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/io.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/io.go new file mode 100644 index 000000000..f364f2110 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/io.go @@ -0,0 +1,16 @@ +package internal + +import "golang.org/x/xerrors" + +// DiscardZeroes makes sure that all written bytes are zero +// before discarding them. +type DiscardZeroes struct{} + +func (DiscardZeroes) Write(p []byte) (int, error) { + for _, b := range p { + if b != 0 { + return 0, xerrors.New("encountered non-zero byte") + } + } + return len(p), nil +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr.go new file mode 100644 index 000000000..a7f12b2db --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr.go @@ -0,0 +1,30 @@ +package internal + +import "unsafe" + +// NewPointer creates a 64-bit pointer from an unsafe Pointer. +func NewPointer(ptr unsafe.Pointer) Pointer { + return Pointer{ptr: ptr} +} + +// NewSlicePointer creates a 64-bit pointer from a byte slice. +func NewSlicePointer(buf []byte) Pointer { + if len(buf) == 0 { + return Pointer{} + } + + return Pointer{ptr: unsafe.Pointer(&buf[0])} +} + +// NewStringPointer creates a 64-bit pointer from a string. +func NewStringPointer(str string) Pointer { + if str == "" { + return Pointer{} + } + + // The kernel expects strings to be zero terminated + buf := make([]byte, len(str)+1) + copy(buf, str) + + return Pointer{ptr: unsafe.Pointer(&buf[0])} +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go new file mode 100644 index 000000000..a56fbcc8e --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go @@ -0,0 +1,14 @@ +// +build armbe mips mips64p32 + +package internal + +import ( + "unsafe" +) + +// Pointer wraps an unsafe.Pointer to be 64bit to +// conform to the syscall specification. +type Pointer struct { + pad uint32 + ptr unsafe.Pointer +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go new file mode 100644 index 000000000..be2ecfca7 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go @@ -0,0 +1,14 @@ +// +build 386 amd64p32 arm mipsle mips64p32le + +package internal + +import ( + "unsafe" +) + +// Pointer wraps an unsafe.Pointer to be 64bit to +// conform to the syscall specification. +type Pointer struct { + ptr unsafe.Pointer + pad uint32 +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr_64.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr_64.go new file mode 100644 index 000000000..69452dceb --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/ptr_64.go @@ -0,0 +1,14 @@ +// +build !386,!amd64p32,!arm,!mipsle,!mips64p32le +// +build !armbe,!mips,!mips64p32 + +package internal + +import ( + "unsafe" +) + +// Pointer wraps an unsafe.Pointer to be 64bit to +// conform to the syscall specification. +type Pointer struct { + ptr unsafe.Pointer +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/syscall.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/syscall.go new file mode 100644 index 000000000..b32cf3bce --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/syscall.go @@ -0,0 +1,23 @@ +package internal + +import ( + "runtime" + "unsafe" + + "github.com/cilium/ebpf/internal/unix" +) + +// BPF wraps SYS_BPF. +// +// Any pointers contained in attr must use the Pointer type from this package. +func BPF(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) { + r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size) + runtime.KeepAlive(attr) + + var err error + if errNo != 0 { + err = errNo + } + + return r1, err +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go new file mode 100644 index 000000000..7efbbd725 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go @@ -0,0 +1,147 @@ +// +build linux + +package unix + +import ( + "syscall" + + linux "golang.org/x/sys/unix" +) + +const ( + ENOENT = linux.ENOENT + EAGAIN = linux.EAGAIN + ENOSPC = linux.ENOSPC + EINVAL = linux.EINVAL + EPOLLIN = linux.EPOLLIN + EINTR = linux.EINTR + ESRCH = linux.ESRCH + ENODEV = linux.ENODEV + BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG + BPF_F_WRONLY_PROG = linux.BPF_F_WRONLY_PROG + BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN + BPF_TAG_SIZE = linux.BPF_TAG_SIZE + SYS_BPF = linux.SYS_BPF + F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC + EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD + EPOLL_CLOEXEC = linux.EPOLL_CLOEXEC + O_CLOEXEC = linux.O_CLOEXEC + O_NONBLOCK = linux.O_NONBLOCK + PROT_READ = linux.PROT_READ + PROT_WRITE = linux.PROT_WRITE + MAP_SHARED = linux.MAP_SHARED + PERF_TYPE_SOFTWARE = linux.PERF_TYPE_SOFTWARE + PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT + PerfBitWatermark = linux.PerfBitWatermark + PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW + PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC + RLIM_INFINITY = linux.RLIM_INFINITY +) + +// Statfs_t is a wrapper +type Statfs_t = linux.Statfs_t + +// Rlimit is a wrapper +type Rlimit = linux.Rlimit + +// Setrlimit is a wrapper +func Setrlimit(resource int, rlim *Rlimit) (err error) { + return linux.Setrlimit(resource, rlim) +} + +// Syscall is a wrapper +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + return linux.Syscall(trap, a1, a2, a3) +} + +// FcntlInt is a wrapper +func FcntlInt(fd uintptr, cmd, arg int) (int, error) { + return linux.FcntlInt(fd, cmd, arg) +} + +// Statfs is a wrapper +func Statfs(path string, buf *Statfs_t) (err error) { + return linux.Statfs(path, buf) +} + +// Close is a wrapper +func Close(fd int) (err error) { + return linux.Close(fd) +} + +// EpollEvent is a wrapper +type EpollEvent = linux.EpollEvent + +// EpollWait is a wrapper +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + return linux.EpollWait(epfd, events, msec) +} + +// EpollCtl is a wrapper +func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { + return linux.EpollCtl(epfd, op, fd, event) +} + +// Eventfd is a wrapper +func Eventfd(initval uint, flags int) (fd int, err error) { + return linux.Eventfd(initval, flags) +} + +// Write is a wrapper +func Write(fd int, p []byte) (n int, err error) { + return linux.Write(fd, p) +} + +// EpollCreate1 is a wrapper +func EpollCreate1(flag int) (fd int, err error) { + return linux.EpollCreate1(flag) +} + +// PerfEventMmapPage is a wrapper +type PerfEventMmapPage linux.PerfEventMmapPage + +// SetNonblock is a wrapper +func SetNonblock(fd int, nonblocking bool) (err error) { + return linux.SetNonblock(fd, nonblocking) +} + +// Mmap is a wrapper +func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { + return linux.Mmap(fd, offset, length, prot, flags) +} + +// Munmap is a wrapper +func Munmap(b []byte) (err error) { + return linux.Munmap(b) +} + +// PerfEventAttr is a wrapper +type PerfEventAttr = linux.PerfEventAttr + +// PerfEventOpen is a wrapper +func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { + return linux.PerfEventOpen(attr, pid, cpu, groupFd, flags) +} + +// Utsname is a wrapper +type Utsname = linux.Utsname + +// Uname is a wrapper +func Uname(buf *Utsname) (err error) { + return linux.Uname(buf) +} + +// Getpid is a wrapper +func Getpid() int { + return linux.Getpid() +} + +// Gettid is a wrapper +func Gettid() int { + return linux.Gettid() +} + +// Tgkill is a wrapper +func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) { + return linux.Tgkill(tgid, tid, sig) +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/internal/unix/types_other.go b/src/runtime/vendor/github.com/cilium/ebpf/internal/unix/types_other.go new file mode 100644 index 000000000..7961b356a --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/internal/unix/types_other.go @@ -0,0 +1,214 @@ +// +build !linux + +package unix + +import ( + "fmt" + "runtime" + "syscall" +) + +var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH) + +const ( + ENOENT = syscall.ENOENT + EAGAIN = syscall.EAGAIN + ENOSPC = syscall.ENOSPC + EINVAL = syscall.EINVAL + EINTR = syscall.EINTR + ESRCH = syscall.ESRCH + ENODEV = syscall.ENODEV + BPF_F_RDONLY_PROG = 0 + BPF_F_WRONLY_PROG = 0 + BPF_OBJ_NAME_LEN = 0x10 + BPF_TAG_SIZE = 0x8 + SYS_BPF = 321 + F_DUPFD_CLOEXEC = 0x406 + EPOLLIN = 0x1 + EPOLL_CTL_ADD = 0x1 + EPOLL_CLOEXEC = 0x80000 + O_CLOEXEC = 0x80000 + O_NONBLOCK = 0x800 + PROT_READ = 0x1 + PROT_WRITE = 0x2 + MAP_SHARED = 0x1 + PERF_TYPE_SOFTWARE = 0x1 + PERF_COUNT_SW_BPF_OUTPUT = 0xa + PerfBitWatermark = 0x4000 + PERF_SAMPLE_RAW = 0x400 + PERF_FLAG_FD_CLOEXEC = 0x8 + RLIM_INFINITY = 0x7fffffffffffffff +) + +// Statfs_t is a wrapper +type Statfs_t struct { + Type int64 + Bsize int64 + Blocks uint64 + Bfree uint64 + Bavail uint64 + Files uint64 + Ffree uint64 + Fsid [2]int32 + Namelen int64 + Frsize int64 + Flags int64 + Spare [4]int64 +} + +// Rlimit is a wrapper +type Rlimit struct { + Cur uint64 + Max uint64 +} + +// Setrlimit is a wrapper +func Setrlimit(resource int, rlim *Rlimit) (err error) { + return errNonLinux +} + +// Syscall is a wrapper +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + return 0, 0, syscall.Errno(1) +} + +// FcntlInt is a wrapper +func FcntlInt(fd uintptr, cmd, arg int) (int, error) { + return -1, errNonLinux +} + +// Statfs is a wrapper +func Statfs(path string, buf *Statfs_t) error { + return errNonLinux +} + +// Close is a wrapper +func Close(fd int) (err error) { + return errNonLinux +} + +// EpollEvent is a wrapper +type EpollEvent struct { + Events uint32 + Fd int32 + Pad int32 +} + +// EpollWait is a wrapper +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + return 0, errNonLinux +} + +// EpollCtl is a wrapper +func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { + return errNonLinux +} + +// Eventfd is a wrapper +func Eventfd(initval uint, flags int) (fd int, err error) { + return 0, errNonLinux +} + +// Write is a wrapper +func Write(fd int, p []byte) (n int, err error) { + return 0, errNonLinux +} + +// EpollCreate1 is a wrapper +func EpollCreate1(flag int) (fd int, err error) { + return 0, errNonLinux +} + +// PerfEventMmapPage is a wrapper +type PerfEventMmapPage struct { + Version uint32 + Compat_version uint32 + Lock uint32 + Index uint32 + Offset int64 + Time_enabled uint64 + Time_running uint64 + Capabilities uint64 + Pmc_width uint16 + Time_shift uint16 + Time_mult uint32 + Time_offset uint64 + Time_zero uint64 + Size uint32 + + Data_head uint64 + Data_tail uint64 + Data_offset uint64 + Data_size uint64 + Aux_head uint64 + Aux_tail uint64 + Aux_offset uint64 + Aux_size uint64 +} + +// SetNonblock is a wrapper +func SetNonblock(fd int, nonblocking bool) (err error) { + return errNonLinux +} + +// Mmap is a wrapper +func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { + return []byte{}, errNonLinux +} + +// Munmap is a wrapper +func Munmap(b []byte) (err error) { + return errNonLinux +} + +// PerfEventAttr is a wrapper +type PerfEventAttr struct { + Type uint32 + Size uint32 + Config uint64 + Sample uint64 + Sample_type uint64 + Read_format uint64 + Bits uint64 + Wakeup uint32 + Bp_type uint32 + Ext1 uint64 + Ext2 uint64 + Branch_sample_type uint64 + Sample_regs_user uint64 + Sample_stack_user uint32 + Clockid int32 + Sample_regs_intr uint64 + Aux_watermark uint32 + Sample_max_stack uint16 +} + +// PerfEventOpen is a wrapper +func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { + return 0, errNonLinux +} + +// Utsname is a wrapper +type Utsname struct { + Release [65]byte +} + +// Uname is a wrapper +func Uname(buf *Utsname) (err error) { + return errNonLinux +} + +// Getpid is a wrapper +func Getpid() int { + return -1 +} + +// Gettid is a wrapper +func Gettid() int { + return -1 +} + +// Tgkill is a wrapper +func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) { + return errNonLinux +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/linker.go b/src/runtime/vendor/github.com/cilium/ebpf/linker.go new file mode 100644 index 000000000..b9a54daec --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/linker.go @@ -0,0 +1,86 @@ +package ebpf + +import ( + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal/btf" + + "golang.org/x/xerrors" +) + +// link resolves bpf-to-bpf calls. +// +// Each library may contain multiple functions / labels, and is only linked +// if prog references one of these functions. +// +// Libraries also linked. +func link(prog *ProgramSpec, libs []*ProgramSpec) error { + var ( + linked = make(map[*ProgramSpec]bool) + pending = []asm.Instructions{prog.Instructions} + insns asm.Instructions + ) + for len(pending) > 0 { + insns, pending = pending[0], pending[1:] + for _, lib := range libs { + if linked[lib] { + continue + } + + needed, err := needSection(insns, lib.Instructions) + if err != nil { + return xerrors.Errorf("linking %s: %w", lib.Name, err) + } + + if !needed { + continue + } + + linked[lib] = true + prog.Instructions = append(prog.Instructions, lib.Instructions...) + pending = append(pending, lib.Instructions) + + if prog.BTF != nil && lib.BTF != nil { + if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil { + return xerrors.Errorf("linking BTF of %s: %w", lib.Name, err) + } + } + } + } + + return nil +} + +func needSection(insns, section asm.Instructions) (bool, error) { + // A map of symbols to the libraries which contain them. + symbols, err := section.SymbolOffsets() + if err != nil { + return false, err + } + + for _, ins := range insns { + if ins.Reference == "" { + continue + } + + if ins.OpCode.JumpOp() != asm.Call || ins.Src != asm.PseudoCall { + continue + } + + if ins.Constant != -1 { + // This is already a valid call, no need to link again. + continue + } + + if _, ok := symbols[ins.Reference]; !ok { + // Symbol isn't available in this section + continue + } + + // At this point we know that at least one function in the + // library is called from insns, so we have to link it. + return true, nil + } + + // None of the functions in the section are called. + return false, nil +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/map.go b/src/runtime/vendor/github.com/cilium/ebpf/map.go new file mode 100644 index 000000000..ddb80542b --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/map.go @@ -0,0 +1,794 @@ +package ebpf + +import ( + "fmt" + "strings" + + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" + "github.com/cilium/ebpf/internal/unix" + + "golang.org/x/xerrors" +) + +// Errors returned by Map and MapIterator methods. +var ( + ErrKeyNotExist = xerrors.New("key does not exist") + ErrIterationAborted = xerrors.New("iteration aborted") +) + +// MapID represents the unique ID of an eBPF map +type MapID uint32 + +// MapSpec defines a Map. +type MapSpec struct { + // Name is passed to the kernel as a debug aid. Must only contain + // alpha numeric and '_' characters. + Name string + Type MapType + KeySize uint32 + ValueSize uint32 + MaxEntries uint32 + Flags uint32 + + // The initial contents of the map. May be nil. + Contents []MapKV + + // Whether to freeze a map after setting its initial contents. + Freeze bool + + // InnerMap is used as a template for ArrayOfMaps and HashOfMaps + InnerMap *MapSpec + + // The BTF associated with this map. + BTF *btf.Map +} + +func (ms *MapSpec) String() string { + return fmt.Sprintf("%s(keySize=%d, valueSize=%d, maxEntries=%d, flags=%d)", ms.Type, ms.KeySize, ms.ValueSize, ms.MaxEntries, ms.Flags) +} + +// Copy returns a copy of the spec. +// +// MapSpec.Contents is a shallow copy. +func (ms *MapSpec) Copy() *MapSpec { + if ms == nil { + return nil + } + + cpy := *ms + cpy.Contents = make([]MapKV, len(ms.Contents)) + copy(cpy.Contents, ms.Contents) + cpy.InnerMap = ms.InnerMap.Copy() + return &cpy +} + +// MapKV is used to initialize the contents of a Map. +type MapKV struct { + Key interface{} + Value interface{} +} + +// Map represents a Map file descriptor. +// +// It is not safe to close a map which is used by other goroutines. +// +// Methods which take interface{} arguments by default encode +// them using binary.Read/Write in the machine's native endianness. +// +// Implement encoding.BinaryMarshaler or encoding.BinaryUnmarshaler +// if you require custom encoding. +type Map struct { + name string + fd *internal.FD + abi MapABI + // Per CPU maps return values larger than the size in the spec + fullValueSize int +} + +// NewMapFromFD creates a map from a raw fd. +// +// You should not use fd after calling this function. +func NewMapFromFD(fd int) (*Map, error) { + if fd < 0 { + return nil, xerrors.New("invalid fd") + } + bpfFd := internal.NewFD(uint32(fd)) + + name, abi, err := newMapABIFromFd(bpfFd) + if err != nil { + bpfFd.Forget() + return nil, err + } + return newMap(bpfFd, name, abi) +} + +// NewMap creates a new Map. +// +// Creating a map for the first time will perform feature detection +// by creating small, temporary maps. +func NewMap(spec *MapSpec) (*Map, error) { + if spec.BTF == nil { + return newMapWithBTF(spec, nil) + } + + handle, err := btf.NewHandle(btf.MapSpec(spec.BTF)) + if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { + return nil, xerrors.Errorf("can't load BTF: %w", err) + } + + return newMapWithBTF(spec, handle) +} + +func newMapWithBTF(spec *MapSpec, handle *btf.Handle) (*Map, error) { + if spec.Type != ArrayOfMaps && spec.Type != HashOfMaps { + return createMap(spec, nil, handle) + } + + if spec.InnerMap == nil { + return nil, xerrors.Errorf("%s requires InnerMap", spec.Type) + } + + template, err := createMap(spec.InnerMap, nil, handle) + if err != nil { + return nil, err + } + defer template.Close() + + return createMap(spec, template.fd, handle) +} + +func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, error) { + abi := newMapABIFromSpec(spec) + + switch spec.Type { + case ArrayOfMaps: + fallthrough + case HashOfMaps: + if err := haveNestedMaps(); err != nil { + return nil, err + } + + if abi.ValueSize != 0 && abi.ValueSize != 4 { + return nil, xerrors.New("ValueSize must be zero or four for map of map") + } + abi.ValueSize = 4 + + case PerfEventArray: + if abi.KeySize != 0 && abi.KeySize != 4 { + return nil, xerrors.New("KeySize must be zero or four for perf event array") + } + abi.KeySize = 4 + + if abi.ValueSize != 0 && abi.ValueSize != 4 { + return nil, xerrors.New("ValueSize must be zero or four for perf event array") + } + abi.ValueSize = 4 + + if abi.MaxEntries == 0 { + n, err := internal.PossibleCPUs() + if err != nil { + return nil, xerrors.Errorf("perf event array: %w", err) + } + abi.MaxEntries = uint32(n) + } + } + + if abi.Flags&(unix.BPF_F_RDONLY_PROG|unix.BPF_F_WRONLY_PROG) > 0 || spec.Freeze { + if err := haveMapMutabilityModifiers(); err != nil { + return nil, xerrors.Errorf("map create: %w", err) + } + } + + attr := bpfMapCreateAttr{ + mapType: abi.Type, + keySize: abi.KeySize, + valueSize: abi.ValueSize, + maxEntries: abi.MaxEntries, + flags: abi.Flags, + } + + if inner != nil { + var err error + attr.innerMapFd, err = inner.Value() + if err != nil { + return nil, xerrors.Errorf("map create: %w", err) + } + } + + if handle != nil && spec.BTF != nil { + attr.btfFd = uint32(handle.FD()) + attr.btfKeyTypeID = btf.MapKey(spec.BTF).ID() + attr.btfValueTypeID = btf.MapValue(spec.BTF).ID() + } + + if haveObjName() == nil { + attr.mapName = newBPFObjName(spec.Name) + } + + fd, err := bpfMapCreate(&attr) + if err != nil { + return nil, xerrors.Errorf("map create: %w", err) + } + + m, err := newMap(fd, spec.Name, abi) + if err != nil { + return nil, err + } + + if err := m.populate(spec.Contents); err != nil { + m.Close() + return nil, xerrors.Errorf("map create: can't set initial contents: %w", err) + } + + if spec.Freeze { + if err := m.Freeze(); err != nil { + m.Close() + return nil, xerrors.Errorf("can't freeze map: %w", err) + } + } + + return m, nil +} + +func newMap(fd *internal.FD, name string, abi *MapABI) (*Map, error) { + m := &Map{ + name, + fd, + *abi, + int(abi.ValueSize), + } + + if !abi.Type.hasPerCPUValue() { + return m, nil + } + + possibleCPUs, err := internal.PossibleCPUs() + if err != nil { + return nil, err + } + + m.fullValueSize = align(int(abi.ValueSize), 8) * possibleCPUs + return m, nil +} + +func (m *Map) String() string { + if m.name != "" { + return fmt.Sprintf("%s(%s)#%v", m.abi.Type, m.name, m.fd) + } + return fmt.Sprintf("%s#%v", m.abi.Type, m.fd) +} + +// ABI gets the ABI of the Map +func (m *Map) ABI() MapABI { + return m.abi +} + +// Lookup retrieves a value from a Map. +// +// Calls Close() on valueOut if it is of type **Map or **Program, +// and *valueOut is not nil. +// +// Returns an error if the key doesn't exist, see IsNotExist. +func (m *Map) Lookup(key, valueOut interface{}) error { + valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize) + + if err := m.lookup(key, valuePtr); err != nil { + return err + } + + if valueBytes == nil { + return nil + } + + if m.abi.Type.hasPerCPUValue() { + return unmarshalPerCPUValue(valueOut, int(m.abi.ValueSize), valueBytes) + } + + switch value := valueOut.(type) { + case **Map: + m, err := unmarshalMap(valueBytes) + if err != nil { + return err + } + + (*value).Close() + *value = m + return nil + case *Map: + return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil)) + case Map: + return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil)) + + case **Program: + p, err := unmarshalProgram(valueBytes) + if err != nil { + return err + } + + (*value).Close() + *value = p + return nil + case *Program: + return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil)) + case Program: + return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil)) + + default: + return unmarshalBytes(valueOut, valueBytes) + } +} + +// LookupAndDelete retrieves and deletes a value from a Map. +// +// Returns ErrKeyNotExist if the key doesn't exist. +func (m *Map) LookupAndDelete(key, valueOut interface{}) error { + valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize) + + keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) + if err != nil { + return xerrors.Errorf("can't marshal key: %w", err) + } + + if err := bpfMapLookupAndDelete(m.fd, keyPtr, valuePtr); err != nil { + return xerrors.Errorf("lookup and delete failed: %w", err) + } + + return unmarshalBytes(valueOut, valueBytes) +} + +// LookupBytes gets a value from Map. +// +// Returns a nil value if a key doesn't exist. +func (m *Map) LookupBytes(key interface{}) ([]byte, error) { + valueBytes := make([]byte, m.fullValueSize) + valuePtr := internal.NewSlicePointer(valueBytes) + + err := m.lookup(key, valuePtr) + if xerrors.Is(err, ErrKeyNotExist) { + return nil, nil + } + + return valueBytes, err +} + +func (m *Map) lookup(key interface{}, valueOut internal.Pointer) error { + keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) + if err != nil { + return xerrors.Errorf("can't marshal key: %w", err) + } + + if err = bpfMapLookupElem(m.fd, keyPtr, valueOut); err != nil { + return xerrors.Errorf("lookup failed: %w", err) + } + return nil +} + +// MapUpdateFlags controls the behaviour of the Map.Update call. +// +// The exact semantics depend on the specific MapType. +type MapUpdateFlags uint64 + +const ( + // UpdateAny creates a new element or update an existing one. + UpdateAny MapUpdateFlags = iota + // UpdateNoExist creates a new element. + UpdateNoExist MapUpdateFlags = 1 << (iota - 1) + // UpdateExist updates an existing element. + UpdateExist +) + +// Put replaces or creates a value in map. +// +// It is equivalent to calling Update with UpdateAny. +func (m *Map) Put(key, value interface{}) error { + return m.Update(key, value, UpdateAny) +} + +// Update changes the value of a key. +func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error { + keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) + if err != nil { + return xerrors.Errorf("can't marshal key: %w", err) + } + + var valuePtr internal.Pointer + if m.abi.Type.hasPerCPUValue() { + valuePtr, err = marshalPerCPUValue(value, int(m.abi.ValueSize)) + } else { + valuePtr, err = marshalPtr(value, int(m.abi.ValueSize)) + } + if err != nil { + return xerrors.Errorf("can't marshal value: %w", err) + } + + if err = bpfMapUpdateElem(m.fd, keyPtr, valuePtr, uint64(flags)); err != nil { + return xerrors.Errorf("update failed: %w", err) + } + + return nil +} + +// Delete removes a value. +// +// Returns ErrKeyNotExist if the key does not exist. +func (m *Map) Delete(key interface{}) error { + keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) + if err != nil { + return xerrors.Errorf("can't marshal key: %w", err) + } + + if err = bpfMapDeleteElem(m.fd, keyPtr); err != nil { + return xerrors.Errorf("delete failed: %w", err) + } + return nil +} + +// NextKey finds the key following an initial key. +// +// See NextKeyBytes for details. +// +// Returns ErrKeyNotExist if there is no next key. +func (m *Map) NextKey(key, nextKeyOut interface{}) error { + nextKeyPtr, nextKeyBytes := makeBuffer(nextKeyOut, int(m.abi.KeySize)) + + if err := m.nextKey(key, nextKeyPtr); err != nil { + return err + } + + if nextKeyBytes == nil { + return nil + } + + if err := unmarshalBytes(nextKeyOut, nextKeyBytes); err != nil { + return xerrors.Errorf("can't unmarshal next key: %w", err) + } + return nil +} + +// NextKeyBytes returns the key following an initial key as a byte slice. +// +// Passing nil will return the first key. +// +// Use Iterate if you want to traverse all entries in the map. +// +// Returns nil if there are no more keys. +func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) { + nextKey := make([]byte, m.abi.KeySize) + nextKeyPtr := internal.NewSlicePointer(nextKey) + + err := m.nextKey(key, nextKeyPtr) + if xerrors.Is(err, ErrKeyNotExist) { + return nil, nil + } + + return nextKey, err +} + +func (m *Map) nextKey(key interface{}, nextKeyOut internal.Pointer) error { + var ( + keyPtr internal.Pointer + err error + ) + + if key != nil { + keyPtr, err = marshalPtr(key, int(m.abi.KeySize)) + if err != nil { + return xerrors.Errorf("can't marshal key: %w", err) + } + } + + if err = bpfMapGetNextKey(m.fd, keyPtr, nextKeyOut); err != nil { + return xerrors.Errorf("next key failed: %w", err) + } + return nil +} + +// Iterate traverses a map. +// +// It's safe to create multiple iterators at the same time. +// +// It's not possible to guarantee that all keys in a map will be +// returned if there are concurrent modifications to the map. +func (m *Map) Iterate() *MapIterator { + return newMapIterator(m) +} + +// Close removes a Map +func (m *Map) Close() error { + if m == nil { + // This makes it easier to clean up when iterating maps + // of maps / programs. + return nil + } + + return m.fd.Close() +} + +// FD gets the file descriptor of the Map. +// +// Calling this function is invalid after Close has been called. +func (m *Map) FD() int { + fd, err := m.fd.Value() + if err != nil { + // Best effort: -1 is the number most likely to be an + // invalid file descriptor. + return -1 + } + + return int(fd) +} + +// Clone creates a duplicate of the Map. +// +// Closing the duplicate does not affect the original, and vice versa. +// Changes made to the map are reflected by both instances however. +// +// Cloning a nil Map returns nil. +func (m *Map) Clone() (*Map, error) { + if m == nil { + return nil, nil + } + + dup, err := m.fd.Dup() + if err != nil { + return nil, xerrors.Errorf("can't clone map: %w", err) + } + + return newMap(dup, m.name, &m.abi) +} + +// Pin persists the map past the lifetime of the process that created it. +// +// This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional +func (m *Map) Pin(fileName string) error { + return bpfPinObject(fileName, m.fd) +} + +// Freeze prevents a map to be modified from user space. +// +// It makes no changes to kernel-side restrictions. +func (m *Map) Freeze() error { + if err := haveMapMutabilityModifiers(); err != nil { + return xerrors.Errorf("can't freeze map: %w", err) + } + + if err := bpfMapFreeze(m.fd); err != nil { + return xerrors.Errorf("can't freeze map: %w", err) + } + return nil +} + +func (m *Map) populate(contents []MapKV) error { + for _, kv := range contents { + if err := m.Put(kv.Key, kv.Value); err != nil { + return xerrors.Errorf("key %v: %w", kv.Key, err) + } + } + return nil +} + +// LoadPinnedMap load a Map from a BPF file. +// +// The function is not compatible with nested maps. +// Use LoadPinnedMapExplicit in these situations. +func LoadPinnedMap(fileName string) (*Map, error) { + fd, err := bpfGetObject(fileName) + if err != nil { + return nil, err + } + name, abi, err := newMapABIFromFd(fd) + if err != nil { + _ = fd.Close() + return nil, err + } + return newMap(fd, name, abi) +} + +// LoadPinnedMapExplicit loads a map with explicit parameters. +func LoadPinnedMapExplicit(fileName string, abi *MapABI) (*Map, error) { + fd, err := bpfGetObject(fileName) + if err != nil { + return nil, err + } + return newMap(fd, "", abi) +} + +func unmarshalMap(buf []byte) (*Map, error) { + if len(buf) != 4 { + return nil, xerrors.New("map id requires 4 byte value") + } + + // Looking up an entry in a nested map or prog array returns an id, + // not an fd. + id := internal.NativeEndian.Uint32(buf) + return NewMapFromID(MapID(id)) +} + +// MarshalBinary implements BinaryMarshaler. +func (m *Map) MarshalBinary() ([]byte, error) { + fd, err := m.fd.Value() + if err != nil { + return nil, err + } + + buf := make([]byte, 4) + internal.NativeEndian.PutUint32(buf, fd) + return buf, nil +} + +func patchValue(value []byte, typ btf.Type, replacements map[string]interface{}) error { + replaced := make(map[string]bool) + replace := func(name string, offset, size int, replacement interface{}) error { + if offset+size > len(value) { + return xerrors.Errorf("%s: offset %d(+%d) is out of bounds", name, offset, size) + } + + buf, err := marshalBytes(replacement, size) + if err != nil { + return xerrors.Errorf("marshal %s: %w", name, err) + } + + copy(value[offset:offset+size], buf) + replaced[name] = true + return nil + } + + switch parent := typ.(type) { + case *btf.Datasec: + for _, secinfo := range parent.Vars { + name := string(secinfo.Type.(*btf.Var).Name) + replacement, ok := replacements[name] + if !ok { + continue + } + + err := replace(name, int(secinfo.Offset), int(secinfo.Size), replacement) + if err != nil { + return err + } + } + + default: + return xerrors.Errorf("patching %T is not supported", typ) + } + + if len(replaced) == len(replacements) { + return nil + } + + var missing []string + for name := range replacements { + if !replaced[name] { + missing = append(missing, name) + } + } + + if len(missing) == 1 { + return xerrors.Errorf("unknown field: %s", missing[0]) + } + + return xerrors.Errorf("unknown fields: %s", strings.Join(missing, ",")) +} + +// MapIterator iterates a Map. +// +// See Map.Iterate. +type MapIterator struct { + target *Map + prevKey interface{} + prevBytes []byte + count, maxEntries uint32 + done bool + err error +} + +func newMapIterator(target *Map) *MapIterator { + return &MapIterator{ + target: target, + maxEntries: target.abi.MaxEntries, + prevBytes: make([]byte, int(target.abi.KeySize)), + } +} + +// Next decodes the next key and value. +// +// Iterating a hash map from which keys are being deleted is not +// safe. You may see the same key multiple times. Iteration may +// also abort with an error, see IsIterationAborted. +// +// Returns false if there are no more entries. You must check +// the result of Err afterwards. +// +// See Map.Get for further caveats around valueOut. +func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool { + if mi.err != nil || mi.done { + return false + } + + for ; mi.count < mi.maxEntries; mi.count++ { + var nextBytes []byte + nextBytes, mi.err = mi.target.NextKeyBytes(mi.prevKey) + if mi.err != nil { + return false + } + + if nextBytes == nil { + mi.done = true + return false + } + + // The user can get access to nextBytes since unmarshalBytes + // does not copy when unmarshaling into a []byte. + // Make a copy to prevent accidental corruption of + // iterator state. + copy(mi.prevBytes, nextBytes) + mi.prevKey = mi.prevBytes + + mi.err = mi.target.Lookup(nextBytes, valueOut) + if xerrors.Is(mi.err, ErrKeyNotExist) { + // Even though the key should be valid, we couldn't look up + // its value. If we're iterating a hash map this is probably + // because a concurrent delete removed the value before we + // could get it. This means that the next call to NextKeyBytes + // is very likely to restart iteration. + // If we're iterating one of the fd maps like + // ProgramArray it means that a given slot doesn't have + // a valid fd associated. It's OK to continue to the next slot. + continue + } + if mi.err != nil { + return false + } + + mi.err = unmarshalBytes(keyOut, nextBytes) + return mi.err == nil + } + + mi.err = xerrors.Errorf("%w", ErrIterationAborted) + return false +} + +// Err returns any encountered error. +// +// The method must be called after Next returns nil. +// +// Returns ErrIterationAborted if it wasn't possible to do a full iteration. +func (mi *MapIterator) Err() error { + return mi.err +} + +// MapGetNextID returns the ID of the next eBPF map. +// +// Returns ErrNotExist, if there is no next eBPF map. +func MapGetNextID(startID MapID) (MapID, error) { + id, err := objGetNextID(_MapGetNextID, uint32(startID)) + return MapID(id), err +} + +// NewMapFromID returns the map for a given id. +// +// Returns ErrNotExist, if there is no eBPF map with the given id. +func NewMapFromID(id MapID) (*Map, error) { + fd, err := bpfObjGetFDByID(_MapGetFDByID, uint32(id)) + if err != nil { + return nil, err + } + + name, abi, err := newMapABIFromFd(fd) + if err != nil { + _ = fd.Close() + return nil, err + } + + return newMap(fd, name, abi) +} + +// ID returns the systemwide unique ID of the map. +func (m *Map) ID() (MapID, error) { + info, err := bpfGetMapInfoByFD(m.fd) + if err != nil { + return MapID(0), err + } + return MapID(info.id), nil +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/marshalers.go b/src/runtime/vendor/github.com/cilium/ebpf/marshalers.go new file mode 100644 index 000000000..89fdc8602 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/marshalers.go @@ -0,0 +1,203 @@ +package ebpf + +import ( + "bytes" + "encoding" + "encoding/binary" + "reflect" + "runtime" + "unsafe" + + "github.com/cilium/ebpf/internal" + + "golang.org/x/xerrors" +) + +func marshalPtr(data interface{}, length int) (internal.Pointer, error) { + if data == nil { + if length == 0 { + return internal.NewPointer(nil), nil + } + return internal.Pointer{}, xerrors.New("can't use nil as key of map") + } + + if ptr, ok := data.(unsafe.Pointer); ok { + return internal.NewPointer(ptr), nil + } + + buf, err := marshalBytes(data, length) + if err != nil { + return internal.Pointer{}, err + } + + return internal.NewSlicePointer(buf), nil +} + +func marshalBytes(data interface{}, length int) (buf []byte, err error) { + switch value := data.(type) { + case encoding.BinaryMarshaler: + buf, err = value.MarshalBinary() + case string: + buf = []byte(value) + case []byte: + buf = value + case unsafe.Pointer: + err = xerrors.New("can't marshal from unsafe.Pointer") + default: + var wr bytes.Buffer + err = binary.Write(&wr, internal.NativeEndian, value) + if err != nil { + err = xerrors.Errorf("encoding %T: %v", value, err) + } + buf = wr.Bytes() + } + if err != nil { + return nil, err + } + + if len(buf) != length { + return nil, xerrors.Errorf("%T doesn't marshal to %d bytes", data, length) + } + return buf, nil +} + +func makeBuffer(dst interface{}, length int) (internal.Pointer, []byte) { + if ptr, ok := dst.(unsafe.Pointer); ok { + return internal.NewPointer(ptr), nil + } + + buf := make([]byte, length) + return internal.NewSlicePointer(buf), buf +} + +func unmarshalBytes(data interface{}, buf []byte) error { + switch value := data.(type) { + case unsafe.Pointer: + sh := &reflect.SliceHeader{ + Data: uintptr(value), + Len: len(buf), + Cap: len(buf), + } + + dst := *(*[]byte)(unsafe.Pointer(sh)) + copy(dst, buf) + runtime.KeepAlive(value) + return nil + case encoding.BinaryUnmarshaler: + return value.UnmarshalBinary(buf) + case *string: + *value = string(buf) + return nil + case *[]byte: + *value = buf + return nil + case string: + return xerrors.New("require pointer to string") + case []byte: + return xerrors.New("require pointer to []byte") + default: + rd := bytes.NewReader(buf) + if err := binary.Read(rd, internal.NativeEndian, value); err != nil { + return xerrors.Errorf("decoding %T: %v", value, err) + } + return nil + } +} + +// marshalPerCPUValue encodes a slice containing one value per +// possible CPU into a buffer of bytes. +// +// Values are initialized to zero if the slice has less elements than CPUs. +// +// slice must have a type like []elementType. +func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, error) { + sliceType := reflect.TypeOf(slice) + if sliceType.Kind() != reflect.Slice { + return internal.Pointer{}, xerrors.New("per-CPU value requires slice") + } + + possibleCPUs, err := internal.PossibleCPUs() + if err != nil { + return internal.Pointer{}, err + } + + sliceValue := reflect.ValueOf(slice) + sliceLen := sliceValue.Len() + if sliceLen > possibleCPUs { + return internal.Pointer{}, xerrors.Errorf("per-CPU value exceeds number of CPUs") + } + + alignedElemLength := align(elemLength, 8) + buf := make([]byte, alignedElemLength*possibleCPUs) + + for i := 0; i < sliceLen; i++ { + elem := sliceValue.Index(i).Interface() + elemBytes, err := marshalBytes(elem, elemLength) + if err != nil { + return internal.Pointer{}, err + } + + offset := i * alignedElemLength + copy(buf[offset:offset+elemLength], elemBytes) + } + + return internal.NewSlicePointer(buf), nil +} + +// unmarshalPerCPUValue decodes a buffer into a slice containing one value per +// possible CPU. +// +// valueOut must have a type like *[]elementType +func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error { + slicePtrType := reflect.TypeOf(slicePtr) + if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice { + return xerrors.Errorf("per-cpu value requires pointer to slice") + } + + possibleCPUs, err := internal.PossibleCPUs() + if err != nil { + return err + } + + sliceType := slicePtrType.Elem() + slice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs) + + sliceElemType := sliceType.Elem() + sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr + if sliceElemIsPointer { + sliceElemType = sliceElemType.Elem() + } + + step := len(buf) / possibleCPUs + if step < elemLength { + return xerrors.Errorf("per-cpu element length is larger than available data") + } + for i := 0; i < possibleCPUs; i++ { + var elem interface{} + if sliceElemIsPointer { + newElem := reflect.New(sliceElemType) + slice.Index(i).Set(newElem) + elem = newElem.Interface() + } else { + elem = slice.Index(i).Addr().Interface() + } + + // Make a copy, since unmarshal can hold on to itemBytes + elemBytes := make([]byte, elemLength) + copy(elemBytes, buf[:elemLength]) + + err := unmarshalBytes(elem, elemBytes) + if err != nil { + return xerrors.Errorf("cpu %d: %w", i, err) + } + + buf = buf[step:] + } + + reflect.ValueOf(slicePtr).Elem().Set(slice) + return nil +} + +func align(n, alignment int) int { + return (int(n) + alignment - 1) / alignment * alignment +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/prog.go b/src/runtime/vendor/github.com/cilium/ebpf/prog.go new file mode 100644 index 000000000..60860a2bb --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/prog.go @@ -0,0 +1,564 @@ +package ebpf + +import ( + "bytes" + "fmt" + "math" + "strings" + "time" + "unsafe" + + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" + "github.com/cilium/ebpf/internal/unix" + + "golang.org/x/xerrors" +) + +// ErrNotSupported is returned whenever the kernel doesn't support a feature. +var ErrNotSupported = internal.ErrNotSupported + +// ProgramID represents the unique ID of an eBPF program +type ProgramID uint32 + +const ( + // Number of bytes to pad the output buffer for BPF_PROG_TEST_RUN. + // This is currently the maximum of spare space allocated for SKB + // and XDP programs, and equal to XDP_PACKET_HEADROOM + NET_IP_ALIGN. + outputPad = 256 + 2 +) + +// DefaultVerifierLogSize is the default number of bytes allocated for the +// verifier log. +const DefaultVerifierLogSize = 64 * 1024 + +// ProgramOptions control loading a program into the kernel. +type ProgramOptions struct { + // Controls the detail emitted by the kernel verifier. Set to non-zero + // to enable logging. + LogLevel uint32 + // Controls the output buffer size for the verifier. Defaults to + // DefaultVerifierLogSize. + LogSize int +} + +// ProgramSpec defines a Program +type ProgramSpec struct { + // Name is passed to the kernel as a debug aid. Must only contain + // alpha numeric and '_' characters. + Name string + Type ProgramType + AttachType AttachType + Instructions asm.Instructions + License string + KernelVersion uint32 + + // The BTF associated with this program. Changing Instructions + // will most likely invalidate the contained data, and may + // result in errors when attempting to load it into the kernel. + BTF *btf.Program +} + +// Copy returns a copy of the spec. +func (ps *ProgramSpec) Copy() *ProgramSpec { + if ps == nil { + return nil + } + + cpy := *ps + cpy.Instructions = make(asm.Instructions, len(ps.Instructions)) + copy(cpy.Instructions, ps.Instructions) + return &cpy +} + +// Program represents BPF program loaded into the kernel. +// +// It is not safe to close a Program which is used by other goroutines. +type Program struct { + // Contains the output of the kernel verifier if enabled, + // otherwise it is empty. + VerifierLog string + + fd *internal.FD + name string + abi ProgramABI +} + +// NewProgram creates a new Program. +// +// Loading a program for the first time will perform +// feature detection by loading small, temporary programs. +func NewProgram(spec *ProgramSpec) (*Program, error) { + return NewProgramWithOptions(spec, ProgramOptions{}) +} + +// NewProgramWithOptions creates a new Program. +// +// Loading a program for the first time will perform +// feature detection by loading small, temporary programs. +func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) { + if spec.BTF == nil { + return newProgramWithBTF(spec, nil, opts) + } + + handle, err := btf.NewHandle(btf.ProgramSpec(spec.BTF)) + if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { + return nil, xerrors.Errorf("can't load BTF: %w", err) + } + + return newProgramWithBTF(spec, handle, opts) +} + +func newProgramWithBTF(spec *ProgramSpec, btf *btf.Handle, opts ProgramOptions) (*Program, error) { + attr, err := convertProgramSpec(spec, btf) + if err != nil { + return nil, err + } + + logSize := DefaultVerifierLogSize + if opts.LogSize > 0 { + logSize = opts.LogSize + } + + var logBuf []byte + if opts.LogLevel > 0 { + logBuf = make([]byte, logSize) + attr.logLevel = opts.LogLevel + attr.logSize = uint32(len(logBuf)) + attr.logBuf = internal.NewSlicePointer(logBuf) + } + + fd, err := bpfProgLoad(attr) + if err == nil { + prog := newProgram(fd, spec.Name, &ProgramABI{spec.Type}) + prog.VerifierLog = internal.CString(logBuf) + return prog, nil + } + + logErr := err + if opts.LogLevel == 0 { + // Re-run with the verifier enabled to get better error messages. + logBuf = make([]byte, logSize) + attr.logLevel = 1 + attr.logSize = uint32(len(logBuf)) + attr.logBuf = internal.NewSlicePointer(logBuf) + + _, logErr = bpfProgLoad(attr) + } + + err = internal.ErrorWithLog(err, logBuf, logErr) + return nil, xerrors.Errorf("can't load program: %w", err) +} + +// NewProgramFromFD creates a program from a raw fd. +// +// You should not use fd after calling this function. +// +// Requires at least Linux 4.11. +func NewProgramFromFD(fd int) (*Program, error) { + if fd < 0 { + return nil, xerrors.New("invalid fd") + } + bpfFd := internal.NewFD(uint32(fd)) + + name, abi, err := newProgramABIFromFd(bpfFd) + if err != nil { + bpfFd.Forget() + return nil, err + } + + return newProgram(bpfFd, name, abi), nil +} + +func newProgram(fd *internal.FD, name string, abi *ProgramABI) *Program { + return &Program{ + name: name, + fd: fd, + abi: *abi, + } +} + +func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr, error) { + if len(spec.Instructions) == 0 { + return nil, xerrors.New("Instructions cannot be empty") + } + + if len(spec.License) == 0 { + return nil, xerrors.New("License cannot be empty") + } + + buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize)) + err := spec.Instructions.Marshal(buf, internal.NativeEndian) + if err != nil { + return nil, err + } + + bytecode := buf.Bytes() + insCount := uint32(len(bytecode) / asm.InstructionSize) + attr := &bpfProgLoadAttr{ + progType: spec.Type, + expectedAttachType: spec.AttachType, + insCount: insCount, + instructions: internal.NewSlicePointer(bytecode), + license: internal.NewStringPointer(spec.License), + kernelVersion: spec.KernelVersion, + } + + if haveObjName() == nil { + attr.progName = newBPFObjName(spec.Name) + } + + if handle != nil && spec.BTF != nil { + attr.progBTFFd = uint32(handle.FD()) + + recSize, bytes, err := btf.ProgramLineInfos(spec.BTF) + if err != nil { + return nil, xerrors.Errorf("can't get BTF line infos: %w", err) + } + attr.lineInfoRecSize = recSize + attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) + attr.lineInfo = internal.NewSlicePointer(bytes) + + recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF) + if err != nil { + return nil, xerrors.Errorf("can't get BTF function infos: %w", err) + } + attr.funcInfoRecSize = recSize + attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) + attr.funcInfo = internal.NewSlicePointer(bytes) + } + + return attr, nil +} + +func (p *Program) String() string { + if p.name != "" { + return fmt.Sprintf("%s(%s)#%v", p.abi.Type, p.name, p.fd) + } + return fmt.Sprintf("%s#%v", p.abi.Type, p.fd) +} + +// ABI gets the ABI of the Program +func (p *Program) ABI() ProgramABI { + return p.abi +} + +// FD gets the file descriptor of the Program. +// +// It is invalid to call this function after Close has been called. +func (p *Program) FD() int { + fd, err := p.fd.Value() + if err != nil { + // Best effort: -1 is the number most likely to be an + // invalid file descriptor. + return -1 + } + + return int(fd) +} + +// Clone creates a duplicate of the Program. +// +// Closing the duplicate does not affect the original, and vice versa. +// +// Cloning a nil Program returns nil. +func (p *Program) Clone() (*Program, error) { + if p == nil { + return nil, nil + } + + dup, err := p.fd.Dup() + if err != nil { + return nil, xerrors.Errorf("can't clone program: %w", err) + } + + return newProgram(dup, p.name, &p.abi), nil +} + +// Pin persists the Program past the lifetime of the process that created it +// +// This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional +func (p *Program) Pin(fileName string) error { + if err := bpfPinObject(fileName, p.fd); err != nil { + return xerrors.Errorf("can't pin program: %w", err) + } + return nil +} + +// Close unloads the program from the kernel. +func (p *Program) Close() error { + if p == nil { + return nil + } + + return p.fd.Close() +} + +// Test runs the Program in the kernel with the given input and returns the +// value returned by the eBPF program. outLen may be zero. +// +// Note: the kernel expects at least 14 bytes input for an ethernet header for +// XDP and SKB programs. +// +// This function requires at least Linux 4.12. +func (p *Program) Test(in []byte) (uint32, []byte, error) { + ret, out, _, err := p.testRun(in, 1, nil) + if err != nil { + return ret, nil, xerrors.Errorf("can't test program: %w", err) + } + return ret, out, nil +} + +// Benchmark runs the Program with the given input for a number of times +// and returns the time taken per iteration. +// +// Returns the result of the last execution of the program and the time per +// run or an error. reset is called whenever the benchmark syscall is +// interrupted, and should be set to testing.B.ResetTimer or similar. +// +// Note: profiling a call to this function will skew it's results, see +// https://github.com/cilium/ebpf/issues/24 +// +// This function requires at least Linux 4.12. +func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) { + ret, _, total, err := p.testRun(in, repeat, reset) + if err != nil { + return ret, total, xerrors.Errorf("can't benchmark program: %w", err) + } + return ret, total, nil +} + +var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() bool { + prog, err := NewProgram(&ProgramSpec{ + Type: SocketFilter, + Instructions: asm.Instructions{ + asm.LoadImm(asm.R0, 0, asm.DWord), + asm.Return(), + }, + License: "MIT", + }) + if err != nil { + // This may be because we lack sufficient permissions, etc. + return false + } + defer prog.Close() + + fd, err := prog.fd.Value() + if err != nil { + return false + } + + // Programs require at least 14 bytes input + in := make([]byte, 14) + attr := bpfProgTestRunAttr{ + fd: fd, + dataSizeIn: uint32(len(in)), + dataIn: internal.NewSlicePointer(in), + } + + _, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + + // Check for EINVAL specifically, rather than err != nil since we + // otherwise misdetect due to insufficient permissions. + return !xerrors.Is(err, unix.EINVAL) +}) + +func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte, time.Duration, error) { + if uint(repeat) > math.MaxUint32 { + return 0, nil, 0, fmt.Errorf("repeat is too high") + } + + if len(in) == 0 { + return 0, nil, 0, fmt.Errorf("missing input") + } + + if uint(len(in)) > math.MaxUint32 { + return 0, nil, 0, fmt.Errorf("input is too long") + } + + if err := haveProgTestRun(); err != nil { + return 0, nil, 0, err + } + + // Older kernels ignore the dataSizeOut argument when copying to user space. + // Combined with things like bpf_xdp_adjust_head() we don't really know what the final + // size will be. Hence we allocate an output buffer which we hope will always be large + // enough, and panic if the kernel wrote past the end of the allocation. + // See https://patchwork.ozlabs.org/cover/1006822/ + out := make([]byte, len(in)+outputPad) + + fd, err := p.fd.Value() + if err != nil { + return 0, nil, 0, err + } + + attr := bpfProgTestRunAttr{ + fd: fd, + dataSizeIn: uint32(len(in)), + dataSizeOut: uint32(len(out)), + dataIn: internal.NewSlicePointer(in), + dataOut: internal.NewSlicePointer(out), + repeat: uint32(repeat), + } + + for { + _, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + if err == nil { + break + } + + if xerrors.Is(err, unix.EINTR) { + if reset != nil { + reset() + } + continue + } + + return 0, nil, 0, xerrors.Errorf("can't run test: %w", err) + } + + if int(attr.dataSizeOut) > cap(out) { + // Houston, we have a problem. The program created more data than we allocated, + // and the kernel wrote past the end of our buffer. + panic("kernel wrote past end of output buffer") + } + out = out[:int(attr.dataSizeOut)] + + total := time.Duration(attr.duration) * time.Nanosecond + return attr.retval, out, total, nil +} + +func unmarshalProgram(buf []byte) (*Program, error) { + if len(buf) != 4 { + return nil, xerrors.New("program id requires 4 byte value") + } + + // Looking up an entry in a nested map or prog array returns an id, + // not an fd. + id := internal.NativeEndian.Uint32(buf) + return NewProgramFromID(ProgramID(id)) +} + +// MarshalBinary implements BinaryMarshaler. +func (p *Program) MarshalBinary() ([]byte, error) { + value, err := p.fd.Value() + if err != nil { + return nil, err + } + + buf := make([]byte, 4) + internal.NativeEndian.PutUint32(buf, value) + return buf, nil +} + +// Attach a Program to a container object fd +func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error { + if fd < 0 { + return xerrors.New("invalid fd") + } + + pfd, err := p.fd.Value() + if err != nil { + return err + } + + attr := bpfProgAlterAttr{ + targetFd: uint32(fd), + attachBpfFd: pfd, + attachType: uint32(typ), + attachFlags: uint32(flags), + } + + return bpfProgAlter(_ProgAttach, &attr) +} + +// Detach a Program from a container object fd +func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error { + if fd < 0 { + return xerrors.New("invalid fd") + } + + pfd, err := p.fd.Value() + if err != nil { + return err + } + + attr := bpfProgAlterAttr{ + targetFd: uint32(fd), + attachBpfFd: pfd, + attachType: uint32(typ), + attachFlags: uint32(flags), + } + + return bpfProgAlter(_ProgDetach, &attr) +} + +// LoadPinnedProgram loads a Program from a BPF file. +// +// Requires at least Linux 4.11. +func LoadPinnedProgram(fileName string) (*Program, error) { + fd, err := bpfGetObject(fileName) + if err != nil { + return nil, err + } + + name, abi, err := newProgramABIFromFd(fd) + if err != nil { + _ = fd.Close() + return nil, xerrors.Errorf("can't get ABI for %s: %w", fileName, err) + } + + return newProgram(fd, name, abi), nil +} + +// SanitizeName replaces all invalid characters in name. +// +// Use this to automatically generate valid names for maps and +// programs at run time. +// +// Passing a negative value for replacement will delete characters +// instead of replacing them. +func SanitizeName(name string, replacement rune) string { + return strings.Map(func(char rune) rune { + if invalidBPFObjNameChar(char) { + return replacement + } + return char + }, name) +} + +// ProgramGetNextID returns the ID of the next eBPF program. +// +// Returns ErrNotExist, if there is no next eBPF program. +func ProgramGetNextID(startID ProgramID) (ProgramID, error) { + id, err := objGetNextID(_ProgGetNextID, uint32(startID)) + return ProgramID(id), err +} + +// NewProgramFromID returns the program for a given id. +// +// Returns ErrNotExist, if there is no eBPF program with the given id. +func NewProgramFromID(id ProgramID) (*Program, error) { + fd, err := bpfObjGetFDByID(_ProgGetFDByID, uint32(id)) + if err != nil { + return nil, err + } + + name, abi, err := newProgramABIFromFd(fd) + if err != nil { + _ = fd.Close() + return nil, err + } + + return newProgram(fd, name, abi), nil +} + +// ID returns the systemwide unique ID of the program. +func (p *Program) ID() (ProgramID, error) { + info, err := bpfGetProgInfoByFD(p.fd) + if err != nil { + return ProgramID(0), err + } + return ProgramID(info.id), nil +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/readme.md b/src/runtime/vendor/github.com/cilium/ebpf/readme.md new file mode 100644 index 000000000..cc8e89c75 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/readme.md @@ -0,0 +1,25 @@ +eBPF +------- +[![](https://godoc.org/github.com/cilium/ebpf?status.svg)](https://godoc.org/github.com/cilium/ebpf) + +eBPF is a pure Go library that provides utilities for loading, compiling, and debugging eBPF programs. It has minimal external dependencies and is intended to be used in long running processes. + +[ebpf/asm](https://godoc.org/github.com/cilium/ebpf/asm) contains a basic assembler. + +The library is maintained by [Cloudflare](https://www.cloudflare.com) and [Cilium](https://www.cilium.io). Feel free to [join](https://cilium.herokuapp.com/) the [libbpf-go](https://cilium.slack.com/messages/libbpf-go) channel on Slack. + +## Current status + +The package is production ready, but **the API is explicitly unstable +right now**. Expect to update your code if you want to follow along. + +## Requirements + +* A version of Go that is [supported by upstream](https://golang.org/doc/devel/release.html#policy) +* Linux 4.9, 4.19 or 5.4 (versions in-between should work, but are not tested) + +## Useful resources + +* [Cilium eBPF documentation](https://cilium.readthedocs.io/en/latest/bpf/#bpf-guide) (recommended) +* [Linux documentation on BPF](http://elixir.free-electrons.com/linux/latest/source/Documentation/networking/filter.txt) +* [eBPF features by Linux version](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md) diff --git a/src/runtime/vendor/github.com/cilium/ebpf/run-tests.sh b/src/runtime/vendor/github.com/cilium/ebpf/run-tests.sh new file mode 100644 index 000000000..daac404cf --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/run-tests.sh @@ -0,0 +1,69 @@ +#!/bin/bash +# Test the current package under a different kernel. +# Requires virtme and qemu to be installed. + +set -eu +set -o pipefail + +if [[ "${1:-}" = "--in-vm" ]]; then + shift + + mount -t bpf bpf /sys/fs/bpf + export CGO_ENABLED=0 + export GOFLAGS=-mod=readonly + export GOPATH=/run/go-path + export GOPROXY=file:///run/go-root/pkg/mod/cache/download + export GOCACHE=/run/go-cache + + echo Running tests... + /usr/local/bin/go test -coverprofile="$1/coverage.txt" -covermode=atomic -v ./... + touch "$1/success" + exit 0 +fi + +# Pull all dependencies, so that we can run tests without the +# vm having network access. +go mod download + +# Use sudo if /dev/kvm isn't accessible by the current user. +sudo="" +if [[ ! -r /dev/kvm || ! -w /dev/kvm ]]; then + sudo="sudo" +fi +readonly sudo + +readonly kernel_version="${1:-}" +if [[ -z "${kernel_version}" ]]; then + echo "Expecting kernel version as first argument" + exit 1 +fi + +readonly kernel="linux-${kernel_version}.bz" +readonly output="$(mktemp -d)" +readonly tmp_dir="${TMPDIR:-$(mktemp -d)}" + +test -e "${tmp_dir}/${kernel}" || { + echo Fetching "${kernel}" + curl --fail -L "https://github.com/newtools/ci-kernels/blob/master/${kernel}?raw=true" -o "${tmp_dir}/${kernel}" +} + +echo Testing on "${kernel_version}" +$sudo virtme-run --kimg "${tmp_dir}/${kernel}" --memory 512M --pwd \ + --rwdir=/run/output="${output}" \ + --rodir=/run/go-path="$(go env GOPATH)" \ + --rwdir=/run/go-cache="$(go env GOCACHE)" \ + --script-sh "$(realpath "$0") --in-vm /run/output" + +if [[ ! -e "${output}/success" ]]; then + echo "Test failed on ${kernel_version}" + exit 1 +else + echo "Test successful on ${kernel_version}" + if [[ -v CODECOV_TOKEN ]]; then + curl --fail -s https://codecov.io/bash > "${tmp_dir}/codecov.sh" + chmod +x "${tmp_dir}/codecov.sh" + "${tmp_dir}/codecov.sh" -f "${output}/coverage.txt" + fi +fi + +$sudo rm -r "${output}" diff --git a/src/runtime/vendor/github.com/cilium/ebpf/syscalls.go b/src/runtime/vendor/github.com/cilium/ebpf/syscalls.go new file mode 100644 index 000000000..20de356df --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/syscalls.go @@ -0,0 +1,478 @@ +package ebpf + +import ( + "path/filepath" + "unsafe" + + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" + "github.com/cilium/ebpf/internal/unix" + + "golang.org/x/xerrors" +) + +// Generic errors returned by BPF syscalls. +var ( + ErrNotExist = xerrors.New("requested object does not exit") +) + +// bpfObjName is a null-terminated string made up of +// 'A-Za-z0-9_' characters. +type bpfObjName [unix.BPF_OBJ_NAME_LEN]byte + +// newBPFObjName truncates the result if it is too long. +func newBPFObjName(name string) bpfObjName { + var result bpfObjName + copy(result[:unix.BPF_OBJ_NAME_LEN-1], name) + return result +} + +func invalidBPFObjNameChar(char rune) bool { + dotAllowed := objNameAllowsDot() == nil + + switch { + case char >= 'A' && char <= 'Z': + fallthrough + case char >= 'a' && char <= 'z': + fallthrough + case char >= '0' && char <= '9': + fallthrough + case dotAllowed && char == '.': + fallthrough + case char == '_': + return false + default: + return true + } +} + +type bpfMapCreateAttr struct { + mapType MapType + keySize uint32 + valueSize uint32 + maxEntries uint32 + flags uint32 + innerMapFd uint32 // since 4.12 56f668dfe00d + numaNode uint32 // since 4.14 96eabe7a40aa + mapName bpfObjName // since 4.15 ad5b177bd73f + mapIfIndex uint32 + btfFd uint32 + btfKeyTypeID btf.TypeID + btfValueTypeID btf.TypeID +} + +type bpfMapOpAttr struct { + mapFd uint32 + padding uint32 + key internal.Pointer + value internal.Pointer + flags uint64 +} + +type bpfMapInfo struct { + mapType uint32 + id uint32 + keySize uint32 + valueSize uint32 + maxEntries uint32 + flags uint32 + mapName bpfObjName // since 4.15 ad5b177bd73f +} + +type bpfPinObjAttr struct { + fileName internal.Pointer + fd uint32 + padding uint32 +} + +type bpfProgLoadAttr struct { + progType ProgramType + insCount uint32 + instructions internal.Pointer + license internal.Pointer + logLevel uint32 + logSize uint32 + logBuf internal.Pointer + kernelVersion uint32 // since 4.1 2541517c32be + progFlags uint32 // since 4.11 e07b98d9bffe + progName bpfObjName // since 4.15 067cae47771c + progIfIndex uint32 // since 4.15 1f6f4cb7ba21 + expectedAttachType AttachType // since 4.17 5e43f899b03a + progBTFFd uint32 + funcInfoRecSize uint32 + funcInfo internal.Pointer + funcInfoCnt uint32 + lineInfoRecSize uint32 + lineInfo internal.Pointer + lineInfoCnt uint32 +} + +type bpfProgInfo struct { + progType uint32 + id uint32 + tag [unix.BPF_TAG_SIZE]byte + jitedLen uint32 + xlatedLen uint32 + jited internal.Pointer + xlated internal.Pointer + loadTime uint64 // since 4.15 cb4d2b3f03d8 + createdByUID uint32 + nrMapIDs uint32 + mapIds internal.Pointer + name bpfObjName +} + +type bpfProgTestRunAttr struct { + fd uint32 + retval uint32 + dataSizeIn uint32 + dataSizeOut uint32 + dataIn internal.Pointer + dataOut internal.Pointer + repeat uint32 + duration uint32 +} + +type bpfProgAlterAttr struct { + targetFd uint32 + attachBpfFd uint32 + attachType uint32 + attachFlags uint32 +} + +type bpfObjGetInfoByFDAttr struct { + fd uint32 + infoLen uint32 + info internal.Pointer // May be either bpfMapInfo or bpfProgInfo +} + +type bpfGetFDByIDAttr struct { + id uint32 + next uint32 +} + +type bpfMapFreezeAttr struct { + mapFd uint32 +} + +type bpfObjGetNextIDAttr struct { + startID uint32 + nextID uint32 + openFlags uint32 +} + +func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) { + for { + fd, err := internal.BPF(_ProgLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + // As of ~4.20 the verifier can be interrupted by a signal, + // and returns EAGAIN in that case. + if err == unix.EAGAIN { + continue + } + + if err != nil { + return nil, err + } + + return internal.NewFD(uint32(fd)), nil + } +} + +func bpfProgAlter(cmd int, attr *bpfProgAlterAttr) error { + _, err := internal.BPF(cmd, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + return err +} + +func bpfMapCreate(attr *bpfMapCreateAttr) (*internal.FD, error) { + fd, err := internal.BPF(_MapCreate, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if err != nil { + return nil, err + } + + return internal.NewFD(uint32(fd)), nil +} + +var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool { + inner, err := bpfMapCreate(&bpfMapCreateAttr{ + mapType: Array, + keySize: 4, + valueSize: 4, + maxEntries: 1, + }) + if err != nil { + return false + } + defer inner.Close() + + innerFd, _ := inner.Value() + nested, err := bpfMapCreate(&bpfMapCreateAttr{ + mapType: ArrayOfMaps, + keySize: 4, + valueSize: 4, + maxEntries: 1, + innerMapFd: innerFd, + }) + if err != nil { + return false + } + + _ = nested.Close() + return true +}) + +var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() bool { + // This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since + // BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check. + m, err := bpfMapCreate(&bpfMapCreateAttr{ + mapType: Array, + keySize: 4, + valueSize: 4, + maxEntries: 1, + flags: unix.BPF_F_RDONLY_PROG, + }) + if err != nil { + return false + } + _ = m.Close() + return true +}) + +func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapOpAttr{ + mapFd: fd, + key: key, + value: valueOut, + } + _, err = internal.BPF(_MapLookupElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return wrapMapError(err) +} + +func bpfMapLookupAndDelete(m *internal.FD, key, valueOut internal.Pointer) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapOpAttr{ + mapFd: fd, + key: key, + value: valueOut, + } + _, err = internal.BPF(_MapLookupAndDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return wrapMapError(err) +} + +func bpfMapUpdateElem(m *internal.FD, key, valueOut internal.Pointer, flags uint64) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapOpAttr{ + mapFd: fd, + key: key, + value: valueOut, + flags: flags, + } + _, err = internal.BPF(_MapUpdateElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return wrapMapError(err) +} + +func bpfMapDeleteElem(m *internal.FD, key internal.Pointer) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapOpAttr{ + mapFd: fd, + key: key, + } + _, err = internal.BPF(_MapDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return wrapMapError(err) +} + +func bpfMapGetNextKey(m *internal.FD, key, nextKeyOut internal.Pointer) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapOpAttr{ + mapFd: fd, + key: key, + value: nextKeyOut, + } + _, err = internal.BPF(_MapGetNextKey, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return wrapMapError(err) +} + +func objGetNextID(cmd int, start uint32) (uint32, error) { + attr := bpfObjGetNextIDAttr{ + startID: start, + } + _, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return attr.nextID, wrapObjError(err) +} + +func wrapObjError(err error) error { + if err == nil { + return nil + } + if xerrors.Is(err, unix.ENOENT) { + return xerrors.Errorf("%w", ErrNotExist) + } + + return xerrors.New(err.Error()) +} + +func wrapMapError(err error) error { + if err == nil { + return nil + } + + if xerrors.Is(err, unix.ENOENT) { + return ErrKeyNotExist + } + + return xerrors.New(err.Error()) +} + +func bpfMapFreeze(m *internal.FD) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapFreezeAttr{ + mapFd: fd, + } + _, err = internal.BPF(_MapFreeze, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return err +} + +const bpfFSType = 0xcafe4a11 + +func bpfPinObject(fileName string, fd *internal.FD) error { + dirName := filepath.Dir(fileName) + var statfs unix.Statfs_t + if err := unix.Statfs(dirName, &statfs); err != nil { + return err + } + if uint64(statfs.Type) != bpfFSType { + return xerrors.Errorf("%s is not on a bpf filesystem", fileName) + } + + value, err := fd.Value() + if err != nil { + return err + } + + _, err = internal.BPF(_ObjPin, unsafe.Pointer(&bpfPinObjAttr{ + fileName: internal.NewStringPointer(fileName), + fd: value, + }), 16) + if err != nil { + return xerrors.Errorf("pin object %s: %w", fileName, err) + } + return nil +} + +func bpfGetObject(fileName string) (*internal.FD, error) { + ptr, err := internal.BPF(_ObjGet, unsafe.Pointer(&bpfPinObjAttr{ + fileName: internal.NewStringPointer(fileName), + }), 16) + if err != nil { + return nil, xerrors.Errorf("get object %s: %w", fileName, err) + } + return internal.NewFD(uint32(ptr)), nil +} + +func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) error { + value, err := fd.Value() + if err != nil { + return err + } + + // available from 4.13 + attr := bpfObjGetInfoByFDAttr{ + fd: value, + infoLen: uint32(size), + info: internal.NewPointer(info), + } + _, err = internal.BPF(_ObjGetInfoByFD, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + if err != nil { + return xerrors.Errorf("fd %d: %w", fd, err) + } + return nil +} + +func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) { + var info bpfProgInfo + if err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil { + return nil, xerrors.Errorf("can't get program info: %w", err) + } + return &info, nil +} + +func bpfGetMapInfoByFD(fd *internal.FD) (*bpfMapInfo, error) { + var info bpfMapInfo + err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)) + if err != nil { + return nil, xerrors.Errorf("can't get map info: %w", err) + } + return &info, nil +} + +var haveObjName = internal.FeatureTest("object names", "4.15", func() bool { + attr := bpfMapCreateAttr{ + mapType: Array, + keySize: 4, + valueSize: 4, + maxEntries: 1, + mapName: newBPFObjName("feature_test"), + } + + fd, err := bpfMapCreate(&attr) + if err != nil { + return false + } + + _ = fd.Close() + return true +}) + +var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() bool { + if err := haveObjName(); err != nil { + return false + } + + attr := bpfMapCreateAttr{ + mapType: Array, + keySize: 4, + valueSize: 4, + maxEntries: 1, + mapName: newBPFObjName(".test"), + } + + fd, err := bpfMapCreate(&attr) + if err != nil { + return false + } + + _ = fd.Close() + return true +}) + +func bpfObjGetFDByID(cmd int, id uint32) (*internal.FD, error) { + attr := bpfGetFDByIDAttr{ + id: id, + } + ptr, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return internal.NewFD(uint32(ptr)), wrapObjError(err) +} diff --git a/src/runtime/vendor/github.com/cilium/ebpf/types.go b/src/runtime/vendor/github.com/cilium/ebpf/types.go new file mode 100644 index 000000000..94cdb3081 --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/types.go @@ -0,0 +1,220 @@ +package ebpf + +//go:generate stringer -output types_string.go -type=MapType,ProgramType + +// MapType indicates the type map structure +// that will be initialized in the kernel. +type MapType uint32 + +// All the various map types that can be created +const ( + UnspecifiedMap MapType = iota + // Hash is a hash map + Hash + // Array is an array map + Array + // ProgramArray - A program array map is a special kind of array map whose map + // values contain only file descriptors referring to other eBPF + // programs. Thus, both the key_size and value_size must be + // exactly four bytes. This map is used in conjunction with the + // TailCall helper. + ProgramArray + // PerfEventArray - A perf event array is used in conjunction with PerfEventRead + // and PerfEventOutput calls, to read the raw bpf_perf_data from the registers. + PerfEventArray + // PerCPUHash - This data structure is useful for people who have high performance + // network needs and can reconcile adds at the end of some cycle, so that + // hashes can be lock free without the use of XAdd, which can be costly. + PerCPUHash + // PerCPUArray - This data structure is useful for people who have high performance + // network needs and can reconcile adds at the end of some cycle, so that + // hashes can be lock free without the use of XAdd, which can be costly. + // Each CPU gets a copy of this hash, the contents of all of which can be reconciled + // later. + PerCPUArray + // StackTrace - This holds whole user and kernel stack traces, it can be retrieved with + // GetStackID + StackTrace + // CGroupArray - This is a very niche structure used to help SKBInCGroup determine + // if an skb is from a socket belonging to a specific cgroup + CGroupArray + // LRUHash - This allows you to create a small hash structure that will purge the + // least recently used items rather than thow an error when you run out of memory + LRUHash + // LRUCPUHash - This is NOT like PerCPUHash, this structure is shared among the CPUs, + // it has more to do with including the CPU id with the LRU calculation so that if a + // particular CPU is using a value over-and-over again, then it will be saved, but if + // a value is being retrieved a lot but sparsely across CPUs it is not as important, basically + // giving weight to CPU locality over overall usage. + LRUCPUHash + // LPMTrie - This is an implementation of Longest-Prefix-Match Trie structure. It is useful, + // for storing things like IP addresses which can be bit masked allowing for keys of differing + // values to refer to the same reference based on their masks. See wikipedia for more details. + LPMTrie + // ArrayOfMaps - Each item in the array is another map. The inner map mustn't be a map of maps + // itself. + ArrayOfMaps + // HashOfMaps - Each item in the hash map is another map. The inner map mustn't be a map of maps + // itself. + HashOfMaps + // DevMap - Specialized map to store references to network devices. + DevMap + // SockMap - Specialized map to store references to sockets. + SockMap + // CPUMap - Specialized map to store references to CPUs. + CPUMap + // XSKMap - Specialized map for XDP programs to store references to open sockets. + XSKMap + // SockHash - Specialized hash to store references to sockets. + SockHash + // CGroupStorage - Special map for CGroups. + CGroupStorage + // ReusePortSockArray - Specialized map to store references to sockets that can be reused. + ReusePortSockArray + // PerCPUCGroupStorage - Special per CPU map for CGroups. + PerCPUCGroupStorage + // Queue - FIFO storage for BPF programs. + Queue + // Stack - LIFO storage for BPF programs. + Stack + // SkStorage - Specialized map for local storage at SK for BPF programs. + SkStorage + // DevMapHash - Hash-based indexing scheme for references to network devices. + DevMapHash +) + +// hasPerCPUValue returns true if the Map stores a value per CPU. +func (mt MapType) hasPerCPUValue() bool { + if mt == PerCPUHash || mt == PerCPUArray { + return true + } + return false +} + +const ( + _MapCreate = iota + _MapLookupElem + _MapUpdateElem + _MapDeleteElem + _MapGetNextKey + _ProgLoad + _ObjPin + _ObjGet + _ProgAttach + _ProgDetach + _ProgTestRun + _ProgGetNextID + _MapGetNextID + _ProgGetFDByID + _MapGetFDByID + _ObjGetInfoByFD + _ProgQuery + _RawTracepointOpen + _BTFLoad + _BTFGetFDByID + _TaskFDQuery + _MapLookupAndDeleteElem + _MapFreeze + _BTFGetNextID +) + +// ProgramType of the eBPF program +type ProgramType uint32 + +// eBPF program types +const ( + // Unrecognized program type + UnspecifiedProgram ProgramType = iota + // SocketFilter socket or seccomp filter + SocketFilter + // Kprobe program + Kprobe + // SchedCLS traffic control shaper + SchedCLS + // SchedACT routing control shaper + SchedACT + // TracePoint program + TracePoint + // XDP program + XDP + // PerfEvent program + PerfEvent + // CGroupSKB program + CGroupSKB + // CGroupSock program + CGroupSock + // LWTIn program + LWTIn + // LWTOut program + LWTOut + // LWTXmit program + LWTXmit + // SockOps program + SockOps + // SkSKB program + SkSKB + // CGroupDevice program + CGroupDevice + // SkMsg program + SkMsg + // RawTracepoint program + RawTracepoint + // CGroupSockAddr program + CGroupSockAddr + // LWTSeg6Local program + LWTSeg6Local + // LircMode2 program + LircMode2 + // SkReuseport program + SkReuseport + // FlowDissector program + FlowDissector + // CGroupSysctl program + CGroupSysctl + // RawTracepointWritable program + RawTracepointWritable + // CGroupSockopt program + CGroupSockopt + // Tracing program + Tracing +) + +// AttachType of the eBPF program, needed to differentiate allowed context accesses in +// some newer program types like CGroupSockAddr. Should be set to AttachNone if not required. +// Will cause invalid argument (EINVAL) at program load time if set incorrectly. +type AttachType uint32 + +// AttachNone is an alias for AttachCGroupInetIngress for readability reasons +const AttachNone AttachType = 0 + +const ( + AttachCGroupInetIngress AttachType = iota + AttachCGroupInetEgress + AttachCGroupInetSockCreate + AttachCGroupSockOps + AttachSkSKBStreamParser + AttachSkSKBStreamVerdict + AttachCGroupDevice + AttachSkMsgVerdict + AttachCGroupInet4Bind + AttachCGroupInet6Bind + AttachCGroupInet4Connect + AttachCGroupInet6Connect + AttachCGroupInet4PostBind + AttachCGroupInet6PostBind + AttachCGroupUDP4Sendmsg + AttachCGroupUDP6Sendmsg + AttachLircMode2 + AttachFlowDissector + AttachCGroupSysctl + AttachCGroupUDP4Recvmsg + AttachCGroupUDP6Recvmsg + AttachCGroupGetsockopt + AttachCGroupSetsockopt + AttachTraceRawTp + AttachTraceFEntry + AttachTraceFExit +) + +// AttachFlags of the eBPF program used in BPF_PROG_ATTACH command +type AttachFlags uint32 diff --git a/src/runtime/vendor/github.com/cilium/ebpf/types_string.go b/src/runtime/vendor/github.com/cilium/ebpf/types_string.go new file mode 100644 index 000000000..f41ba77df --- /dev/null +++ b/src/runtime/vendor/github.com/cilium/ebpf/types_string.go @@ -0,0 +1,91 @@ +// Code generated by "stringer -output types_string.go -type=MapType,ProgramType"; DO NOT EDIT. + +package ebpf + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[UnspecifiedMap-0] + _ = x[Hash-1] + _ = x[Array-2] + _ = x[ProgramArray-3] + _ = x[PerfEventArray-4] + _ = x[PerCPUHash-5] + _ = x[PerCPUArray-6] + _ = x[StackTrace-7] + _ = x[CGroupArray-8] + _ = x[LRUHash-9] + _ = x[LRUCPUHash-10] + _ = x[LPMTrie-11] + _ = x[ArrayOfMaps-12] + _ = x[HashOfMaps-13] + _ = x[DevMap-14] + _ = x[SockMap-15] + _ = x[CPUMap-16] + _ = x[XSKMap-17] + _ = x[SockHash-18] + _ = x[CGroupStorage-19] + _ = x[ReusePortSockArray-20] + _ = x[PerCPUCGroupStorage-21] + _ = x[Queue-22] + _ = x[Stack-23] + _ = x[SkStorage-24] + _ = x[DevMapHash-25] +} + +const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHash" + +var _MapType_index = [...]uint8{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248} + +func (i MapType) String() string { + if i >= MapType(len(_MapType_index)-1) { + return "MapType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _MapType_name[_MapType_index[i]:_MapType_index[i+1]] +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[UnspecifiedProgram-0] + _ = x[SocketFilter-1] + _ = x[Kprobe-2] + _ = x[SchedCLS-3] + _ = x[SchedACT-4] + _ = x[TracePoint-5] + _ = x[XDP-6] + _ = x[PerfEvent-7] + _ = x[CGroupSKB-8] + _ = x[CGroupSock-9] + _ = x[LWTIn-10] + _ = x[LWTOut-11] + _ = x[LWTXmit-12] + _ = x[SockOps-13] + _ = x[SkSKB-14] + _ = x[CGroupDevice-15] + _ = x[SkMsg-16] + _ = x[RawTracepoint-17] + _ = x[CGroupSockAddr-18] + _ = x[LWTSeg6Local-19] + _ = x[LircMode2-20] + _ = x[SkReuseport-21] + _ = x[FlowDissector-22] + _ = x[CGroupSysctl-23] + _ = x[RawTracepointWritable-24] + _ = x[CGroupSockopt-25] + _ = x[Tracing-26] +} + +const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracing" + +var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265} + +func (i ProgramType) String() string { + if i >= ProgramType(len(_ProgramType_index)-1) { + return "ProgramType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ProgramType_name[_ProgramType_index[i]:_ProgramType_index[i+1]] +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/.gitignore b/src/runtime/vendor/github.com/containerd/cgroups/.gitignore new file mode 100644 index 000000000..528cd5b39 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/.gitignore @@ -0,0 +1 @@ +example/example diff --git a/src/runtime/vendor/github.com/containerd/cgroups/.travis.yml b/src/runtime/vendor/github.com/containerd/cgroups/.travis.yml new file mode 100644 index 000000000..98eaf8dda --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/.travis.yml @@ -0,0 +1,27 @@ +language: go +go: + - 1.11.x + - 1.12.x + +go_import_path: github.com/containerd/cgroups + +install: + - mkdir -p $GOPATH/src/github.com/prometheus $GOPATH/src/github.com/opencontainers $GOPATH/src/github.com/coreos $GOPATH/src/github.com/godbus + - cd $GOPATH/src/github.com/opencontainers && git clone https://github.com/opencontainers/runtime-spec && cd runtime-spec && git checkout fa4b36aa9c99e00c2ef7b5c0013c84100ede5f4e + - cd $GOPATH/src/github.com/coreos && git clone https://github.com/coreos/go-systemd && cd go-systemd && git checkout 48702e0da86bd25e76cfef347e2adeb434a0d0a6 + - cd $GOPATH/src/github.com/godbus && git clone https://github.com/godbus/dbus && cd dbus && git checkout c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f + - cd $GOPATH/src/github.com/containerd/cgroups + - go get -d -t ./... + - go get -u github.com/vbatts/git-validation + - go get -u github.com/kunalkushwaha/ltag + +before_script: + - pushd ..; git clone https://github.com/containerd/project; popd + +script: + - DCO_VERBOSITY=-q ../project/script/validate/dco + - ../project/script/validate/fileheader ../project/ + - go test -race -coverprofile=coverage.txt -covermode=atomic + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/src/runtime/vendor/github.com/containerd/cgroups/LICENSE b/src/runtime/vendor/github.com/containerd/cgroups/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/src/runtime/vendor/github.com/containerd/cgroups/Makefile b/src/runtime/vendor/github.com/containerd/cgroups/Makefile new file mode 100644 index 000000000..d7f2628ae --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/Makefile @@ -0,0 +1,21 @@ +# Copyright The containerd Authors. + +# 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. + +PACKAGES=$(shell go list ./... | grep -v /vendor/) + +all: + go build -v + +proto: + protobuild --quiet ${PACKAGES} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/Protobuild.toml b/src/runtime/vendor/github.com/containerd/cgroups/Protobuild.toml new file mode 100644 index 000000000..0d462b507 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/Protobuild.toml @@ -0,0 +1,39 @@ +version = "unstable" +generator = "gogoctrd" +plugins = ["grpc"] + +# Control protoc include paths. Below are usually some good defaults, but feel +# free to try it without them if it works for your project. +[includes] + # Include paths that will be added before all others. Typically, you want to + # treat the root of the project as an include, but this may not be necessary. + # before = ["."] + + # Paths that should be treated as include roots in relation to the vendor + # directory. These will be calculated with the vendor directory nearest the + # target package. + # vendored = ["github.com/gogo/protobuf"] + packages = ["github.com/gogo/protobuf"] + + # Paths that will be added untouched to the end of the includes. We use + # `/usr/local/include` to pickup the common install location of protobuf. + # This is the default. + after = ["/usr/local/include"] + +# This section maps protobuf imports to Go packages. These will become +# `-M` directives in the call to the go protobuf generator. +[packages] + "gogoproto/gogo.proto" = "github.com/gogo/protobuf/gogoproto" + "google/protobuf/any.proto" = "github.com/gogo/protobuf/types" + "google/protobuf/descriptor.proto" = "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" + "google/protobuf/field_mask.proto" = "github.com/gogo/protobuf/types" + "google/protobuf/timestamp.proto" = "github.com/gogo/protobuf/types" + +# Aggregrate the API descriptors to lock down API changes. +[[descriptors]] +prefix = "github.com/containerd/cgroups" +target = "metrics.pb.txt" +ignore_files = [ + "google/protobuf/descriptor.proto", + "gogoproto/gogo.proto" +] diff --git a/src/runtime/vendor/github.com/containerd/cgroups/README.md b/src/runtime/vendor/github.com/containerd/cgroups/README.md new file mode 100644 index 000000000..81ad11cc7 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/README.md @@ -0,0 +1,124 @@ +# cgroups + +[![Build Status](https://travis-ci.org/containerd/cgroups.svg?branch=master)](https://travis-ci.org/containerd/cgroups) +[![codecov](https://codecov.io/gh/containerd/cgroups/branch/master/graph/badge.svg)](https://codecov.io/gh/containerd/cgroups) +[![GoDoc](https://godoc.org/github.com/containerd/cgroups?status.svg)](https://godoc.org/github.com/containerd/cgroups) +[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/cgroups)](https://goreportcard.com/report/github.com/containerd/cgroups) + +Go package for creating, managing, inspecting, and destroying cgroups. +The resources format for settings on the cgroup uses the OCI runtime-spec found +[here](https://github.com/opencontainers/runtime-spec). + +## Examples + +### Create a new cgroup + +This creates a new cgroup using a static path for all subsystems under `/test`. + +* /sys/fs/cgroup/cpu/test +* /sys/fs/cgroup/memory/test +* etc.... + +It uses a single hierarchy and specifies cpu shares as a resource constraint and +uses the v1 implementation of cgroups. + + +```go +shares := uint64(100) +control, err := cgroups.New(cgroups.V1, cgroups.StaticPath("/test"), &specs.LinuxResources{ + CPU: &specs.CPU{ + Shares: &shares, + }, +}) +defer control.Delete() +``` + +### Create with systemd slice support + + +```go +control, err := cgroups.New(cgroups.Systemd, cgroups.Slice("system.slice", "runc-test"), &specs.LinuxResources{ + CPU: &specs.CPU{ + Shares: &shares, + }, +}) + +``` + +### Load an existing cgroup + +```go +control, err = cgroups.Load(cgroups.V1, cgroups.StaticPath("/test")) +``` + +### Add a process to the cgroup + +```go +if err := control.Add(cgroups.Process{Pid:1234}); err != nil { +} +``` + +### Update the cgroup + +To update the resources applied in the cgroup + +```go +shares = uint64(200) +if err := control.Update(&specs.LinuxResources{ + CPU: &specs.CPU{ + Shares: &shares, + }, +}); err != nil { +} +``` + +### Freeze and Thaw the cgroup + +```go +if err := control.Freeze(); err != nil { +} +if err := control.Thaw(); err != nil { +} +``` + +### List all processes in the cgroup or recursively + +```go +processes, err := control.Processes(cgroups.Devices, recursive) +``` + +### Get Stats on the cgroup + +```go +stats, err := control.Stat() +``` + +By adding `cgroups.IgnoreNotExist` all non-existent files will be ignored, e.g. swap memory stats without swap enabled +```go +stats, err := control.Stat(cgroups.IgnoreNotExist) +``` + +### Move process across cgroups + +This allows you to take processes from one cgroup and move them to another. + +```go +err := control.MoveTo(destination) +``` + +### Create subcgroup + +```go +subCgroup, err := control.New("child", resources) +``` + +## Project details + +Cgroups is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/src/runtime/vendor/github.com/containerd/cgroups/blkio.go b/src/runtime/vendor/github.com/containerd/cgroups/blkio.go new file mode 100644 index 000000000..7c498def6 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/blkio.go @@ -0,0 +1,338 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func NewBlkio(root string) *blkioController { + return &blkioController{ + root: filepath.Join(root, string(Blkio)), + } +} + +type blkioController struct { + root string +} + +func (b *blkioController) Name() Name { + return Blkio +} + +func (b *blkioController) Path(path string) string { + return filepath.Join(b.root, path) +} + +func (b *blkioController) Create(path string, resources *specs.LinuxResources) error { + if err := os.MkdirAll(b.Path(path), defaultDirPerm); err != nil { + return err + } + if resources.BlockIO == nil { + return nil + } + for _, t := range createBlkioSettings(resources.BlockIO) { + if t.value != nil { + if err := ioutil.WriteFile( + filepath.Join(b.Path(path), fmt.Sprintf("blkio.%s", t.name)), + t.format(t.value), + defaultFilePerm, + ); err != nil { + return err + } + } + } + return nil +} + +func (b *blkioController) Update(path string, resources *specs.LinuxResources) error { + return b.Create(path, resources) +} + +func (b *blkioController) Stat(path string, stats *Metrics) error { + stats.Blkio = &BlkIOStat{} + settings := []blkioStatSettings{ + { + name: "throttle.io_serviced", + entry: &stats.Blkio.IoServicedRecursive, + }, + { + name: "throttle.io_service_bytes", + entry: &stats.Blkio.IoServiceBytesRecursive, + }, + } + // Try to read CFQ stats available on all CFQ enabled kernels first + if _, err := os.Lstat(filepath.Join(b.Path(path), fmt.Sprintf("blkio.io_serviced_recursive"))); err == nil { + settings = []blkioStatSettings{} + settings = append(settings, + blkioStatSettings{ + name: "sectors_recursive", + entry: &stats.Blkio.SectorsRecursive, + }, + blkioStatSettings{ + name: "io_service_bytes_recursive", + entry: &stats.Blkio.IoServiceBytesRecursive, + }, + blkioStatSettings{ + name: "io_serviced_recursive", + entry: &stats.Blkio.IoServicedRecursive, + }, + blkioStatSettings{ + name: "io_queued_recursive", + entry: &stats.Blkio.IoQueuedRecursive, + }, + blkioStatSettings{ + name: "io_service_time_recursive", + entry: &stats.Blkio.IoServiceTimeRecursive, + }, + blkioStatSettings{ + name: "io_wait_time_recursive", + entry: &stats.Blkio.IoWaitTimeRecursive, + }, + blkioStatSettings{ + name: "io_merged_recursive", + entry: &stats.Blkio.IoMergedRecursive, + }, + blkioStatSettings{ + name: "time_recursive", + entry: &stats.Blkio.IoTimeRecursive, + }, + ) + } + f, err := os.Open("/proc/diskstats") + if err != nil { + return err + } + defer f.Close() + + devices, err := getDevices(f) + if err != nil { + return err + } + + for _, t := range settings { + if err := b.readEntry(devices, path, t.name, t.entry); err != nil { + return err + } + } + return nil +} + +func (b *blkioController) readEntry(devices map[deviceKey]string, path, name string, entry *[]*BlkIOEntry) error { + f, err := os.Open(filepath.Join(b.Path(path), fmt.Sprintf("blkio.%s", name))) + if err != nil { + return err + } + defer f.Close() + sc := bufio.NewScanner(f) + for sc.Scan() { + if err := sc.Err(); err != nil { + return err + } + // format: dev type amount + fields := strings.FieldsFunc(sc.Text(), splitBlkIOStatLine) + if len(fields) < 3 { + if len(fields) == 2 && fields[0] == "Total" { + // skip total line + continue + } else { + return fmt.Errorf("Invalid line found while parsing %s: %s", path, sc.Text()) + } + } + major, err := strconv.ParseUint(fields[0], 10, 64) + if err != nil { + return err + } + minor, err := strconv.ParseUint(fields[1], 10, 64) + if err != nil { + return err + } + op := "" + valueField := 2 + if len(fields) == 4 { + op = fields[2] + valueField = 3 + } + v, err := strconv.ParseUint(fields[valueField], 10, 64) + if err != nil { + return err + } + *entry = append(*entry, &BlkIOEntry{ + Device: devices[deviceKey{major, minor}], + Major: major, + Minor: minor, + Op: op, + Value: v, + }) + } + return nil +} + +func createBlkioSettings(blkio *specs.LinuxBlockIO) []blkioSettings { + settings := []blkioSettings{} + + if blkio.Weight != nil { + settings = append(settings, + blkioSettings{ + name: "weight", + value: blkio.Weight, + format: uintf, + }) + } + if blkio.LeafWeight != nil { + settings = append(settings, + blkioSettings{ + name: "leaf_weight", + value: blkio.LeafWeight, + format: uintf, + }) + } + for _, wd := range blkio.WeightDevice { + if wd.Weight != nil { + settings = append(settings, + blkioSettings{ + name: "weight_device", + value: wd, + format: weightdev, + }) + } + if wd.LeafWeight != nil { + settings = append(settings, + blkioSettings{ + name: "leaf_weight_device", + value: wd, + format: weightleafdev, + }) + } + } + for _, t := range []struct { + name string + list []specs.LinuxThrottleDevice + }{ + { + name: "throttle.read_bps_device", + list: blkio.ThrottleReadBpsDevice, + }, + { + name: "throttle.read_iops_device", + list: blkio.ThrottleReadIOPSDevice, + }, + { + name: "throttle.write_bps_device", + list: blkio.ThrottleWriteBpsDevice, + }, + { + name: "throttle.write_iops_device", + list: blkio.ThrottleWriteIOPSDevice, + }, + } { + for _, td := range t.list { + settings = append(settings, blkioSettings{ + name: t.name, + value: td, + format: throttleddev, + }) + } + } + return settings +} + +type blkioSettings struct { + name string + value interface{} + format func(v interface{}) []byte +} + +type blkioStatSettings struct { + name string + entry *[]*BlkIOEntry +} + +func uintf(v interface{}) []byte { + return []byte(strconv.FormatUint(uint64(*v.(*uint16)), 10)) +} + +func weightdev(v interface{}) []byte { + wd := v.(specs.LinuxWeightDevice) + return []byte(fmt.Sprintf("%d:%d %d", wd.Major, wd.Minor, *wd.Weight)) +} + +func weightleafdev(v interface{}) []byte { + wd := v.(specs.LinuxWeightDevice) + return []byte(fmt.Sprintf("%d:%d %d", wd.Major, wd.Minor, *wd.LeafWeight)) +} + +func throttleddev(v interface{}) []byte { + td := v.(specs.LinuxThrottleDevice) + return []byte(fmt.Sprintf("%d:%d %d", td.Major, td.Minor, td.Rate)) +} + +func splitBlkIOStatLine(r rune) bool { + return r == ' ' || r == ':' +} + +type deviceKey struct { + major, minor uint64 +} + +// getDevices makes a best effort attempt to read all the devices into a map +// keyed by major and minor number. Since devices may be mapped multiple times, +// we err on taking the first occurrence. +func getDevices(r io.Reader) (map[deviceKey]string, error) { + + var ( + s = bufio.NewScanner(r) + devices = make(map[deviceKey]string) + ) + for s.Scan() { + fields := strings.Fields(s.Text()) + major, err := strconv.Atoi(fields[0]) + if err != nil { + return nil, err + } + minor, err := strconv.Atoi(fields[1]) + if err != nil { + return nil, err + } + key := deviceKey{ + major: uint64(major), + minor: uint64(minor), + } + if _, ok := devices[key]; ok { + continue + } + devices[key] = filepath.Join("/dev", fields[2]) + } + return devices, s.Err() +} + +func major(devNumber uint64) uint64 { + return (devNumber >> 8) & 0xfff +} + +func minor(devNumber uint64) uint64 { + return (devNumber & 0xff) | ((devNumber >> 12) & 0xfff00) +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/cgroup.go b/src/runtime/vendor/github.com/containerd/cgroups/cgroup.go new file mode 100644 index 000000000..53866685b --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/cgroup.go @@ -0,0 +1,532 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) + +// New returns a new control via the cgroup cgroups interface +func New(hierarchy Hierarchy, path Path, resources *specs.LinuxResources, opts ...InitOpts) (Cgroup, error) { + config := newInitConfig() + for _, o := range opts { + if err := o(config); err != nil { + return nil, err + } + } + subsystems, err := hierarchy() + if err != nil { + return nil, err + } + var active []Subsystem + for _, s := range subsystems { + // check if subsystem exists + if err := initializeSubsystem(s, path, resources); err != nil { + if err == ErrControllerNotActive { + if config.InitCheck != nil { + if skerr := config.InitCheck(s, path, err); skerr != nil { + if skerr != ErrIgnoreSubsystem { + return nil, skerr + } + } + } + continue + } + return nil, err + } + active = append(active, s) + } + return &cgroup{ + path: path, + subsystems: active, + }, nil +} + +// Load will load an existing cgroup and allow it to be controlled +func Load(hierarchy Hierarchy, path Path, opts ...InitOpts) (Cgroup, error) { + config := newInitConfig() + for _, o := range opts { + if err := o(config); err != nil { + return nil, err + } + } + var activeSubsystems []Subsystem + subsystems, err := hierarchy() + if err != nil { + return nil, err + } + // check that the subsystems still exist, and keep only those that actually exist + for _, s := range pathers(subsystems) { + p, err := path(s.Name()) + if err != nil { + if os.IsNotExist(errors.Cause(err)) { + return nil, ErrCgroupDeleted + } + if err == ErrControllerNotActive { + if config.InitCheck != nil { + if skerr := config.InitCheck(s, path, err); skerr != nil { + if skerr != ErrIgnoreSubsystem { + return nil, skerr + } + } + } + continue + } + return nil, err + } + if _, err := os.Lstat(s.Path(p)); err != nil { + if os.IsNotExist(err) { + continue + } + return nil, err + } + activeSubsystems = append(activeSubsystems, s) + } + // if we do not have any active systems then the cgroup is deleted + if len(activeSubsystems) == 0 { + return nil, ErrCgroupDeleted + } + return &cgroup{ + path: path, + subsystems: activeSubsystems, + }, nil +} + +type cgroup struct { + path Path + + subsystems []Subsystem + mu sync.Mutex + err error +} + +// New returns a new sub cgroup +func (c *cgroup) New(name string, resources *specs.LinuxResources) (Cgroup, error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return nil, c.err + } + path := subPath(c.path, name) + for _, s := range c.subsystems { + if err := initializeSubsystem(s, path, resources); err != nil { + return nil, err + } + } + return &cgroup{ + path: path, + subsystems: c.subsystems, + }, nil +} + +// Subsystems returns all the subsystems that are currently being +// consumed by the group +func (c *cgroup) Subsystems() []Subsystem { + return c.subsystems +} + +// Add moves the provided process into the new cgroup +func (c *cgroup) Add(process Process) error { + if process.Pid <= 0 { + return ErrInvalidPid + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return c.err + } + return c.add(process) +} + +func (c *cgroup) add(process Process) error { + for _, s := range pathers(c.subsystems) { + p, err := c.path(s.Name()) + if err != nil { + return err + } + if err := ioutil.WriteFile( + filepath.Join(s.Path(p), cgroupProcs), + []byte(strconv.Itoa(process.Pid)), + defaultFilePerm, + ); err != nil { + return err + } + } + return nil +} + +// AddTask moves the provided tasks (threads) into the new cgroup +func (c *cgroup) AddTask(process Process) error { + if process.Pid <= 0 { + return ErrInvalidPid + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return c.err + } + return c.addTask(process) +} + +func (c *cgroup) addTask(process Process) error { + for _, s := range pathers(c.subsystems) { + p, err := c.path(s.Name()) + if err != nil { + return err + } + if err := ioutil.WriteFile( + filepath.Join(s.Path(p), cgroupTasks), + []byte(strconv.Itoa(process.Pid)), + defaultFilePerm, + ); err != nil { + return err + } + } + return nil +} + +// Delete will remove the control group from each of the subsystems registered +func (c *cgroup) Delete() error { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return c.err + } + var errors []string + for _, s := range c.subsystems { + if d, ok := s.(deleter); ok { + sp, err := c.path(s.Name()) + if err != nil { + return err + } + if err := d.Delete(sp); err != nil { + errors = append(errors, string(s.Name())) + } + continue + } + if p, ok := s.(pather); ok { + sp, err := c.path(s.Name()) + if err != nil { + return err + } + path := p.Path(sp) + if err := remove(path); err != nil { + errors = append(errors, path) + } + } + } + if len(errors) > 0 { + return fmt.Errorf("cgroups: unable to remove paths %s", strings.Join(errors, ", ")) + } + c.err = ErrCgroupDeleted + return nil +} + +// Stat returns the current metrics for the cgroup +func (c *cgroup) Stat(handlers ...ErrorHandler) (*Metrics, error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return nil, c.err + } + if len(handlers) == 0 { + handlers = append(handlers, errPassthrough) + } + var ( + stats = &Metrics{ + CPU: &CPUStat{ + Throttling: &Throttle{}, + Usage: &CPUUsage{}, + }, + } + wg = &sync.WaitGroup{} + errs = make(chan error, len(c.subsystems)) + ) + for _, s := range c.subsystems { + if ss, ok := s.(stater); ok { + sp, err := c.path(s.Name()) + if err != nil { + return nil, err + } + wg.Add(1) + go func() { + defer wg.Done() + if err := ss.Stat(sp, stats); err != nil { + for _, eh := range handlers { + if herr := eh(err); herr != nil { + errs <- herr + } + } + } + }() + } + } + wg.Wait() + close(errs) + for err := range errs { + return nil, err + } + return stats, nil +} + +// Update updates the cgroup with the new resource values provided +// +// Be prepared to handle EBUSY when trying to update a cgroup with +// live processes and other operations like Stats being performed at the +// same time +func (c *cgroup) Update(resources *specs.LinuxResources) error { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return c.err + } + for _, s := range c.subsystems { + if u, ok := s.(updater); ok { + sp, err := c.path(s.Name()) + if err != nil { + return err + } + if err := u.Update(sp, resources); err != nil { + return err + } + } + } + return nil +} + +// Processes returns the processes running inside the cgroup along +// with the subsystem used, pid, and path +func (c *cgroup) Processes(subsystem Name, recursive bool) ([]Process, error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return nil, c.err + } + return c.processes(subsystem, recursive) +} + +func (c *cgroup) processes(subsystem Name, recursive bool) ([]Process, error) { + s := c.getSubsystem(subsystem) + sp, err := c.path(subsystem) + if err != nil { + return nil, err + } + path := s.(pather).Path(sp) + var processes []Process + err = filepath.Walk(path, func(p string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !recursive && info.IsDir() { + if p == path { + return nil + } + return filepath.SkipDir + } + dir, name := filepath.Split(p) + if name != cgroupProcs { + return nil + } + procs, err := readPids(dir, subsystem) + if err != nil { + return err + } + processes = append(processes, procs...) + return nil + }) + return processes, err +} + +// Tasks returns the tasks running inside the cgroup along +// with the subsystem used, pid, and path +func (c *cgroup) Tasks(subsystem Name, recursive bool) ([]Task, error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return nil, c.err + } + return c.tasks(subsystem, recursive) +} + +func (c *cgroup) tasks(subsystem Name, recursive bool) ([]Task, error) { + s := c.getSubsystem(subsystem) + sp, err := c.path(subsystem) + if err != nil { + return nil, err + } + path := s.(pather).Path(sp) + var tasks []Task + err = filepath.Walk(path, func(p string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !recursive && info.IsDir() { + if p == path { + return nil + } + return filepath.SkipDir + } + dir, name := filepath.Split(p) + if name != cgroupTasks { + return nil + } + procs, err := readTasksPids(dir, subsystem) + if err != nil { + return err + } + tasks = append(tasks, procs...) + return nil + }) + return tasks, err +} + +// Freeze freezes the entire cgroup and all the processes inside it +func (c *cgroup) Freeze() error { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return c.err + } + s := c.getSubsystem(Freezer) + if s == nil { + return ErrFreezerNotSupported + } + sp, err := c.path(Freezer) + if err != nil { + return err + } + return s.(*freezerController).Freeze(sp) +} + +// Thaw thaws out the cgroup and all the processes inside it +func (c *cgroup) Thaw() error { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return c.err + } + s := c.getSubsystem(Freezer) + if s == nil { + return ErrFreezerNotSupported + } + sp, err := c.path(Freezer) + if err != nil { + return err + } + return s.(*freezerController).Thaw(sp) +} + +// OOMEventFD returns the memory cgroup's out of memory event fd that triggers +// when processes inside the cgroup receive an oom event. Returns +// ErrMemoryNotSupported if memory cgroups is not supported. +func (c *cgroup) OOMEventFD() (uintptr, error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return 0, c.err + } + s := c.getSubsystem(Memory) + if s == nil { + return 0, ErrMemoryNotSupported + } + sp, err := c.path(Memory) + if err != nil { + return 0, err + } + return s.(*memoryController).OOMEventFD(sp) +} + +// State returns the state of the cgroup and its processes +func (c *cgroup) State() State { + c.mu.Lock() + defer c.mu.Unlock() + c.checkExists() + if c.err != nil && c.err == ErrCgroupDeleted { + return Deleted + } + s := c.getSubsystem(Freezer) + if s == nil { + return Thawed + } + sp, err := c.path(Freezer) + if err != nil { + return Unknown + } + state, err := s.(*freezerController).state(sp) + if err != nil { + return Unknown + } + return state +} + +// MoveTo does a recursive move subsystem by subsystem of all the processes +// inside the group +func (c *cgroup) MoveTo(destination Cgroup) error { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return c.err + } + for _, s := range c.subsystems { + processes, err := c.processes(s.Name(), true) + if err != nil { + return err + } + for _, p := range processes { + if err := destination.Add(p); err != nil { + if strings.Contains(err.Error(), "no such process") { + continue + } + return err + } + } + } + return nil +} + +func (c *cgroup) getSubsystem(n Name) Subsystem { + for _, s := range c.subsystems { + if s.Name() == n { + return s + } + } + return nil +} + +func (c *cgroup) checkExists() { + for _, s := range pathers(c.subsystems) { + p, err := c.path(s.Name()) + if err != nil { + return + } + if _, err := os.Lstat(s.Path(p)); err != nil { + if os.IsNotExist(err) { + c.err = ErrCgroupDeleted + return + } + } + } +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/control.go b/src/runtime/vendor/github.com/containerd/cgroups/control.go new file mode 100644 index 000000000..1f62c54f3 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/control.go @@ -0,0 +1,88 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "os" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +const ( + cgroupProcs = "cgroup.procs" + cgroupTasks = "tasks" + defaultDirPerm = 0755 +) + +// defaultFilePerm is a var so that the test framework can change the filemode +// of all files created when the tests are running. The difference between the +// tests and real world use is that files like "cgroup.procs" will exist when writing +// to a read cgroup filesystem and do not exist prior when running in the tests. +// this is set to a non 0 value in the test code +var defaultFilePerm = os.FileMode(0) + +type Process struct { + // Subsystem is the name of the subsystem that the process is in + Subsystem Name + // Pid is the process id of the process + Pid int + // Path is the full path of the subsystem and location that the process is in + Path string +} + +type Task struct { + // Subsystem is the name of the subsystem that the task is in + Subsystem Name + // Pid is the process id of the task + Pid int + // Path is the full path of the subsystem and location that the task is in + Path string +} + +// Cgroup handles interactions with the individual groups to perform +// actions on them as them main interface to this cgroup package +type Cgroup interface { + // New creates a new cgroup under the calling cgroup + New(string, *specs.LinuxResources) (Cgroup, error) + // Add adds a process to the cgroup (cgroup.procs) + Add(Process) error + // AddTask adds a process to the cgroup (tasks) + AddTask(Process) error + // Delete removes the cgroup as a whole + Delete() error + // MoveTo moves all the processes under the calling cgroup to the provided one + // subsystems are moved one at a time + MoveTo(Cgroup) error + // Stat returns the stats for all subsystems in the cgroup + Stat(...ErrorHandler) (*Metrics, error) + // Update updates all the subsystems with the provided resource changes + Update(resources *specs.LinuxResources) error + // Processes returns all the processes in a select subsystem for the cgroup + Processes(Name, bool) ([]Process, error) + // Tasks returns all the tasks in a select subsystem for the cgroup + Tasks(Name, bool) ([]Task, error) + // Freeze freezes or pauses all processes inside the cgroup + Freeze() error + // Thaw thaw or resumes all processes inside the cgroup + Thaw() error + // OOMEventFD returns the memory subsystem's event fd for OOM events + OOMEventFD() (uintptr, error) + // State returns the cgroups current state + State() State + // Subsystems returns all the subsystems in the cgroup + Subsystems() []Subsystem +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/cpu.go b/src/runtime/vendor/github.com/containerd/cgroups/cpu.go new file mode 100644 index 000000000..431cd3e51 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/cpu.go @@ -0,0 +1,129 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func NewCpu(root string) *cpuController { + return &cpuController{ + root: filepath.Join(root, string(Cpu)), + } +} + +type cpuController struct { + root string +} + +func (c *cpuController) Name() Name { + return Cpu +} + +func (c *cpuController) Path(path string) string { + return filepath.Join(c.root, path) +} + +func (c *cpuController) Create(path string, resources *specs.LinuxResources) error { + if err := os.MkdirAll(c.Path(path), defaultDirPerm); err != nil { + return err + } + if cpu := resources.CPU; cpu != nil { + for _, t := range []struct { + name string + ivalue *int64 + uvalue *uint64 + }{ + { + name: "rt_period_us", + uvalue: cpu.RealtimePeriod, + }, + { + name: "rt_runtime_us", + ivalue: cpu.RealtimeRuntime, + }, + { + name: "shares", + uvalue: cpu.Shares, + }, + { + name: "cfs_period_us", + uvalue: cpu.Period, + }, + { + name: "cfs_quota_us", + ivalue: cpu.Quota, + }, + } { + var value []byte + if t.uvalue != nil { + value = []byte(strconv.FormatUint(*t.uvalue, 10)) + } else if t.ivalue != nil { + value = []byte(strconv.FormatInt(*t.ivalue, 10)) + } + if value != nil { + if err := ioutil.WriteFile( + filepath.Join(c.Path(path), fmt.Sprintf("cpu.%s", t.name)), + value, + defaultFilePerm, + ); err != nil { + return err + } + } + } + } + return nil +} + +func (c *cpuController) Update(path string, resources *specs.LinuxResources) error { + return c.Create(path, resources) +} + +func (c *cpuController) Stat(path string, stats *Metrics) error { + f, err := os.Open(filepath.Join(c.Path(path), "cpu.stat")) + if err != nil { + return err + } + defer f.Close() + // get or create the cpu field because cpuacct can also set values on this struct + sc := bufio.NewScanner(f) + for sc.Scan() { + if err := sc.Err(); err != nil { + return err + } + key, v, err := parseKV(sc.Text()) + if err != nil { + return err + } + switch key { + case "nr_periods": + stats.CPU.Throttling.Periods = v + case "nr_throttled": + stats.CPU.Throttling.ThrottledPeriods = v + case "throttled_time": + stats.CPU.Throttling.ThrottledTime = v + } + } + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/cpuacct.go b/src/runtime/vendor/github.com/containerd/cgroups/cpuacct.go new file mode 100644 index 000000000..42a490a87 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/cpuacct.go @@ -0,0 +1,121 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "fmt" + "io/ioutil" + "path/filepath" + "strconv" + "strings" +) + +const nanosecondsInSecond = 1000000000 + +var clockTicks = getClockTicks() + +func NewCpuacct(root string) *cpuacctController { + return &cpuacctController{ + root: filepath.Join(root, string(Cpuacct)), + } +} + +type cpuacctController struct { + root string +} + +func (c *cpuacctController) Name() Name { + return Cpuacct +} + +func (c *cpuacctController) Path(path string) string { + return filepath.Join(c.root, path) +} + +func (c *cpuacctController) Stat(path string, stats *Metrics) error { + user, kernel, err := c.getUsage(path) + if err != nil { + return err + } + total, err := readUint(filepath.Join(c.Path(path), "cpuacct.usage")) + if err != nil { + return err + } + percpu, err := c.percpuUsage(path) + if err != nil { + return err + } + stats.CPU.Usage.Total = total + stats.CPU.Usage.User = user + stats.CPU.Usage.Kernel = kernel + stats.CPU.Usage.PerCPU = percpu + return nil +} + +func (c *cpuacctController) percpuUsage(path string) ([]uint64, error) { + var usage []uint64 + data, err := ioutil.ReadFile(filepath.Join(c.Path(path), "cpuacct.usage_percpu")) + if err != nil { + return nil, err + } + for _, v := range strings.Fields(string(data)) { + u, err := strconv.ParseUint(v, 10, 64) + if err != nil { + return nil, err + } + usage = append(usage, u) + } + return usage, nil +} + +func (c *cpuacctController) getUsage(path string) (user uint64, kernel uint64, err error) { + statPath := filepath.Join(c.Path(path), "cpuacct.stat") + data, err := ioutil.ReadFile(statPath) + if err != nil { + return 0, 0, err + } + fields := strings.Fields(string(data)) + if len(fields) != 4 { + return 0, 0, fmt.Errorf("%q is expected to have 4 fields", statPath) + } + for _, t := range []struct { + index int + name string + value *uint64 + }{ + { + index: 0, + name: "user", + value: &user, + }, + { + index: 2, + name: "system", + value: &kernel, + }, + } { + if fields[t.index] != t.name { + return 0, 0, fmt.Errorf("expected field %q but found %q in %q", t.name, fields[t.index], statPath) + } + v, err := strconv.ParseUint(fields[t.index+1], 10, 64) + if err != nil { + return 0, 0, err + } + *t.value = v + } + return (user * nanosecondsInSecond) / clockTicks, (kernel * nanosecondsInSecond) / clockTicks, nil +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/cpuset.go b/src/runtime/vendor/github.com/containerd/cgroups/cpuset.go new file mode 100644 index 000000000..30208515e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/cpuset.go @@ -0,0 +1,159 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func NewCputset(root string) *cpusetController { + return &cpusetController{ + root: filepath.Join(root, string(Cpuset)), + } +} + +type cpusetController struct { + root string +} + +func (c *cpusetController) Name() Name { + return Cpuset +} + +func (c *cpusetController) Path(path string) string { + return filepath.Join(c.root, path) +} + +func (c *cpusetController) Create(path string, resources *specs.LinuxResources) error { + if err := c.ensureParent(c.Path(path), c.root); err != nil { + return err + } + if err := os.MkdirAll(c.Path(path), defaultDirPerm); err != nil { + return err + } + if err := c.copyIfNeeded(c.Path(path), filepath.Dir(c.Path(path))); err != nil { + return err + } + if resources.CPU != nil { + for _, t := range []struct { + name string + value string + }{ + { + name: "cpus", + value: resources.CPU.Cpus, + }, + { + name: "mems", + value: resources.CPU.Mems, + }, + } { + if t.value != "" { + if err := ioutil.WriteFile( + filepath.Join(c.Path(path), fmt.Sprintf("cpuset.%s", t.name)), + []byte(t.value), + defaultFilePerm, + ); err != nil { + return err + } + } + } + } + return nil +} + +func (c *cpusetController) Update(path string, resources *specs.LinuxResources) error { + return c.Create(path, resources) +} + +func (c *cpusetController) getValues(path string) (cpus []byte, mems []byte, err error) { + if cpus, err = ioutil.ReadFile(filepath.Join(path, "cpuset.cpus")); err != nil && !os.IsNotExist(err) { + return + } + if mems, err = ioutil.ReadFile(filepath.Join(path, "cpuset.mems")); err != nil && !os.IsNotExist(err) { + return + } + return cpus, mems, nil +} + +// ensureParent makes sure that the parent directory of current is created +// and populated with the proper cpus and mems files copied from +// it's parent. +func (c *cpusetController) ensureParent(current, root string) error { + parent := filepath.Dir(current) + if _, err := filepath.Rel(root, parent); err != nil { + return nil + } + // Avoid infinite recursion. + if parent == current { + return fmt.Errorf("cpuset: cgroup parent path outside cgroup root") + } + if cleanPath(parent) != root { + if err := c.ensureParent(parent, root); err != nil { + return err + } + } + if err := os.MkdirAll(current, defaultDirPerm); err != nil { + return err + } + return c.copyIfNeeded(current, parent) +} + +// copyIfNeeded copies the cpuset.cpus and cpuset.mems from the parent +// directory to the current directory if the file's contents are 0 +func (c *cpusetController) copyIfNeeded(current, parent string) error { + var ( + err error + currentCpus, currentMems []byte + parentCpus, parentMems []byte + ) + if currentCpus, currentMems, err = c.getValues(current); err != nil { + return err + } + if parentCpus, parentMems, err = c.getValues(parent); err != nil { + return err + } + if isEmpty(currentCpus) { + if err := ioutil.WriteFile( + filepath.Join(current, "cpuset.cpus"), + parentCpus, + defaultFilePerm, + ); err != nil { + return err + } + } + if isEmpty(currentMems) { + if err := ioutil.WriteFile( + filepath.Join(current, "cpuset.mems"), + parentMems, + defaultFilePerm, + ); err != nil { + return err + } + } + return nil +} + +func isEmpty(b []byte) bool { + return len(bytes.Trim(b, "\n")) == 0 +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/devices.go b/src/runtime/vendor/github.com/containerd/cgroups/devices.go new file mode 100644 index 000000000..f6a3b1947 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/devices.go @@ -0,0 +1,93 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +const ( + allowDeviceFile = "devices.allow" + denyDeviceFile = "devices.deny" + wildcard = -1 +) + +func NewDevices(root string) *devicesController { + return &devicesController{ + root: filepath.Join(root, string(Devices)), + } +} + +type devicesController struct { + root string +} + +func (d *devicesController) Name() Name { + return Devices +} + +func (d *devicesController) Path(path string) string { + return filepath.Join(d.root, path) +} + +func (d *devicesController) Create(path string, resources *specs.LinuxResources) error { + if err := os.MkdirAll(d.Path(path), defaultDirPerm); err != nil { + return err + } + for _, device := range resources.Devices { + file := denyDeviceFile + if device.Allow { + file = allowDeviceFile + } + if device.Type == "" { + device.Type = "a" + } + if err := ioutil.WriteFile( + filepath.Join(d.Path(path), file), + []byte(deviceString(device)), + defaultFilePerm, + ); err != nil { + return err + } + } + return nil +} + +func (d *devicesController) Update(path string, resources *specs.LinuxResources) error { + return d.Create(path, resources) +} + +func deviceString(device specs.LinuxDeviceCgroup) string { + return fmt.Sprintf("%s %s:%s %s", + device.Type, + deviceNumber(device.Major), + deviceNumber(device.Minor), + device.Access, + ) +} + +func deviceNumber(number *int64) string { + if number == nil || *number == wildcard { + return "*" + } + return fmt.Sprint(*number) +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/errors.go b/src/runtime/vendor/github.com/containerd/cgroups/errors.go new file mode 100644 index 000000000..f1ad8315c --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/errors.go @@ -0,0 +1,47 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "errors" + "os" +) + +var ( + ErrInvalidPid = errors.New("cgroups: pid must be greater than 0") + ErrMountPointNotExist = errors.New("cgroups: cgroup mountpoint does not exist") + ErrInvalidFormat = errors.New("cgroups: parsing file with invalid format failed") + ErrFreezerNotSupported = errors.New("cgroups: freezer cgroup not supported on this system") + ErrMemoryNotSupported = errors.New("cgroups: memory cgroup not supported on this system") + ErrCgroupDeleted = errors.New("cgroups: cgroup deleted") + ErrNoCgroupMountDestination = errors.New("cgroups: cannot find cgroup mount destination") +) + +// ErrorHandler is a function that handles and acts on errors +type ErrorHandler func(err error) error + +// IgnoreNotExist ignores any errors that are for not existing files +func IgnoreNotExist(err error) error { + if os.IsNotExist(err) { + return nil + } + return err +} + +func errPassthrough(err error) error { + return err +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/freezer.go b/src/runtime/vendor/github.com/containerd/cgroups/freezer.go new file mode 100644 index 000000000..5e668408a --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/freezer.go @@ -0,0 +1,82 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "io/ioutil" + "path/filepath" + "strings" + "time" +) + +func NewFreezer(root string) *freezerController { + return &freezerController{ + root: filepath.Join(root, string(Freezer)), + } +} + +type freezerController struct { + root string +} + +func (f *freezerController) Name() Name { + return Freezer +} + +func (f *freezerController) Path(path string) string { + return filepath.Join(f.root, path) +} + +func (f *freezerController) Freeze(path string) error { + return f.waitState(path, Frozen) +} + +func (f *freezerController) Thaw(path string) error { + return f.waitState(path, Thawed) +} + +func (f *freezerController) changeState(path string, state State) error { + return ioutil.WriteFile( + filepath.Join(f.root, path, "freezer.state"), + []byte(strings.ToUpper(string(state))), + defaultFilePerm, + ) +} + +func (f *freezerController) state(path string) (State, error) { + current, err := ioutil.ReadFile(filepath.Join(f.root, path, "freezer.state")) + if err != nil { + return "", err + } + return State(strings.ToLower(strings.TrimSpace(string(current)))), nil +} + +func (f *freezerController) waitState(path string, state State) error { + for { + if err := f.changeState(path, state); err != nil { + return err + } + current, err := f.state(path) + if err != nil { + return err + } + if current == state { + return nil + } + time.Sleep(1 * time.Millisecond) + } +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/hierarchy.go b/src/runtime/vendor/github.com/containerd/cgroups/hierarchy.go new file mode 100644 index 000000000..9221bf3f1 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/hierarchy.go @@ -0,0 +1,20 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +// Hierarchy enableds both unified and split hierarchy for cgroups +type Hierarchy func() ([]Subsystem, error) diff --git a/src/runtime/vendor/github.com/containerd/cgroups/hugetlb.go b/src/runtime/vendor/github.com/containerd/cgroups/hugetlb.go new file mode 100644 index 000000000..3718706d7 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/hugetlb.go @@ -0,0 +1,109 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func NewHugetlb(root string) (*hugetlbController, error) { + sizes, err := hugePageSizes() + if err != nil { + return nil, err + } + + return &hugetlbController{ + root: filepath.Join(root, string(Hugetlb)), + sizes: sizes, + }, nil +} + +type hugetlbController struct { + root string + sizes []string +} + +func (h *hugetlbController) Name() Name { + return Hugetlb +} + +func (h *hugetlbController) Path(path string) string { + return filepath.Join(h.root, path) +} + +func (h *hugetlbController) Create(path string, resources *specs.LinuxResources) error { + if err := os.MkdirAll(h.Path(path), defaultDirPerm); err != nil { + return err + } + for _, limit := range resources.HugepageLimits { + if err := ioutil.WriteFile( + filepath.Join(h.Path(path), strings.Join([]string{"hugetlb", limit.Pagesize, "limit_in_bytes"}, ".")), + []byte(strconv.FormatUint(limit.Limit, 10)), + defaultFilePerm, + ); err != nil { + return err + } + } + return nil +} + +func (h *hugetlbController) Stat(path string, stats *Metrics) error { + for _, size := range h.sizes { + s, err := h.readSizeStat(path, size) + if err != nil { + return err + } + stats.Hugetlb = append(stats.Hugetlb, s) + } + return nil +} + +func (h *hugetlbController) readSizeStat(path, size string) (*HugetlbStat, error) { + s := HugetlbStat{ + Pagesize: size, + } + for _, t := range []struct { + name string + value *uint64 + }{ + { + name: "usage_in_bytes", + value: &s.Usage, + }, + { + name: "max_usage_in_bytes", + value: &s.Max, + }, + { + name: "failcnt", + value: &s.Failcnt, + }, + } { + v, err := readUint(filepath.Join(h.Path(path), strings.Join([]string{"hugetlb", size, t.name}, "."))) + if err != nil { + return nil, err + } + *t.value = v + } + return &s, nil +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/memory.go b/src/runtime/vendor/github.com/containerd/cgroups/memory.go new file mode 100644 index 000000000..b8b5fe7ea --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/memory.go @@ -0,0 +1,329 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + + "golang.org/x/sys/unix" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func NewMemory(root string) *memoryController { + return &memoryController{ + root: filepath.Join(root, string(Memory)), + } +} + +type memoryController struct { + root string +} + +func (m *memoryController) Name() Name { + return Memory +} + +func (m *memoryController) Path(path string) string { + return filepath.Join(m.root, path) +} + +func (m *memoryController) Create(path string, resources *specs.LinuxResources) error { + if err := os.MkdirAll(m.Path(path), defaultDirPerm); err != nil { + return err + } + if resources.Memory == nil { + return nil + } + if resources.Memory.Kernel != nil { + // Check if kernel memory is enabled + // We have to limit the kernel memory here as it won't be accounted at all + // until a limit is set on the cgroup and limit cannot be set once the + // cgroup has children, or if there are already tasks in the cgroup. + for _, i := range []int64{1, -1} { + if err := ioutil.WriteFile( + filepath.Join(m.Path(path), "memory.kmem.limit_in_bytes"), + []byte(strconv.FormatInt(i, 10)), + defaultFilePerm, + ); err != nil { + return checkEBUSY(err) + } + } + } + return m.set(path, getMemorySettings(resources)) +} + +func (m *memoryController) Update(path string, resources *specs.LinuxResources) error { + if resources.Memory == nil { + return nil + } + g := func(v *int64) bool { + return v != nil && *v > 0 + } + settings := getMemorySettings(resources) + if g(resources.Memory.Limit) && g(resources.Memory.Swap) { + // if the updated swap value is larger than the current memory limit set the swap changes first + // then set the memory limit as swap must always be larger than the current limit + current, err := readUint(filepath.Join(m.Path(path), "memory.limit_in_bytes")) + if err != nil { + return err + } + if current < uint64(*resources.Memory.Swap) { + settings[0], settings[1] = settings[1], settings[0] + } + } + return m.set(path, settings) +} + +func (m *memoryController) Stat(path string, stats *Metrics) error { + f, err := os.Open(filepath.Join(m.Path(path), "memory.stat")) + if err != nil { + return err + } + defer f.Close() + stats.Memory = &MemoryStat{ + Usage: &MemoryEntry{}, + Swap: &MemoryEntry{}, + Kernel: &MemoryEntry{}, + KernelTCP: &MemoryEntry{}, + } + if err := m.parseStats(f, stats.Memory); err != nil { + return err + } + for _, t := range []struct { + module string + entry *MemoryEntry + }{ + { + module: "", + entry: stats.Memory.Usage, + }, + { + module: "memsw", + entry: stats.Memory.Swap, + }, + { + module: "kmem", + entry: stats.Memory.Kernel, + }, + { + module: "kmem.tcp", + entry: stats.Memory.KernelTCP, + }, + } { + for _, tt := range []struct { + name string + value *uint64 + }{ + { + name: "usage_in_bytes", + value: &t.entry.Usage, + }, + { + name: "max_usage_in_bytes", + value: &t.entry.Max, + }, + { + name: "failcnt", + value: &t.entry.Failcnt, + }, + { + name: "limit_in_bytes", + value: &t.entry.Limit, + }, + } { + parts := []string{"memory"} + if t.module != "" { + parts = append(parts, t.module) + } + parts = append(parts, tt.name) + v, err := readUint(filepath.Join(m.Path(path), strings.Join(parts, "."))) + if err != nil { + return err + } + *tt.value = v + } + } + return nil +} + +func (m *memoryController) OOMEventFD(path string) (uintptr, error) { + root := m.Path(path) + f, err := os.Open(filepath.Join(root, "memory.oom_control")) + if err != nil { + return 0, err + } + defer f.Close() + fd, _, serr := unix.RawSyscall(unix.SYS_EVENTFD2, 0, unix.EFD_CLOEXEC, 0) + if serr != 0 { + return 0, serr + } + if err := writeEventFD(root, f.Fd(), fd); err != nil { + unix.Close(int(fd)) + return 0, err + } + return fd, nil +} + +func writeEventFD(root string, cfd, efd uintptr) error { + f, err := os.OpenFile(filepath.Join(root, "cgroup.event_control"), os.O_WRONLY, 0) + if err != nil { + return err + } + _, err = f.WriteString(fmt.Sprintf("%d %d", efd, cfd)) + f.Close() + return err +} + +func (m *memoryController) parseStats(r io.Reader, stat *MemoryStat) error { + var ( + raw = make(map[string]uint64) + sc = bufio.NewScanner(r) + line int + ) + for sc.Scan() { + if err := sc.Err(); err != nil { + return err + } + key, v, err := parseKV(sc.Text()) + if err != nil { + return fmt.Errorf("%d: %v", line, err) + } + raw[key] = v + line++ + } + stat.Cache = raw["cache"] + stat.RSS = raw["rss"] + stat.RSSHuge = raw["rss_huge"] + stat.MappedFile = raw["mapped_file"] + stat.Dirty = raw["dirty"] + stat.Writeback = raw["writeback"] + stat.PgPgIn = raw["pgpgin"] + stat.PgPgOut = raw["pgpgout"] + stat.PgFault = raw["pgfault"] + stat.PgMajFault = raw["pgmajfault"] + stat.InactiveAnon = raw["inactive_anon"] + stat.ActiveAnon = raw["active_anon"] + stat.InactiveFile = raw["inactive_file"] + stat.ActiveFile = raw["active_file"] + stat.Unevictable = raw["unevictable"] + stat.HierarchicalMemoryLimit = raw["hierarchical_memory_limit"] + stat.HierarchicalSwapLimit = raw["hierarchical_memsw_limit"] + stat.TotalCache = raw["total_cache"] + stat.TotalRSS = raw["total_rss"] + stat.TotalRSSHuge = raw["total_rss_huge"] + stat.TotalMappedFile = raw["total_mapped_file"] + stat.TotalDirty = raw["total_dirty"] + stat.TotalWriteback = raw["total_writeback"] + stat.TotalPgPgIn = raw["total_pgpgin"] + stat.TotalPgPgOut = raw["total_pgpgout"] + stat.TotalPgFault = raw["total_pgfault"] + stat.TotalPgMajFault = raw["total_pgmajfault"] + stat.TotalInactiveAnon = raw["total_inactive_anon"] + stat.TotalActiveAnon = raw["total_active_anon"] + stat.TotalInactiveFile = raw["total_inactive_file"] + stat.TotalActiveFile = raw["total_active_file"] + stat.TotalUnevictable = raw["total_unevictable"] + return nil +} + +func (m *memoryController) set(path string, settings []memorySettings) error { + for _, t := range settings { + if t.value != nil { + if err := ioutil.WriteFile( + filepath.Join(m.Path(path), fmt.Sprintf("memory.%s", t.name)), + []byte(strconv.FormatInt(*t.value, 10)), + defaultFilePerm, + ); err != nil { + return err + } + } + } + return nil +} + +type memorySettings struct { + name string + value *int64 +} + +func getMemorySettings(resources *specs.LinuxResources) []memorySettings { + mem := resources.Memory + var swappiness *int64 + if mem.Swappiness != nil { + v := int64(*mem.Swappiness) + swappiness = &v + } + return []memorySettings{ + { + name: "limit_in_bytes", + value: mem.Limit, + }, + { + name: "soft_limit_in_bytes", + value: mem.Reservation, + }, + { + name: "memsw.limit_in_bytes", + value: mem.Swap, + }, + { + name: "kmem.limit_in_bytes", + value: mem.Kernel, + }, + { + name: "kmem.tcp.limit_in_bytes", + value: mem.KernelTCP, + }, + { + name: "oom_control", + value: getOomControlValue(mem), + }, + { + name: "swappiness", + value: swappiness, + }, + } +} + +func checkEBUSY(err error) error { + if pathErr, ok := err.(*os.PathError); ok { + if errNo, ok := pathErr.Err.(syscall.Errno); ok { + if errNo == unix.EBUSY { + return fmt.Errorf( + "failed to set memory.kmem.limit_in_bytes, because either tasks have already joined this cgroup or it has children") + } + } + } + return err +} + +func getOomControlValue(mem *specs.LinuxMemory) *int64 { + if mem.DisableOOMKiller != nil && *mem.DisableOOMKiller { + i := int64(1) + return &i + } + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/metrics.pb.go b/src/runtime/vendor/github.com/containerd/cgroups/metrics.pb.go new file mode 100644 index 000000000..7dd7f6f3c --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/metrics.pb.go @@ -0,0 +1,4696 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/cgroups/metrics.proto + +/* + Package cgroups is a generated protocol buffer package. + + It is generated from these files: + github.com/containerd/cgroups/metrics.proto + + It has these top-level messages: + Metrics + HugetlbStat + PidsStat + CPUStat + CPUUsage + Throttle + MemoryStat + MemoryEntry + BlkIOStat + BlkIOEntry + RdmaStat + RdmaEntry + NetworkStat +*/ +package cgroups + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type Metrics struct { + Hugetlb []*HugetlbStat `protobuf:"bytes,1,rep,name=hugetlb" json:"hugetlb,omitempty"` + Pids *PidsStat `protobuf:"bytes,2,opt,name=pids" json:"pids,omitempty"` + CPU *CPUStat `protobuf:"bytes,3,opt,name=cpu" json:"cpu,omitempty"` + Memory *MemoryStat `protobuf:"bytes,4,opt,name=memory" json:"memory,omitempty"` + Blkio *BlkIOStat `protobuf:"bytes,5,opt,name=blkio" json:"blkio,omitempty"` + Rdma *RdmaStat `protobuf:"bytes,6,opt,name=rdma" json:"rdma,omitempty"` + Network []*NetworkStat `protobuf:"bytes,7,rep,name=network" json:"network,omitempty"` +} + +func (m *Metrics) Reset() { *m = Metrics{} } +func (*Metrics) ProtoMessage() {} +func (*Metrics) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{0} } + +type HugetlbStat struct { + Usage uint64 `protobuf:"varint,1,opt,name=usage,proto3" json:"usage,omitempty"` + Max uint64 `protobuf:"varint,2,opt,name=max,proto3" json:"max,omitempty"` + Failcnt uint64 `protobuf:"varint,3,opt,name=failcnt,proto3" json:"failcnt,omitempty"` + Pagesize string `protobuf:"bytes,4,opt,name=pagesize,proto3" json:"pagesize,omitempty"` +} + +func (m *HugetlbStat) Reset() { *m = HugetlbStat{} } +func (*HugetlbStat) ProtoMessage() {} +func (*HugetlbStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{1} } + +type PidsStat struct { + Current uint64 `protobuf:"varint,1,opt,name=current,proto3" json:"current,omitempty"` + Limit uint64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` +} + +func (m *PidsStat) Reset() { *m = PidsStat{} } +func (*PidsStat) ProtoMessage() {} +func (*PidsStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{2} } + +type CPUStat struct { + Usage *CPUUsage `protobuf:"bytes,1,opt,name=usage" json:"usage,omitempty"` + Throttling *Throttle `protobuf:"bytes,2,opt,name=throttling" json:"throttling,omitempty"` +} + +func (m *CPUStat) Reset() { *m = CPUStat{} } +func (*CPUStat) ProtoMessage() {} +func (*CPUStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{3} } + +type CPUUsage struct { + // values in nanoseconds + Total uint64 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"` + Kernel uint64 `protobuf:"varint,2,opt,name=kernel,proto3" json:"kernel,omitempty"` + User uint64 `protobuf:"varint,3,opt,name=user,proto3" json:"user,omitempty"` + PerCPU []uint64 `protobuf:"varint,4,rep,packed,name=per_cpu,json=perCpu" json:"per_cpu,omitempty"` +} + +func (m *CPUUsage) Reset() { *m = CPUUsage{} } +func (*CPUUsage) ProtoMessage() {} +func (*CPUUsage) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{4} } + +type Throttle struct { + Periods uint64 `protobuf:"varint,1,opt,name=periods,proto3" json:"periods,omitempty"` + ThrottledPeriods uint64 `protobuf:"varint,2,opt,name=throttled_periods,json=throttledPeriods,proto3" json:"throttled_periods,omitempty"` + ThrottledTime uint64 `protobuf:"varint,3,opt,name=throttled_time,json=throttledTime,proto3" json:"throttled_time,omitempty"` +} + +func (m *Throttle) Reset() { *m = Throttle{} } +func (*Throttle) ProtoMessage() {} +func (*Throttle) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{5} } + +type MemoryStat struct { + Cache uint64 `protobuf:"varint,1,opt,name=cache,proto3" json:"cache,omitempty"` + RSS uint64 `protobuf:"varint,2,opt,name=rss,proto3" json:"rss,omitempty"` + RSSHuge uint64 `protobuf:"varint,3,opt,name=rss_huge,json=rssHuge,proto3" json:"rss_huge,omitempty"` + MappedFile uint64 `protobuf:"varint,4,opt,name=mapped_file,json=mappedFile,proto3" json:"mapped_file,omitempty"` + Dirty uint64 `protobuf:"varint,5,opt,name=dirty,proto3" json:"dirty,omitempty"` + Writeback uint64 `protobuf:"varint,6,opt,name=writeback,proto3" json:"writeback,omitempty"` + PgPgIn uint64 `protobuf:"varint,7,opt,name=pg_pg_in,json=pgPgIn,proto3" json:"pg_pg_in,omitempty"` + PgPgOut uint64 `protobuf:"varint,8,opt,name=pg_pg_out,json=pgPgOut,proto3" json:"pg_pg_out,omitempty"` + PgFault uint64 `protobuf:"varint,9,opt,name=pg_fault,json=pgFault,proto3" json:"pg_fault,omitempty"` + PgMajFault uint64 `protobuf:"varint,10,opt,name=pg_maj_fault,json=pgMajFault,proto3" json:"pg_maj_fault,omitempty"` + InactiveAnon uint64 `protobuf:"varint,11,opt,name=inactive_anon,json=inactiveAnon,proto3" json:"inactive_anon,omitempty"` + ActiveAnon uint64 `protobuf:"varint,12,opt,name=active_anon,json=activeAnon,proto3" json:"active_anon,omitempty"` + InactiveFile uint64 `protobuf:"varint,13,opt,name=inactive_file,json=inactiveFile,proto3" json:"inactive_file,omitempty"` + ActiveFile uint64 `protobuf:"varint,14,opt,name=active_file,json=activeFile,proto3" json:"active_file,omitempty"` + Unevictable uint64 `protobuf:"varint,15,opt,name=unevictable,proto3" json:"unevictable,omitempty"` + HierarchicalMemoryLimit uint64 `protobuf:"varint,16,opt,name=hierarchical_memory_limit,json=hierarchicalMemoryLimit,proto3" json:"hierarchical_memory_limit,omitempty"` + HierarchicalSwapLimit uint64 `protobuf:"varint,17,opt,name=hierarchical_swap_limit,json=hierarchicalSwapLimit,proto3" json:"hierarchical_swap_limit,omitempty"` + TotalCache uint64 `protobuf:"varint,18,opt,name=total_cache,json=totalCache,proto3" json:"total_cache,omitempty"` + TotalRSS uint64 `protobuf:"varint,19,opt,name=total_rss,json=totalRss,proto3" json:"total_rss,omitempty"` + TotalRSSHuge uint64 `protobuf:"varint,20,opt,name=total_rss_huge,json=totalRssHuge,proto3" json:"total_rss_huge,omitempty"` + TotalMappedFile uint64 `protobuf:"varint,21,opt,name=total_mapped_file,json=totalMappedFile,proto3" json:"total_mapped_file,omitempty"` + TotalDirty uint64 `protobuf:"varint,22,opt,name=total_dirty,json=totalDirty,proto3" json:"total_dirty,omitempty"` + TotalWriteback uint64 `protobuf:"varint,23,opt,name=total_writeback,json=totalWriteback,proto3" json:"total_writeback,omitempty"` + TotalPgPgIn uint64 `protobuf:"varint,24,opt,name=total_pg_pg_in,json=totalPgPgIn,proto3" json:"total_pg_pg_in,omitempty"` + TotalPgPgOut uint64 `protobuf:"varint,25,opt,name=total_pg_pg_out,json=totalPgPgOut,proto3" json:"total_pg_pg_out,omitempty"` + TotalPgFault uint64 `protobuf:"varint,26,opt,name=total_pg_fault,json=totalPgFault,proto3" json:"total_pg_fault,omitempty"` + TotalPgMajFault uint64 `protobuf:"varint,27,opt,name=total_pg_maj_fault,json=totalPgMajFault,proto3" json:"total_pg_maj_fault,omitempty"` + TotalInactiveAnon uint64 `protobuf:"varint,28,opt,name=total_inactive_anon,json=totalInactiveAnon,proto3" json:"total_inactive_anon,omitempty"` + TotalActiveAnon uint64 `protobuf:"varint,29,opt,name=total_active_anon,json=totalActiveAnon,proto3" json:"total_active_anon,omitempty"` + TotalInactiveFile uint64 `protobuf:"varint,30,opt,name=total_inactive_file,json=totalInactiveFile,proto3" json:"total_inactive_file,omitempty"` + TotalActiveFile uint64 `protobuf:"varint,31,opt,name=total_active_file,json=totalActiveFile,proto3" json:"total_active_file,omitempty"` + TotalUnevictable uint64 `protobuf:"varint,32,opt,name=total_unevictable,json=totalUnevictable,proto3" json:"total_unevictable,omitempty"` + Usage *MemoryEntry `protobuf:"bytes,33,opt,name=usage" json:"usage,omitempty"` + Swap *MemoryEntry `protobuf:"bytes,34,opt,name=swap" json:"swap,omitempty"` + Kernel *MemoryEntry `protobuf:"bytes,35,opt,name=kernel" json:"kernel,omitempty"` + KernelTCP *MemoryEntry `protobuf:"bytes,36,opt,name=kernel_tcp,json=kernelTcp" json:"kernel_tcp,omitempty"` +} + +func (m *MemoryStat) Reset() { *m = MemoryStat{} } +func (*MemoryStat) ProtoMessage() {} +func (*MemoryStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{6} } + +type MemoryEntry struct { + Limit uint64 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` + Usage uint64 `protobuf:"varint,2,opt,name=usage,proto3" json:"usage,omitempty"` + Max uint64 `protobuf:"varint,3,opt,name=max,proto3" json:"max,omitempty"` + Failcnt uint64 `protobuf:"varint,4,opt,name=failcnt,proto3" json:"failcnt,omitempty"` +} + +func (m *MemoryEntry) Reset() { *m = MemoryEntry{} } +func (*MemoryEntry) ProtoMessage() {} +func (*MemoryEntry) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{7} } + +type BlkIOStat struct { + IoServiceBytesRecursive []*BlkIOEntry `protobuf:"bytes,1,rep,name=io_service_bytes_recursive,json=ioServiceBytesRecursive" json:"io_service_bytes_recursive,omitempty"` + IoServicedRecursive []*BlkIOEntry `protobuf:"bytes,2,rep,name=io_serviced_recursive,json=ioServicedRecursive" json:"io_serviced_recursive,omitempty"` + IoQueuedRecursive []*BlkIOEntry `protobuf:"bytes,3,rep,name=io_queued_recursive,json=ioQueuedRecursive" json:"io_queued_recursive,omitempty"` + IoServiceTimeRecursive []*BlkIOEntry `protobuf:"bytes,4,rep,name=io_service_time_recursive,json=ioServiceTimeRecursive" json:"io_service_time_recursive,omitempty"` + IoWaitTimeRecursive []*BlkIOEntry `protobuf:"bytes,5,rep,name=io_wait_time_recursive,json=ioWaitTimeRecursive" json:"io_wait_time_recursive,omitempty"` + IoMergedRecursive []*BlkIOEntry `protobuf:"bytes,6,rep,name=io_merged_recursive,json=ioMergedRecursive" json:"io_merged_recursive,omitempty"` + IoTimeRecursive []*BlkIOEntry `protobuf:"bytes,7,rep,name=io_time_recursive,json=ioTimeRecursive" json:"io_time_recursive,omitempty"` + SectorsRecursive []*BlkIOEntry `protobuf:"bytes,8,rep,name=sectors_recursive,json=sectorsRecursive" json:"sectors_recursive,omitempty"` +} + +func (m *BlkIOStat) Reset() { *m = BlkIOStat{} } +func (*BlkIOStat) ProtoMessage() {} +func (*BlkIOStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{8} } + +type BlkIOEntry struct { + Op string `protobuf:"bytes,1,opt,name=op,proto3" json:"op,omitempty"` + Device string `protobuf:"bytes,2,opt,name=device,proto3" json:"device,omitempty"` + Major uint64 `protobuf:"varint,3,opt,name=major,proto3" json:"major,omitempty"` + Minor uint64 `protobuf:"varint,4,opt,name=minor,proto3" json:"minor,omitempty"` + Value uint64 `protobuf:"varint,5,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *BlkIOEntry) Reset() { *m = BlkIOEntry{} } +func (*BlkIOEntry) ProtoMessage() {} +func (*BlkIOEntry) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{9} } + +type RdmaStat struct { + Current []*RdmaEntry `protobuf:"bytes,1,rep,name=current" json:"current,omitempty"` + Limit []*RdmaEntry `protobuf:"bytes,2,rep,name=limit" json:"limit,omitempty"` +} + +func (m *RdmaStat) Reset() { *m = RdmaStat{} } +func (*RdmaStat) ProtoMessage() {} +func (*RdmaStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{10} } + +type RdmaEntry struct { + Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"` + HcaHandles uint32 `protobuf:"varint,2,opt,name=hca_handles,json=hcaHandles,proto3" json:"hca_handles,omitempty"` + HcaObjects uint32 `protobuf:"varint,3,opt,name=hca_objects,json=hcaObjects,proto3" json:"hca_objects,omitempty"` +} + +func (m *RdmaEntry) Reset() { *m = RdmaEntry{} } +func (*RdmaEntry) ProtoMessage() {} +func (*RdmaEntry) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{11} } + +type NetworkStat struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + RxBytes uint64 `protobuf:"varint,2,opt,name=rx_bytes,json=rxBytes,proto3" json:"rx_bytes,omitempty"` + RxPackets uint64 `protobuf:"varint,3,opt,name=rx_packets,json=rxPackets,proto3" json:"rx_packets,omitempty"` + RxErrors uint64 `protobuf:"varint,4,opt,name=rx_errors,json=rxErrors,proto3" json:"rx_errors,omitempty"` + RxDropped uint64 `protobuf:"varint,5,opt,name=rx_dropped,json=rxDropped,proto3" json:"rx_dropped,omitempty"` + TxBytes uint64 `protobuf:"varint,6,opt,name=tx_bytes,json=txBytes,proto3" json:"tx_bytes,omitempty"` + TxPackets uint64 `protobuf:"varint,7,opt,name=tx_packets,json=txPackets,proto3" json:"tx_packets,omitempty"` + TxErrors uint64 `protobuf:"varint,8,opt,name=tx_errors,json=txErrors,proto3" json:"tx_errors,omitempty"` + TxDropped uint64 `protobuf:"varint,9,opt,name=tx_dropped,json=txDropped,proto3" json:"tx_dropped,omitempty"` +} + +func (m *NetworkStat) Reset() { *m = NetworkStat{} } +func (*NetworkStat) ProtoMessage() {} +func (*NetworkStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{12} } + +func init() { + proto.RegisterType((*Metrics)(nil), "io.containerd.cgroups.v1.Metrics") + proto.RegisterType((*HugetlbStat)(nil), "io.containerd.cgroups.v1.HugetlbStat") + proto.RegisterType((*PidsStat)(nil), "io.containerd.cgroups.v1.PidsStat") + proto.RegisterType((*CPUStat)(nil), "io.containerd.cgroups.v1.CPUStat") + proto.RegisterType((*CPUUsage)(nil), "io.containerd.cgroups.v1.CPUUsage") + proto.RegisterType((*Throttle)(nil), "io.containerd.cgroups.v1.Throttle") + proto.RegisterType((*MemoryStat)(nil), "io.containerd.cgroups.v1.MemoryStat") + proto.RegisterType((*MemoryEntry)(nil), "io.containerd.cgroups.v1.MemoryEntry") + proto.RegisterType((*BlkIOStat)(nil), "io.containerd.cgroups.v1.BlkIOStat") + proto.RegisterType((*BlkIOEntry)(nil), "io.containerd.cgroups.v1.BlkIOEntry") + proto.RegisterType((*RdmaStat)(nil), "io.containerd.cgroups.v1.RdmaStat") + proto.RegisterType((*RdmaEntry)(nil), "io.containerd.cgroups.v1.RdmaEntry") + proto.RegisterType((*NetworkStat)(nil), "io.containerd.cgroups.v1.NetworkStat") +} +func (m *Metrics) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Metrics) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Hugetlb) > 0 { + for _, msg := range m.Hugetlb { + dAtA[i] = 0xa + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.Pids != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Pids.Size())) + n1, err := m.Pids.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + } + if m.CPU != nil { + dAtA[i] = 0x1a + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.CPU.Size())) + n2, err := m.CPU.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + } + if m.Memory != nil { + dAtA[i] = 0x22 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Memory.Size())) + n3, err := m.Memory.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n3 + } + if m.Blkio != nil { + dAtA[i] = 0x2a + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Blkio.Size())) + n4, err := m.Blkio.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n4 + } + if m.Rdma != nil { + dAtA[i] = 0x32 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Rdma.Size())) + n5, err := m.Rdma.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n5 + } + if len(m.Network) > 0 { + for _, msg := range m.Network { + dAtA[i] = 0x3a + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *HugetlbStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HugetlbStat) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Usage != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Usage)) + } + if m.Max != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Max)) + } + if m.Failcnt != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Failcnt)) + } + if len(m.Pagesize) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Pagesize))) + i += copy(dAtA[i:], m.Pagesize) + } + return i, nil +} + +func (m *PidsStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PidsStat) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Current != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Current)) + } + if m.Limit != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Limit)) + } + return i, nil +} + +func (m *CPUStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CPUStat) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Usage != nil { + dAtA[i] = 0xa + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Usage.Size())) + n6, err := m.Usage.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n6 + } + if m.Throttling != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Throttling.Size())) + n7, err := m.Throttling.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n7 + } + return i, nil +} + +func (m *CPUUsage) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CPUUsage) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Total != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Total)) + } + if m.Kernel != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Kernel)) + } + if m.User != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.User)) + } + if len(m.PerCPU) > 0 { + dAtA9 := make([]byte, len(m.PerCPU)*10) + var j8 int + for _, num := range m.PerCPU { + for num >= 1<<7 { + dAtA9[j8] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j8++ + } + dAtA9[j8] = uint8(num) + j8++ + } + dAtA[i] = 0x22 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(j8)) + i += copy(dAtA[i:], dAtA9[:j8]) + } + return i, nil +} + +func (m *Throttle) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Throttle) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Periods != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Periods)) + } + if m.ThrottledPeriods != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.ThrottledPeriods)) + } + if m.ThrottledTime != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.ThrottledTime)) + } + return i, nil +} + +func (m *MemoryStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MemoryStat) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Cache != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Cache)) + } + if m.RSS != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.RSS)) + } + if m.RSSHuge != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.RSSHuge)) + } + if m.MappedFile != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.MappedFile)) + } + if m.Dirty != 0 { + dAtA[i] = 0x28 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Dirty)) + } + if m.Writeback != 0 { + dAtA[i] = 0x30 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Writeback)) + } + if m.PgPgIn != 0 { + dAtA[i] = 0x38 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.PgPgIn)) + } + if m.PgPgOut != 0 { + dAtA[i] = 0x40 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.PgPgOut)) + } + if m.PgFault != 0 { + dAtA[i] = 0x48 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.PgFault)) + } + if m.PgMajFault != 0 { + dAtA[i] = 0x50 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.PgMajFault)) + } + if m.InactiveAnon != 0 { + dAtA[i] = 0x58 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.InactiveAnon)) + } + if m.ActiveAnon != 0 { + dAtA[i] = 0x60 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.ActiveAnon)) + } + if m.InactiveFile != 0 { + dAtA[i] = 0x68 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.InactiveFile)) + } + if m.ActiveFile != 0 { + dAtA[i] = 0x70 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.ActiveFile)) + } + if m.Unevictable != 0 { + dAtA[i] = 0x78 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Unevictable)) + } + if m.HierarchicalMemoryLimit != 0 { + dAtA[i] = 0x80 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.HierarchicalMemoryLimit)) + } + if m.HierarchicalSwapLimit != 0 { + dAtA[i] = 0x88 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.HierarchicalSwapLimit)) + } + if m.TotalCache != 0 { + dAtA[i] = 0x90 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalCache)) + } + if m.TotalRSS != 0 { + dAtA[i] = 0x98 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalRSS)) + } + if m.TotalRSSHuge != 0 { + dAtA[i] = 0xa0 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalRSSHuge)) + } + if m.TotalMappedFile != 0 { + dAtA[i] = 0xa8 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalMappedFile)) + } + if m.TotalDirty != 0 { + dAtA[i] = 0xb0 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalDirty)) + } + if m.TotalWriteback != 0 { + dAtA[i] = 0xb8 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalWriteback)) + } + if m.TotalPgPgIn != 0 { + dAtA[i] = 0xc0 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgPgIn)) + } + if m.TotalPgPgOut != 0 { + dAtA[i] = 0xc8 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgPgOut)) + } + if m.TotalPgFault != 0 { + dAtA[i] = 0xd0 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgFault)) + } + if m.TotalPgMajFault != 0 { + dAtA[i] = 0xd8 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgMajFault)) + } + if m.TotalInactiveAnon != 0 { + dAtA[i] = 0xe0 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalInactiveAnon)) + } + if m.TotalActiveAnon != 0 { + dAtA[i] = 0xe8 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalActiveAnon)) + } + if m.TotalInactiveFile != 0 { + dAtA[i] = 0xf0 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalInactiveFile)) + } + if m.TotalActiveFile != 0 { + dAtA[i] = 0xf8 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalActiveFile)) + } + if m.TotalUnevictable != 0 { + dAtA[i] = 0x80 + i++ + dAtA[i] = 0x2 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalUnevictable)) + } + if m.Usage != nil { + dAtA[i] = 0x8a + i++ + dAtA[i] = 0x2 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Usage.Size())) + n10, err := m.Usage.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n10 + } + if m.Swap != nil { + dAtA[i] = 0x92 + i++ + dAtA[i] = 0x2 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Swap.Size())) + n11, err := m.Swap.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n11 + } + if m.Kernel != nil { + dAtA[i] = 0x9a + i++ + dAtA[i] = 0x2 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Kernel.Size())) + n12, err := m.Kernel.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n12 + } + if m.KernelTCP != nil { + dAtA[i] = 0xa2 + i++ + dAtA[i] = 0x2 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.KernelTCP.Size())) + n13, err := m.KernelTCP.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n13 + } + return i, nil +} + +func (m *MemoryEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MemoryEntry) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Limit != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Limit)) + } + if m.Usage != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Usage)) + } + if m.Max != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Max)) + } + if m.Failcnt != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Failcnt)) + } + return i, nil +} + +func (m *BlkIOStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BlkIOStat) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.IoServiceBytesRecursive) > 0 { + for _, msg := range m.IoServiceBytesRecursive { + dAtA[i] = 0xa + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.IoServicedRecursive) > 0 { + for _, msg := range m.IoServicedRecursive { + dAtA[i] = 0x12 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.IoQueuedRecursive) > 0 { + for _, msg := range m.IoQueuedRecursive { + dAtA[i] = 0x1a + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.IoServiceTimeRecursive) > 0 { + for _, msg := range m.IoServiceTimeRecursive { + dAtA[i] = 0x22 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.IoWaitTimeRecursive) > 0 { + for _, msg := range m.IoWaitTimeRecursive { + dAtA[i] = 0x2a + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.IoMergedRecursive) > 0 { + for _, msg := range m.IoMergedRecursive { + dAtA[i] = 0x32 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.IoTimeRecursive) > 0 { + for _, msg := range m.IoTimeRecursive { + dAtA[i] = 0x3a + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.SectorsRecursive) > 0 { + for _, msg := range m.SectorsRecursive { + dAtA[i] = 0x42 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *BlkIOEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BlkIOEntry) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Op) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Op))) + i += copy(dAtA[i:], m.Op) + } + if len(m.Device) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Device))) + i += copy(dAtA[i:], m.Device) + } + if m.Major != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Major)) + } + if m.Minor != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Minor)) + } + if m.Value != 0 { + dAtA[i] = 0x28 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Value)) + } + return i, nil +} + +func (m *RdmaStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RdmaStat) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Current) > 0 { + for _, msg := range m.Current { + dAtA[i] = 0xa + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if len(m.Limit) > 0 { + for _, msg := range m.Limit { + dAtA[i] = 0x12 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *RdmaEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RdmaEntry) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Device) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Device))) + i += copy(dAtA[i:], m.Device) + } + if m.HcaHandles != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.HcaHandles)) + } + if m.HcaObjects != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.HcaObjects)) + } + return i, nil +} + +func (m *NetworkStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NetworkStat) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Name) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if m.RxBytes != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.RxBytes)) + } + if m.RxPackets != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.RxPackets)) + } + if m.RxErrors != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.RxErrors)) + } + if m.RxDropped != 0 { + dAtA[i] = 0x28 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.RxDropped)) + } + if m.TxBytes != 0 { + dAtA[i] = 0x30 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TxBytes)) + } + if m.TxPackets != 0 { + dAtA[i] = 0x38 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TxPackets)) + } + if m.TxErrors != 0 { + dAtA[i] = 0x40 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TxErrors)) + } + if m.TxDropped != 0 { + dAtA[i] = 0x48 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.TxDropped)) + } + return i, nil +} + +func encodeVarintMetrics(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Metrics) Size() (n int) { + var l int + _ = l + if len(m.Hugetlb) > 0 { + for _, e := range m.Hugetlb { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.Pids != nil { + l = m.Pids.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.CPU != nil { + l = m.CPU.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Memory != nil { + l = m.Memory.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Blkio != nil { + l = m.Blkio.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Rdma != nil { + l = m.Rdma.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if len(m.Network) > 0 { + for _, e := range m.Network { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + return n +} + +func (m *HugetlbStat) Size() (n int) { + var l int + _ = l + if m.Usage != 0 { + n += 1 + sovMetrics(uint64(m.Usage)) + } + if m.Max != 0 { + n += 1 + sovMetrics(uint64(m.Max)) + } + if m.Failcnt != 0 { + n += 1 + sovMetrics(uint64(m.Failcnt)) + } + l = len(m.Pagesize) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + return n +} + +func (m *PidsStat) Size() (n int) { + var l int + _ = l + if m.Current != 0 { + n += 1 + sovMetrics(uint64(m.Current)) + } + if m.Limit != 0 { + n += 1 + sovMetrics(uint64(m.Limit)) + } + return n +} + +func (m *CPUStat) Size() (n int) { + var l int + _ = l + if m.Usage != nil { + l = m.Usage.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Throttling != nil { + l = m.Throttling.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + return n +} + +func (m *CPUUsage) Size() (n int) { + var l int + _ = l + if m.Total != 0 { + n += 1 + sovMetrics(uint64(m.Total)) + } + if m.Kernel != 0 { + n += 1 + sovMetrics(uint64(m.Kernel)) + } + if m.User != 0 { + n += 1 + sovMetrics(uint64(m.User)) + } + if len(m.PerCPU) > 0 { + l = 0 + for _, e := range m.PerCPU { + l += sovMetrics(uint64(e)) + } + n += 1 + sovMetrics(uint64(l)) + l + } + return n +} + +func (m *Throttle) Size() (n int) { + var l int + _ = l + if m.Periods != 0 { + n += 1 + sovMetrics(uint64(m.Periods)) + } + if m.ThrottledPeriods != 0 { + n += 1 + sovMetrics(uint64(m.ThrottledPeriods)) + } + if m.ThrottledTime != 0 { + n += 1 + sovMetrics(uint64(m.ThrottledTime)) + } + return n +} + +func (m *MemoryStat) Size() (n int) { + var l int + _ = l + if m.Cache != 0 { + n += 1 + sovMetrics(uint64(m.Cache)) + } + if m.RSS != 0 { + n += 1 + sovMetrics(uint64(m.RSS)) + } + if m.RSSHuge != 0 { + n += 1 + sovMetrics(uint64(m.RSSHuge)) + } + if m.MappedFile != 0 { + n += 1 + sovMetrics(uint64(m.MappedFile)) + } + if m.Dirty != 0 { + n += 1 + sovMetrics(uint64(m.Dirty)) + } + if m.Writeback != 0 { + n += 1 + sovMetrics(uint64(m.Writeback)) + } + if m.PgPgIn != 0 { + n += 1 + sovMetrics(uint64(m.PgPgIn)) + } + if m.PgPgOut != 0 { + n += 1 + sovMetrics(uint64(m.PgPgOut)) + } + if m.PgFault != 0 { + n += 1 + sovMetrics(uint64(m.PgFault)) + } + if m.PgMajFault != 0 { + n += 1 + sovMetrics(uint64(m.PgMajFault)) + } + if m.InactiveAnon != 0 { + n += 1 + sovMetrics(uint64(m.InactiveAnon)) + } + if m.ActiveAnon != 0 { + n += 1 + sovMetrics(uint64(m.ActiveAnon)) + } + if m.InactiveFile != 0 { + n += 1 + sovMetrics(uint64(m.InactiveFile)) + } + if m.ActiveFile != 0 { + n += 1 + sovMetrics(uint64(m.ActiveFile)) + } + if m.Unevictable != 0 { + n += 1 + sovMetrics(uint64(m.Unevictable)) + } + if m.HierarchicalMemoryLimit != 0 { + n += 2 + sovMetrics(uint64(m.HierarchicalMemoryLimit)) + } + if m.HierarchicalSwapLimit != 0 { + n += 2 + sovMetrics(uint64(m.HierarchicalSwapLimit)) + } + if m.TotalCache != 0 { + n += 2 + sovMetrics(uint64(m.TotalCache)) + } + if m.TotalRSS != 0 { + n += 2 + sovMetrics(uint64(m.TotalRSS)) + } + if m.TotalRSSHuge != 0 { + n += 2 + sovMetrics(uint64(m.TotalRSSHuge)) + } + if m.TotalMappedFile != 0 { + n += 2 + sovMetrics(uint64(m.TotalMappedFile)) + } + if m.TotalDirty != 0 { + n += 2 + sovMetrics(uint64(m.TotalDirty)) + } + if m.TotalWriteback != 0 { + n += 2 + sovMetrics(uint64(m.TotalWriteback)) + } + if m.TotalPgPgIn != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgPgIn)) + } + if m.TotalPgPgOut != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgPgOut)) + } + if m.TotalPgFault != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgFault)) + } + if m.TotalPgMajFault != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgMajFault)) + } + if m.TotalInactiveAnon != 0 { + n += 2 + sovMetrics(uint64(m.TotalInactiveAnon)) + } + if m.TotalActiveAnon != 0 { + n += 2 + sovMetrics(uint64(m.TotalActiveAnon)) + } + if m.TotalInactiveFile != 0 { + n += 2 + sovMetrics(uint64(m.TotalInactiveFile)) + } + if m.TotalActiveFile != 0 { + n += 2 + sovMetrics(uint64(m.TotalActiveFile)) + } + if m.TotalUnevictable != 0 { + n += 2 + sovMetrics(uint64(m.TotalUnevictable)) + } + if m.Usage != nil { + l = m.Usage.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + if m.Swap != nil { + l = m.Swap.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + if m.Kernel != nil { + l = m.Kernel.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + if m.KernelTCP != nil { + l = m.KernelTCP.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + return n +} + +func (m *MemoryEntry) Size() (n int) { + var l int + _ = l + if m.Limit != 0 { + n += 1 + sovMetrics(uint64(m.Limit)) + } + if m.Usage != 0 { + n += 1 + sovMetrics(uint64(m.Usage)) + } + if m.Max != 0 { + n += 1 + sovMetrics(uint64(m.Max)) + } + if m.Failcnt != 0 { + n += 1 + sovMetrics(uint64(m.Failcnt)) + } + return n +} + +func (m *BlkIOStat) Size() (n int) { + var l int + _ = l + if len(m.IoServiceBytesRecursive) > 0 { + for _, e := range m.IoServiceBytesRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoServicedRecursive) > 0 { + for _, e := range m.IoServicedRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoQueuedRecursive) > 0 { + for _, e := range m.IoQueuedRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoServiceTimeRecursive) > 0 { + for _, e := range m.IoServiceTimeRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoWaitTimeRecursive) > 0 { + for _, e := range m.IoWaitTimeRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoMergedRecursive) > 0 { + for _, e := range m.IoMergedRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoTimeRecursive) > 0 { + for _, e := range m.IoTimeRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.SectorsRecursive) > 0 { + for _, e := range m.SectorsRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + return n +} + +func (m *BlkIOEntry) Size() (n int) { + var l int + _ = l + l = len(m.Op) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + l = len(m.Device) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Major != 0 { + n += 1 + sovMetrics(uint64(m.Major)) + } + if m.Minor != 0 { + n += 1 + sovMetrics(uint64(m.Minor)) + } + if m.Value != 0 { + n += 1 + sovMetrics(uint64(m.Value)) + } + return n +} + +func (m *RdmaStat) Size() (n int) { + var l int + _ = l + if len(m.Current) > 0 { + for _, e := range m.Current { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.Limit) > 0 { + for _, e := range m.Limit { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + return n +} + +func (m *RdmaEntry) Size() (n int) { + var l int + _ = l + l = len(m.Device) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.HcaHandles != 0 { + n += 1 + sovMetrics(uint64(m.HcaHandles)) + } + if m.HcaObjects != 0 { + n += 1 + sovMetrics(uint64(m.HcaObjects)) + } + return n +} + +func (m *NetworkStat) Size() (n int) { + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.RxBytes != 0 { + n += 1 + sovMetrics(uint64(m.RxBytes)) + } + if m.RxPackets != 0 { + n += 1 + sovMetrics(uint64(m.RxPackets)) + } + if m.RxErrors != 0 { + n += 1 + sovMetrics(uint64(m.RxErrors)) + } + if m.RxDropped != 0 { + n += 1 + sovMetrics(uint64(m.RxDropped)) + } + if m.TxBytes != 0 { + n += 1 + sovMetrics(uint64(m.TxBytes)) + } + if m.TxPackets != 0 { + n += 1 + sovMetrics(uint64(m.TxPackets)) + } + if m.TxErrors != 0 { + n += 1 + sovMetrics(uint64(m.TxErrors)) + } + if m.TxDropped != 0 { + n += 1 + sovMetrics(uint64(m.TxDropped)) + } + return n +} + +func sovMetrics(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozMetrics(x uint64) (n int) { + return sovMetrics(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Metrics) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Metrics{`, + `Hugetlb:` + strings.Replace(fmt.Sprintf("%v", this.Hugetlb), "HugetlbStat", "HugetlbStat", 1) + `,`, + `Pids:` + strings.Replace(fmt.Sprintf("%v", this.Pids), "PidsStat", "PidsStat", 1) + `,`, + `CPU:` + strings.Replace(fmt.Sprintf("%v", this.CPU), "CPUStat", "CPUStat", 1) + `,`, + `Memory:` + strings.Replace(fmt.Sprintf("%v", this.Memory), "MemoryStat", "MemoryStat", 1) + `,`, + `Blkio:` + strings.Replace(fmt.Sprintf("%v", this.Blkio), "BlkIOStat", "BlkIOStat", 1) + `,`, + `Rdma:` + strings.Replace(fmt.Sprintf("%v", this.Rdma), "RdmaStat", "RdmaStat", 1) + `,`, + `Network:` + strings.Replace(fmt.Sprintf("%v", this.Network), "NetworkStat", "NetworkStat", 1) + `,`, + `}`, + }, "") + return s +} +func (this *HugetlbStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&HugetlbStat{`, + `Usage:` + fmt.Sprintf("%v", this.Usage) + `,`, + `Max:` + fmt.Sprintf("%v", this.Max) + `,`, + `Failcnt:` + fmt.Sprintf("%v", this.Failcnt) + `,`, + `Pagesize:` + fmt.Sprintf("%v", this.Pagesize) + `,`, + `}`, + }, "") + return s +} +func (this *PidsStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PidsStat{`, + `Current:` + fmt.Sprintf("%v", this.Current) + `,`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, + `}`, + }, "") + return s +} +func (this *CPUStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CPUStat{`, + `Usage:` + strings.Replace(fmt.Sprintf("%v", this.Usage), "CPUUsage", "CPUUsage", 1) + `,`, + `Throttling:` + strings.Replace(fmt.Sprintf("%v", this.Throttling), "Throttle", "Throttle", 1) + `,`, + `}`, + }, "") + return s +} +func (this *CPUUsage) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CPUUsage{`, + `Total:` + fmt.Sprintf("%v", this.Total) + `,`, + `Kernel:` + fmt.Sprintf("%v", this.Kernel) + `,`, + `User:` + fmt.Sprintf("%v", this.User) + `,`, + `PerCPU:` + fmt.Sprintf("%v", this.PerCPU) + `,`, + `}`, + }, "") + return s +} +func (this *Throttle) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Throttle{`, + `Periods:` + fmt.Sprintf("%v", this.Periods) + `,`, + `ThrottledPeriods:` + fmt.Sprintf("%v", this.ThrottledPeriods) + `,`, + `ThrottledTime:` + fmt.Sprintf("%v", this.ThrottledTime) + `,`, + `}`, + }, "") + return s +} +func (this *MemoryStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MemoryStat{`, + `Cache:` + fmt.Sprintf("%v", this.Cache) + `,`, + `RSS:` + fmt.Sprintf("%v", this.RSS) + `,`, + `RSSHuge:` + fmt.Sprintf("%v", this.RSSHuge) + `,`, + `MappedFile:` + fmt.Sprintf("%v", this.MappedFile) + `,`, + `Dirty:` + fmt.Sprintf("%v", this.Dirty) + `,`, + `Writeback:` + fmt.Sprintf("%v", this.Writeback) + `,`, + `PgPgIn:` + fmt.Sprintf("%v", this.PgPgIn) + `,`, + `PgPgOut:` + fmt.Sprintf("%v", this.PgPgOut) + `,`, + `PgFault:` + fmt.Sprintf("%v", this.PgFault) + `,`, + `PgMajFault:` + fmt.Sprintf("%v", this.PgMajFault) + `,`, + `InactiveAnon:` + fmt.Sprintf("%v", this.InactiveAnon) + `,`, + `ActiveAnon:` + fmt.Sprintf("%v", this.ActiveAnon) + `,`, + `InactiveFile:` + fmt.Sprintf("%v", this.InactiveFile) + `,`, + `ActiveFile:` + fmt.Sprintf("%v", this.ActiveFile) + `,`, + `Unevictable:` + fmt.Sprintf("%v", this.Unevictable) + `,`, + `HierarchicalMemoryLimit:` + fmt.Sprintf("%v", this.HierarchicalMemoryLimit) + `,`, + `HierarchicalSwapLimit:` + fmt.Sprintf("%v", this.HierarchicalSwapLimit) + `,`, + `TotalCache:` + fmt.Sprintf("%v", this.TotalCache) + `,`, + `TotalRSS:` + fmt.Sprintf("%v", this.TotalRSS) + `,`, + `TotalRSSHuge:` + fmt.Sprintf("%v", this.TotalRSSHuge) + `,`, + `TotalMappedFile:` + fmt.Sprintf("%v", this.TotalMappedFile) + `,`, + `TotalDirty:` + fmt.Sprintf("%v", this.TotalDirty) + `,`, + `TotalWriteback:` + fmt.Sprintf("%v", this.TotalWriteback) + `,`, + `TotalPgPgIn:` + fmt.Sprintf("%v", this.TotalPgPgIn) + `,`, + `TotalPgPgOut:` + fmt.Sprintf("%v", this.TotalPgPgOut) + `,`, + `TotalPgFault:` + fmt.Sprintf("%v", this.TotalPgFault) + `,`, + `TotalPgMajFault:` + fmt.Sprintf("%v", this.TotalPgMajFault) + `,`, + `TotalInactiveAnon:` + fmt.Sprintf("%v", this.TotalInactiveAnon) + `,`, + `TotalActiveAnon:` + fmt.Sprintf("%v", this.TotalActiveAnon) + `,`, + `TotalInactiveFile:` + fmt.Sprintf("%v", this.TotalInactiveFile) + `,`, + `TotalActiveFile:` + fmt.Sprintf("%v", this.TotalActiveFile) + `,`, + `TotalUnevictable:` + fmt.Sprintf("%v", this.TotalUnevictable) + `,`, + `Usage:` + strings.Replace(fmt.Sprintf("%v", this.Usage), "MemoryEntry", "MemoryEntry", 1) + `,`, + `Swap:` + strings.Replace(fmt.Sprintf("%v", this.Swap), "MemoryEntry", "MemoryEntry", 1) + `,`, + `Kernel:` + strings.Replace(fmt.Sprintf("%v", this.Kernel), "MemoryEntry", "MemoryEntry", 1) + `,`, + `KernelTCP:` + strings.Replace(fmt.Sprintf("%v", this.KernelTCP), "MemoryEntry", "MemoryEntry", 1) + `,`, + `}`, + }, "") + return s +} +func (this *MemoryEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MemoryEntry{`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, + `Usage:` + fmt.Sprintf("%v", this.Usage) + `,`, + `Max:` + fmt.Sprintf("%v", this.Max) + `,`, + `Failcnt:` + fmt.Sprintf("%v", this.Failcnt) + `,`, + `}`, + }, "") + return s +} +func (this *BlkIOStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&BlkIOStat{`, + `IoServiceBytesRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoServiceBytesRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, + `IoServicedRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoServicedRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, + `IoQueuedRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoQueuedRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, + `IoServiceTimeRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoServiceTimeRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, + `IoWaitTimeRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoWaitTimeRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, + `IoMergedRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoMergedRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, + `IoTimeRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoTimeRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, + `SectorsRecursive:` + strings.Replace(fmt.Sprintf("%v", this.SectorsRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, + `}`, + }, "") + return s +} +func (this *BlkIOEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&BlkIOEntry{`, + `Op:` + fmt.Sprintf("%v", this.Op) + `,`, + `Device:` + fmt.Sprintf("%v", this.Device) + `,`, + `Major:` + fmt.Sprintf("%v", this.Major) + `,`, + `Minor:` + fmt.Sprintf("%v", this.Minor) + `,`, + `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `}`, + }, "") + return s +} +func (this *RdmaStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RdmaStat{`, + `Current:` + strings.Replace(fmt.Sprintf("%v", this.Current), "RdmaEntry", "RdmaEntry", 1) + `,`, + `Limit:` + strings.Replace(fmt.Sprintf("%v", this.Limit), "RdmaEntry", "RdmaEntry", 1) + `,`, + `}`, + }, "") + return s +} +func (this *RdmaEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RdmaEntry{`, + `Device:` + fmt.Sprintf("%v", this.Device) + `,`, + `HcaHandles:` + fmt.Sprintf("%v", this.HcaHandles) + `,`, + `HcaObjects:` + fmt.Sprintf("%v", this.HcaObjects) + `,`, + `}`, + }, "") + return s +} +func (this *NetworkStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&NetworkStat{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `RxBytes:` + fmt.Sprintf("%v", this.RxBytes) + `,`, + `RxPackets:` + fmt.Sprintf("%v", this.RxPackets) + `,`, + `RxErrors:` + fmt.Sprintf("%v", this.RxErrors) + `,`, + `RxDropped:` + fmt.Sprintf("%v", this.RxDropped) + `,`, + `TxBytes:` + fmt.Sprintf("%v", this.TxBytes) + `,`, + `TxPackets:` + fmt.Sprintf("%v", this.TxPackets) + `,`, + `TxErrors:` + fmt.Sprintf("%v", this.TxErrors) + `,`, + `TxDropped:` + fmt.Sprintf("%v", this.TxDropped) + `,`, + `}`, + }, "") + return s +} +func valueToStringMetrics(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Metrics) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Metrics: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Metrics: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hugetlb", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hugetlb = append(m.Hugetlb, &HugetlbStat{}) + if err := m.Hugetlb[len(m.Hugetlb)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pids", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pids == nil { + m.Pids = &PidsStat{} + } + if err := m.Pids.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CPU", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CPU == nil { + m.CPU = &CPUStat{} + } + if err := m.CPU.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Memory == nil { + m.Memory = &MemoryStat{} + } + if err := m.Memory.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Blkio", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Blkio == nil { + m.Blkio = &BlkIOStat{} + } + if err := m.Blkio.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rdma", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Rdma == nil { + m.Rdma = &RdmaStat{} + } + if err := m.Rdma.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Network", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Network = append(m.Network, &NetworkStat{}) + if err := m.Network[len(m.Network)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HugetlbStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HugetlbStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HugetlbStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + m.Usage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Usage |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType) + } + m.Max = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Max |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Failcnt", wireType) + } + m.Failcnt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Failcnt |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagesize", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pagesize = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PidsStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PidsStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PidsStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) + } + m.Current = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Current |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CPUStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CPUStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CPUStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Usage == nil { + m.Usage = &CPUUsage{} + } + if err := m.Usage.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Throttling", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Throttling == nil { + m.Throttling = &Throttle{} + } + if err := m.Throttling.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CPUUsage) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CPUUsage: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CPUUsage: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) + } + m.Total = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Total |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Kernel", wireType) + } + m.Kernel = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Kernel |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) + } + m.User = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.User |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.PerCPU = append(m.PerCPU, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + packedLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.PerCPU = append(m.PerCPU, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field PerCPU", wireType) + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Throttle) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Throttle: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Throttle: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Periods", wireType) + } + m.Periods = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Periods |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ThrottledPeriods", wireType) + } + m.ThrottledPeriods = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ThrottledPeriods |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ThrottledTime", wireType) + } + m.ThrottledTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ThrottledTime |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MemoryStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MemoryStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MemoryStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cache", wireType) + } + m.Cache = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Cache |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RSS", wireType) + } + m.RSS = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RSS |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RSSHuge", wireType) + } + m.RSSHuge = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RSSHuge |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MappedFile", wireType) + } + m.MappedFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MappedFile |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Dirty", wireType) + } + m.Dirty = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Dirty |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Writeback", wireType) + } + m.Writeback = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Writeback |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgPgIn", wireType) + } + m.PgPgIn = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgPgIn |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgPgOut", wireType) + } + m.PgPgOut = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgPgOut |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgFault", wireType) + } + m.PgFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgFault |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgMajFault", wireType) + } + m.PgMajFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgMajFault |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InactiveAnon", wireType) + } + m.InactiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InactiveAnon |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveAnon", wireType) + } + m.ActiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ActiveAnon |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InactiveFile", wireType) + } + m.InactiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InactiveFile |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveFile", wireType) + } + m.ActiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ActiveFile |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Unevictable", wireType) + } + m.Unevictable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Unevictable |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HierarchicalMemoryLimit", wireType) + } + m.HierarchicalMemoryLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HierarchicalMemoryLimit |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 17: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HierarchicalSwapLimit", wireType) + } + m.HierarchicalSwapLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HierarchicalSwapLimit |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 18: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalCache", wireType) + } + m.TotalCache = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalCache |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalRSS", wireType) + } + m.TotalRSS = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalRSS |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 20: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalRSSHuge", wireType) + } + m.TotalRSSHuge = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalRSSHuge |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 21: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalMappedFile", wireType) + } + m.TotalMappedFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalMappedFile |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 22: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalDirty", wireType) + } + m.TotalDirty = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalDirty |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 23: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalWriteback", wireType) + } + m.TotalWriteback = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalWriteback |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 24: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgPgIn", wireType) + } + m.TotalPgPgIn = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgPgIn |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 25: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgPgOut", wireType) + } + m.TotalPgPgOut = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgPgOut |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 26: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgFault", wireType) + } + m.TotalPgFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgFault |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 27: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgMajFault", wireType) + } + m.TotalPgMajFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgMajFault |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 28: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalInactiveAnon", wireType) + } + m.TotalInactiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalInactiveAnon |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 29: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalActiveAnon", wireType) + } + m.TotalActiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalActiveAnon |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 30: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalInactiveFile", wireType) + } + m.TotalInactiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalInactiveFile |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 31: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalActiveFile", wireType) + } + m.TotalActiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalActiveFile |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 32: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalUnevictable", wireType) + } + m.TotalUnevictable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalUnevictable |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 33: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Usage == nil { + m.Usage = &MemoryEntry{} + } + if err := m.Usage.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 34: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Swap", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Swap == nil { + m.Swap = &MemoryEntry{} + } + if err := m.Swap.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 35: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kernel", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Kernel == nil { + m.Kernel = &MemoryEntry{} + } + if err := m.Kernel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 36: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field KernelTCP", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.KernelTCP == nil { + m.KernelTCP = &MemoryEntry{} + } + if err := m.KernelTCP.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MemoryEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MemoryEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MemoryEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + m.Usage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Usage |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType) + } + m.Max = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Max |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Failcnt", wireType) + } + m.Failcnt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Failcnt |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BlkIOStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BlkIOStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BlkIOStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoServiceBytesRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoServiceBytesRecursive = append(m.IoServiceBytesRecursive, &BlkIOEntry{}) + if err := m.IoServiceBytesRecursive[len(m.IoServiceBytesRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoServicedRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoServicedRecursive = append(m.IoServicedRecursive, &BlkIOEntry{}) + if err := m.IoServicedRecursive[len(m.IoServicedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoQueuedRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoQueuedRecursive = append(m.IoQueuedRecursive, &BlkIOEntry{}) + if err := m.IoQueuedRecursive[len(m.IoQueuedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoServiceTimeRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoServiceTimeRecursive = append(m.IoServiceTimeRecursive, &BlkIOEntry{}) + if err := m.IoServiceTimeRecursive[len(m.IoServiceTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoWaitTimeRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoWaitTimeRecursive = append(m.IoWaitTimeRecursive, &BlkIOEntry{}) + if err := m.IoWaitTimeRecursive[len(m.IoWaitTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoMergedRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoMergedRecursive = append(m.IoMergedRecursive, &BlkIOEntry{}) + if err := m.IoMergedRecursive[len(m.IoMergedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoTimeRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoTimeRecursive = append(m.IoTimeRecursive, &BlkIOEntry{}) + if err := m.IoTimeRecursive[len(m.IoTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SectorsRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SectorsRecursive = append(m.SectorsRecursive, &BlkIOEntry{}) + if err := m.SectorsRecursive[len(m.SectorsRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BlkIOEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BlkIOEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BlkIOEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Op", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Op = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Device = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Major", wireType) + } + m.Major = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Major |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Minor", wireType) + } + m.Minor = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Minor |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + m.Value = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Value |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RdmaStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RdmaStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RdmaStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Current = append(m.Current, &RdmaEntry{}) + if err := m.Current[len(m.Current)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Limit = append(m.Limit, &RdmaEntry{}) + if err := m.Limit[len(m.Limit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RdmaEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RdmaEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RdmaEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Device = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HcaHandles", wireType) + } + m.HcaHandles = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HcaHandles |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HcaObjects", wireType) + } + m.HcaObjects = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HcaObjects |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NetworkStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NetworkStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NetworkStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxBytes", wireType) + } + m.RxBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxBytes |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxPackets", wireType) + } + m.RxPackets = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxPackets |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxErrors", wireType) + } + m.RxErrors = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxErrors |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxDropped", wireType) + } + m.RxDropped = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxDropped |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxBytes", wireType) + } + m.TxBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxBytes |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxPackets", wireType) + } + m.TxPackets = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxPackets |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxErrors", wireType) + } + m.TxErrors = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxErrors |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxDropped", wireType) + } + m.TxDropped = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxDropped |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMetrics(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthMetrics + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipMetrics(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthMetrics = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMetrics = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("github.com/containerd/cgroups/metrics.proto", fileDescriptorMetrics) } + +var fileDescriptorMetrics = []byte{ + // 1549 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x57, 0x4d, 0x6f, 0x1b, 0xb7, + 0x16, 0x8d, 0x2c, 0xd9, 0xd2, 0x5c, 0xd9, 0x8e, 0x4d, 0x27, 0xce, 0xd8, 0x49, 0x2c, 0x47, 0xb6, + 0xdf, 0xf3, 0x7b, 0x06, 0x64, 0xbc, 0x3c, 0x20, 0x68, 0xd2, 0x04, 0x45, 0xe4, 0x24, 0x48, 0xd0, + 0xba, 0x51, 0x46, 0x36, 0xd2, 0xae, 0x06, 0xd4, 0x88, 0x19, 0xd1, 0x96, 0x86, 0x13, 0x0e, 0xc7, + 0x96, 0xbb, 0xea, 0xa2, 0x40, 0x57, 0xfd, 0x33, 0xfd, 0x15, 0x59, 0x76, 0x53, 0xa0, 0xdd, 0x18, + 0x8d, 0x7e, 0x49, 0x41, 0x72, 0x3e, 0xa8, 0x24, 0x8e, 0xab, 0xdd, 0x90, 0x3c, 0xe7, 0xdc, 0xcb, + 0x3b, 0x87, 0xc3, 0x3b, 0xb0, 0xe3, 0x53, 0xd1, 0x8b, 0x3b, 0x0d, 0x8f, 0x0d, 0x76, 0x3d, 0x16, + 0x08, 0x4c, 0x03, 0xc2, 0xbb, 0xbb, 0x9e, 0xcf, 0x59, 0x1c, 0x46, 0xbb, 0x03, 0x22, 0x38, 0xf5, + 0xa2, 0x46, 0xc8, 0x99, 0x60, 0xc8, 0xa6, 0xac, 0x91, 0x83, 0x1a, 0x09, 0xa8, 0x71, 0xf2, 0xbf, + 0xd5, 0x6b, 0x3e, 0xf3, 0x99, 0x02, 0xed, 0xca, 0x27, 0x8d, 0xaf, 0xff, 0x5a, 0x84, 0xf2, 0xbe, + 0x56, 0x40, 0x5f, 0x41, 0xb9, 0x17, 0xfb, 0x44, 0xf4, 0x3b, 0x76, 0x61, 0xbd, 0xb8, 0x5d, 0xbd, + 0xbb, 0xd5, 0xb8, 0x48, 0xad, 0xf1, 0x5c, 0x03, 0xdb, 0x02, 0x0b, 0x27, 0x65, 0xa1, 0x7b, 0x50, + 0x0a, 0x69, 0x37, 0xb2, 0xa7, 0xd6, 0x0b, 0xdb, 0xd5, 0xbb, 0xf5, 0x8b, 0xd9, 0x2d, 0xda, 0x8d, + 0x14, 0x55, 0xe1, 0xd1, 0x43, 0x28, 0x7a, 0x61, 0x6c, 0x17, 0x15, 0xed, 0xce, 0xc5, 0xb4, 0xbd, + 0xd6, 0xa1, 0x64, 0x35, 0xcb, 0xa3, 0xf3, 0x5a, 0x71, 0xaf, 0x75, 0xe8, 0x48, 0x1a, 0x7a, 0x08, + 0x33, 0x03, 0x32, 0x60, 0xfc, 0xcc, 0x2e, 0x29, 0x81, 0xcd, 0x8b, 0x05, 0xf6, 0x15, 0x4e, 0x45, + 0x4e, 0x38, 0xe8, 0x3e, 0x4c, 0x77, 0xfa, 0xc7, 0x94, 0xd9, 0xd3, 0x8a, 0xbc, 0x71, 0x31, 0xb9, + 0xd9, 0x3f, 0x7e, 0xf1, 0x52, 0x71, 0x35, 0x43, 0x6e, 0x97, 0x77, 0x07, 0xd8, 0x9e, 0xb9, 0x6c, + 0xbb, 0x4e, 0x77, 0x80, 0xf5, 0x76, 0x25, 0x5e, 0xd6, 0x39, 0x20, 0xe2, 0x94, 0xf1, 0x63, 0xbb, + 0x7c, 0x59, 0x9d, 0xbf, 0xd5, 0x40, 0x5d, 0xe7, 0x84, 0x55, 0x3f, 0x86, 0xaa, 0x51, 0x7f, 0x74, + 0x0d, 0xa6, 0xe3, 0x08, 0xfb, 0xc4, 0x2e, 0xac, 0x17, 0xb6, 0x4b, 0x8e, 0x1e, 0xa0, 0x05, 0x28, + 0x0e, 0xf0, 0x50, 0xbd, 0x8b, 0x92, 0x23, 0x1f, 0x91, 0x0d, 0xe5, 0x37, 0x98, 0xf6, 0xbd, 0x40, + 0xa8, 0x52, 0x97, 0x9c, 0x74, 0x88, 0x56, 0xa1, 0x12, 0x62, 0x9f, 0x44, 0xf4, 0x07, 0xa2, 0x8a, + 0x68, 0x39, 0xd9, 0xb8, 0xfe, 0x00, 0x2a, 0xe9, 0xeb, 0x92, 0x0a, 0x5e, 0xcc, 0x39, 0x09, 0x44, + 0x12, 0x2b, 0x1d, 0xca, 0x1c, 0xfa, 0x74, 0x40, 0x45, 0x12, 0x4f, 0x0f, 0xea, 0x3f, 0x17, 0xa0, + 0x9c, 0xbc, 0x34, 0xf4, 0x85, 0x99, 0xe5, 0x67, 0xcb, 0xb5, 0xd7, 0x3a, 0x3c, 0x94, 0xc8, 0x74, + 0x27, 0x4d, 0x00, 0xd1, 0xe3, 0x4c, 0x88, 0x3e, 0x0d, 0xfc, 0xcb, 0xcd, 0x75, 0xa0, 0xb1, 0xc4, + 0x31, 0x58, 0xf5, 0xb7, 0x50, 0x49, 0x65, 0x65, 0xae, 0x82, 0x09, 0xdc, 0x4f, 0xeb, 0xa5, 0x06, + 0x68, 0x19, 0x66, 0x8e, 0x09, 0x0f, 0x48, 0x3f, 0xd9, 0x42, 0x32, 0x42, 0x08, 0x4a, 0x71, 0x44, + 0x78, 0x52, 0x32, 0xf5, 0x8c, 0x36, 0xa0, 0x1c, 0x12, 0xee, 0x4a, 0xd3, 0x96, 0xd6, 0x8b, 0xdb, + 0xa5, 0x26, 0x8c, 0xce, 0x6b, 0x33, 0x2d, 0xc2, 0xa5, 0x29, 0x67, 0x42, 0xc2, 0xf7, 0xc2, 0xb8, + 0x3e, 0x84, 0x4a, 0x9a, 0x8a, 0x2c, 0x5c, 0x48, 0x38, 0x65, 0xdd, 0x28, 0x2d, 0x5c, 0x32, 0x44, + 0x3b, 0xb0, 0x98, 0xa4, 0x49, 0xba, 0x6e, 0x8a, 0xd1, 0x19, 0x2c, 0x64, 0x0b, 0xad, 0x04, 0xbc, + 0x05, 0xf3, 0x39, 0x58, 0xd0, 0x01, 0x49, 0xb2, 0x9a, 0xcb, 0x66, 0x0f, 0xe8, 0x80, 0xd4, 0xff, + 0xac, 0x02, 0xe4, 0x56, 0x97, 0xfb, 0xf5, 0xb0, 0xd7, 0xcb, 0xfc, 0xa1, 0x06, 0x68, 0x05, 0x8a, + 0x3c, 0x4a, 0x42, 0xe9, 0x13, 0xe5, 0xb4, 0xdb, 0x8e, 0x9c, 0x43, 0xff, 0x82, 0x0a, 0x8f, 0x22, + 0x57, 0x1e, 0x6b, 0x1d, 0xa0, 0x59, 0x1d, 0x9d, 0xd7, 0xca, 0x4e, 0xbb, 0x2d, 0x6d, 0xe7, 0x94, + 0x79, 0x14, 0xc9, 0x07, 0x54, 0x83, 0xea, 0x00, 0x87, 0x21, 0xe9, 0xba, 0x6f, 0x68, 0x5f, 0x3b, + 0xa7, 0xe4, 0x80, 0x9e, 0x7a, 0x46, 0xfb, 0xaa, 0xd2, 0x5d, 0xca, 0xc5, 0x99, 0x3a, 0x5c, 0x25, + 0x47, 0x0f, 0xd0, 0x2d, 0xb0, 0x4e, 0x39, 0x15, 0xa4, 0x83, 0xbd, 0x63, 0x75, 0x78, 0x4a, 0x4e, + 0x3e, 0x81, 0x6c, 0xa8, 0x84, 0xbe, 0x1b, 0xfa, 0x2e, 0x0d, 0xec, 0xb2, 0x7e, 0x13, 0xa1, 0xdf, + 0xf2, 0x5f, 0x04, 0x68, 0x15, 0x2c, 0xbd, 0xc2, 0x62, 0x61, 0x57, 0x92, 0x32, 0xfa, 0x2d, 0xff, + 0x65, 0x2c, 0xd0, 0x8a, 0x62, 0xbd, 0xc1, 0x71, 0x5f, 0xd8, 0x56, 0xba, 0xf4, 0x4c, 0x0e, 0xd1, + 0x3a, 0xcc, 0x86, 0xbe, 0x3b, 0xc0, 0x47, 0xc9, 0x32, 0xe8, 0x34, 0x43, 0x7f, 0x1f, 0x1f, 0x69, + 0xc4, 0x06, 0xcc, 0xd1, 0x00, 0x7b, 0x82, 0x9e, 0x10, 0x17, 0x07, 0x2c, 0xb0, 0xab, 0x0a, 0x32, + 0x9b, 0x4e, 0x3e, 0x0e, 0x58, 0x20, 0x37, 0x6b, 0x42, 0x66, 0xb5, 0x8a, 0x01, 0x30, 0x55, 0x54, + 0x3d, 0xe6, 0xc6, 0x55, 0x54, 0x45, 0x72, 0x15, 0x05, 0x99, 0x37, 0x55, 0x14, 0x60, 0x1d, 0xaa, + 0x71, 0x40, 0x4e, 0xa8, 0x27, 0x70, 0xa7, 0x4f, 0xec, 0xab, 0x0a, 0x60, 0x4e, 0xa1, 0x07, 0xb0, + 0xd2, 0xa3, 0x84, 0x63, 0xee, 0xf5, 0xa8, 0x87, 0xfb, 0xae, 0xfe, 0x90, 0xb9, 0xfa, 0xf8, 0x2d, + 0x28, 0xfc, 0x0d, 0x13, 0xa0, 0x9d, 0xf0, 0x8d, 0x5c, 0x46, 0xf7, 0x60, 0x6c, 0xc9, 0x8d, 0x4e, + 0x71, 0x98, 0x30, 0x17, 0x15, 0xf3, 0xba, 0xb9, 0xdc, 0x3e, 0xc5, 0xa1, 0xe6, 0xd5, 0xa0, 0xaa, + 0x4e, 0x89, 0xab, 0x8d, 0x84, 0x74, 0xda, 0x6a, 0x6a, 0x4f, 0xb9, 0xe9, 0x3f, 0x60, 0x69, 0x80, + 0xf4, 0xd4, 0x92, 0xf2, 0xcc, 0xec, 0xe8, 0xbc, 0x56, 0x39, 0x90, 0x93, 0xd2, 0x58, 0x15, 0xb5, + 0xec, 0x44, 0x11, 0xba, 0x07, 0xf3, 0x19, 0x54, 0x7b, 0xec, 0x9a, 0xc2, 0x2f, 0x8c, 0xce, 0x6b, + 0xb3, 0x29, 0x5e, 0x19, 0x6d, 0x36, 0xe5, 0x28, 0xb7, 0xfd, 0x17, 0x16, 0x35, 0xcf, 0xf4, 0xdc, + 0x75, 0x95, 0xc9, 0x55, 0xb5, 0xb0, 0x9f, 0x1b, 0x2f, 0xcb, 0x57, 0xdb, 0x6f, 0xd9, 0xc8, 0xf7, + 0x89, 0xf2, 0xe0, 0xbf, 0x41, 0x73, 0xdc, 0xdc, 0x89, 0x37, 0x14, 0x48, 0xe7, 0xf6, 0x3a, 0xb3, + 0xe3, 0x46, 0x9a, 0x6d, 0x66, 0x4a, 0x5b, 0xbf, 0x12, 0x35, 0xdb, 0xd2, 0xce, 0xdc, 0x4a, 0xd5, + 0x72, 0x7f, 0xae, 0xe8, 0x97, 0x9f, 0xa1, 0xa4, 0x49, 0x37, 0x0d, 0x2d, 0xed, 0xc5, 0xd5, 0x31, + 0x94, 0x76, 0xe3, 0x0e, 0xa0, 0x0c, 0x95, 0xbb, 0xf6, 0xa6, 0xb1, 0xd1, 0x56, 0x6e, 0xdd, 0x06, + 0x2c, 0x69, 0xf0, 0xb8, 0x81, 0x6f, 0x29, 0xb4, 0xae, 0xd7, 0x0b, 0xd3, 0xc5, 0x59, 0x11, 0x4d, + 0xf4, 0x6d, 0x43, 0xfb, 0x71, 0x8e, 0xfd, 0x58, 0x5b, 0x95, 0x7c, 0xed, 0x13, 0xda, 0xaa, 0xe8, + 0x1f, 0x6a, 0x2b, 0x74, 0xed, 0x23, 0x6d, 0x85, 0xdd, 0x49, 0xb1, 0xa6, 0xd9, 0xd7, 0x93, 0xcf, + 0x9e, 0x5c, 0x38, 0x34, 0x1c, 0xff, 0x65, 0x7a, 0x75, 0xdc, 0x51, 0xdf, 0xfe, 0xad, 0xcb, 0x2e, + 0xf8, 0xa7, 0x81, 0xe0, 0x67, 0xe9, 0xed, 0x71, 0x1f, 0x4a, 0xd2, 0xe5, 0x76, 0x7d, 0x12, 0xae, + 0xa2, 0xa0, 0x47, 0xd9, 0x95, 0xb0, 0x31, 0x09, 0x39, 0xbd, 0x39, 0xda, 0x00, 0xfa, 0xc9, 0x15, + 0x5e, 0x68, 0x6f, 0x4e, 0x20, 0xd1, 0x9c, 0x1b, 0x9d, 0xd7, 0xac, 0xaf, 0x15, 0xf9, 0x60, 0xaf, + 0xe5, 0x58, 0x5a, 0xe7, 0xc0, 0x0b, 0xeb, 0x04, 0xaa, 0x06, 0x30, 0xbf, 0x77, 0x0b, 0xc6, 0xbd, + 0x9b, 0x77, 0x04, 0x53, 0x9f, 0xe8, 0x08, 0x8a, 0x9f, 0xec, 0x08, 0x4a, 0x63, 0x1d, 0x41, 0xfd, + 0xf7, 0x69, 0xb0, 0xb2, 0x86, 0x07, 0x61, 0x58, 0xa5, 0xcc, 0x8d, 0x08, 0x3f, 0xa1, 0x1e, 0x71, + 0x3b, 0x67, 0x82, 0x44, 0x2e, 0x27, 0x5e, 0xcc, 0x23, 0x7a, 0x42, 0x92, 0x66, 0x71, 0xf3, 0x92, + 0xce, 0x49, 0xd7, 0xe6, 0x06, 0x65, 0x6d, 0x2d, 0xd3, 0x94, 0x2a, 0x4e, 0x2a, 0x82, 0xbe, 0x83, + 0xeb, 0x79, 0x88, 0xae, 0xa1, 0x3e, 0x35, 0x81, 0xfa, 0x52, 0xa6, 0xde, 0xcd, 0x95, 0x0f, 0x60, + 0x89, 0x32, 0xf7, 0x6d, 0x4c, 0xe2, 0x31, 0xdd, 0xe2, 0x04, 0xba, 0x8b, 0x94, 0xbd, 0x52, 0xfc, + 0x5c, 0xd5, 0x85, 0x15, 0xa3, 0x24, 0xf2, 0x2e, 0x36, 0xb4, 0x4b, 0x13, 0x68, 0x2f, 0x67, 0x39, + 0xcb, 0xbb, 0x3b, 0x0f, 0xf0, 0x3d, 0x2c, 0x53, 0xe6, 0x9e, 0x62, 0x2a, 0x3e, 0x54, 0x9f, 0x9e, + 0xac, 0x22, 0xaf, 0x31, 0x15, 0xe3, 0xd2, 0xba, 0x22, 0x03, 0xc2, 0xfd, 0xb1, 0x8a, 0xcc, 0x4c, + 0x56, 0x91, 0x7d, 0xc5, 0xcf, 0x55, 0x5b, 0xb0, 0x48, 0xd9, 0x87, 0xb9, 0x96, 0x27, 0xd0, 0xbc, + 0x4a, 0xd9, 0x78, 0x9e, 0xaf, 0x60, 0x31, 0x22, 0x9e, 0x60, 0xdc, 0x74, 0x5b, 0x65, 0x02, 0xc5, + 0x85, 0x84, 0x9e, 0x49, 0xd6, 0x4f, 0x00, 0xf2, 0x75, 0x34, 0x0f, 0x53, 0x2c, 0x54, 0x47, 0xc7, + 0x72, 0xa6, 0x58, 0x28, 0x7b, 0xc0, 0xae, 0xfc, 0xec, 0xe8, 0x83, 0x63, 0x39, 0xc9, 0x48, 0x9e, + 0xa7, 0x01, 0x3e, 0x62, 0x69, 0x13, 0xa8, 0x07, 0x6a, 0x96, 0x06, 0x8c, 0x27, 0x67, 0x47, 0x0f, + 0xe4, 0xec, 0x09, 0xee, 0xc7, 0x24, 0xed, 0x79, 0xd4, 0xa0, 0xfe, 0x53, 0x01, 0x2a, 0xe9, 0x6f, + 0x00, 0x7a, 0x64, 0xb6, 0xd1, 0xc5, 0xcf, 0xff, 0x75, 0x48, 0x92, 0xde, 0x4c, 0xd6, 0x6b, 0xdf, + 0xcf, 0x7b, 0xed, 0x7f, 0x4c, 0x4e, 0x1a, 0x72, 0x02, 0x56, 0x36, 0x67, 0xec, 0xb6, 0x30, 0xb6, + 0xdb, 0x1a, 0x54, 0x7b, 0x1e, 0x76, 0x7b, 0x38, 0xe8, 0xf6, 0x89, 0xee, 0x10, 0xe7, 0x1c, 0xe8, + 0x79, 0xf8, 0xb9, 0x9e, 0x49, 0x01, 0xac, 0x73, 0x44, 0x3c, 0x11, 0xa9, 0xa2, 0x68, 0xc0, 0x4b, + 0x3d, 0x53, 0xff, 0x65, 0x0a, 0xaa, 0xc6, 0x9f, 0x8b, 0xec, 0xa1, 0x03, 0x3c, 0x48, 0xe3, 0xa8, + 0x67, 0xd9, 0xb1, 0xf1, 0xa1, 0xfe, 0x96, 0x24, 0x9f, 0xa9, 0x32, 0x1f, 0xaa, 0x8f, 0x02, 0xba, + 0x0d, 0xc0, 0x87, 0x6e, 0x88, 0xbd, 0x63, 0x92, 0xc8, 0x97, 0x1c, 0x8b, 0x0f, 0x5b, 0x7a, 0x02, + 0xdd, 0x04, 0x8b, 0x0f, 0x5d, 0xc2, 0x39, 0xe3, 0x51, 0x52, 0xfb, 0x0a, 0x1f, 0x3e, 0x55, 0xe3, + 0x84, 0xdb, 0xe5, 0x4c, 0xf6, 0x02, 0xc9, 0x3b, 0xb0, 0xf8, 0xf0, 0x89, 0x9e, 0x90, 0x51, 0x45, + 0x1a, 0x55, 0xb7, 0x9e, 0x65, 0x91, 0x47, 0x15, 0x79, 0x54, 0xdd, 0x7a, 0x5a, 0xc2, 0x8c, 0x2a, + 0xb2, 0xa8, 0xba, 0xfb, 0xac, 0x08, 0x23, 0xaa, 0xc8, 0xa3, 0x5a, 0x29, 0x37, 0x89, 0xda, 0xb4, + 0xdf, 0xbd, 0x5f, 0xbb, 0xf2, 0xc7, 0xfb, 0xb5, 0x2b, 0x3f, 0x8e, 0xd6, 0x0a, 0xef, 0x46, 0x6b, + 0x85, 0xdf, 0x46, 0x6b, 0x85, 0xbf, 0x46, 0x6b, 0x85, 0xce, 0x8c, 0xfa, 0x0d, 0xff, 0xff, 0xdf, + 0x01, 0x00, 0x00, 0xff, 0xff, 0x19, 0x9d, 0xe2, 0xd3, 0xe5, 0x0f, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/metrics.pb.txt b/src/runtime/vendor/github.com/containerd/cgroups/metrics.pb.txt new file mode 100644 index 000000000..5b0f10a34 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/metrics.pb.txt @@ -0,0 +1,712 @@ +file { + name: "github.com/containerd/cgroups/metrics.proto" + package: "io.containerd.cgroups.v1" + dependency: "gogoproto/gogo.proto" + message_type { + name: "Metrics" + field { + name: "hugetlb" + number: 1 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.HugetlbStat" + json_name: "hugetlb" + } + field { + name: "pids" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.PidsStat" + json_name: "pids" + } + field { + name: "cpu" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.CPUStat" + options { + 65004: "CPU" + } + json_name: "cpu" + } + field { + name: "memory" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.MemoryStat" + json_name: "memory" + } + field { + name: "blkio" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOStat" + json_name: "blkio" + } + field { + name: "rdma" + number: 6 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.RdmaStat" + json_name: "rdma" + } + field { + name: "network" + number: 7 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.NetworkStat" + json_name: "network" + } + } + message_type { + name: "HugetlbStat" + field { + name: "usage" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "usage" + } + field { + name: "max" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "max" + } + field { + name: "failcnt" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "failcnt" + } + field { + name: "pagesize" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "pagesize" + } + } + message_type { + name: "PidsStat" + field { + name: "current" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "current" + } + field { + name: "limit" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "limit" + } + } + message_type { + name: "CPUStat" + field { + name: "usage" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.CPUUsage" + json_name: "usage" + } + field { + name: "throttling" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.Throttle" + json_name: "throttling" + } + } + message_type { + name: "CPUUsage" + field { + name: "total" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "total" + } + field { + name: "kernel" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "kernel" + } + field { + name: "user" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "user" + } + field { + name: "per_cpu" + number: 4 + label: LABEL_REPEATED + type: TYPE_UINT64 + options { + 65004: "PerCPU" + } + json_name: "perCpu" + } + } + message_type { + name: "Throttle" + field { + name: "periods" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "periods" + } + field { + name: "throttled_periods" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "throttledPeriods" + } + field { + name: "throttled_time" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "throttledTime" + } + } + message_type { + name: "MemoryStat" + field { + name: "cache" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "cache" + } + field { + name: "rss" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + options { + 65004: "RSS" + } + json_name: "rss" + } + field { + name: "rss_huge" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + options { + 65004: "RSSHuge" + } + json_name: "rssHuge" + } + field { + name: "mapped_file" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "mappedFile" + } + field { + name: "dirty" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "dirty" + } + field { + name: "writeback" + number: 6 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "writeback" + } + field { + name: "pg_pg_in" + number: 7 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "pgPgIn" + } + field { + name: "pg_pg_out" + number: 8 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "pgPgOut" + } + field { + name: "pg_fault" + number: 9 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "pgFault" + } + field { + name: "pg_maj_fault" + number: 10 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "pgMajFault" + } + field { + name: "inactive_anon" + number: 11 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "inactiveAnon" + } + field { + name: "active_anon" + number: 12 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "activeAnon" + } + field { + name: "inactive_file" + number: 13 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "inactiveFile" + } + field { + name: "active_file" + number: 14 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "activeFile" + } + field { + name: "unevictable" + number: 15 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "unevictable" + } + field { + name: "hierarchical_memory_limit" + number: 16 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "hierarchicalMemoryLimit" + } + field { + name: "hierarchical_swap_limit" + number: 17 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "hierarchicalSwapLimit" + } + field { + name: "total_cache" + number: 18 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalCache" + } + field { + name: "total_rss" + number: 19 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + options { + 65004: "TotalRSS" + } + json_name: "totalRss" + } + field { + name: "total_rss_huge" + number: 20 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + options { + 65004: "TotalRSSHuge" + } + json_name: "totalRssHuge" + } + field { + name: "total_mapped_file" + number: 21 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalMappedFile" + } + field { + name: "total_dirty" + number: 22 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalDirty" + } + field { + name: "total_writeback" + number: 23 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalWriteback" + } + field { + name: "total_pg_pg_in" + number: 24 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalPgPgIn" + } + field { + name: "total_pg_pg_out" + number: 25 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalPgPgOut" + } + field { + name: "total_pg_fault" + number: 26 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalPgFault" + } + field { + name: "total_pg_maj_fault" + number: 27 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalPgMajFault" + } + field { + name: "total_inactive_anon" + number: 28 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalInactiveAnon" + } + field { + name: "total_active_anon" + number: 29 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalActiveAnon" + } + field { + name: "total_inactive_file" + number: 30 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalInactiveFile" + } + field { + name: "total_active_file" + number: 31 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalActiveFile" + } + field { + name: "total_unevictable" + number: 32 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "totalUnevictable" + } + field { + name: "usage" + number: 33 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.MemoryEntry" + json_name: "usage" + } + field { + name: "swap" + number: 34 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.MemoryEntry" + json_name: "swap" + } + field { + name: "kernel" + number: 35 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.MemoryEntry" + json_name: "kernel" + } + field { + name: "kernel_tcp" + number: 36 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.MemoryEntry" + options { + 65004: "KernelTCP" + } + json_name: "kernelTcp" + } + } + message_type { + name: "MemoryEntry" + field { + name: "limit" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "limit" + } + field { + name: "usage" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "usage" + } + field { + name: "max" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "max" + } + field { + name: "failcnt" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "failcnt" + } + } + message_type { + name: "BlkIOStat" + field { + name: "io_service_bytes_recursive" + number: 1 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioServiceBytesRecursive" + } + field { + name: "io_serviced_recursive" + number: 2 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioServicedRecursive" + } + field { + name: "io_queued_recursive" + number: 3 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioQueuedRecursive" + } + field { + name: "io_service_time_recursive" + number: 4 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioServiceTimeRecursive" + } + field { + name: "io_wait_time_recursive" + number: 5 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioWaitTimeRecursive" + } + field { + name: "io_merged_recursive" + number: 6 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioMergedRecursive" + } + field { + name: "io_time_recursive" + number: 7 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "ioTimeRecursive" + } + field { + name: "sectors_recursive" + number: 8 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.BlkIOEntry" + json_name: "sectorsRecursive" + } + } + message_type { + name: "BlkIOEntry" + field { + name: "op" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "op" + } + field { + name: "device" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "device" + } + field { + name: "major" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "major" + } + field { + name: "minor" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "minor" + } + field { + name: "value" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "value" + } + } + message_type { + name: "RdmaStat" + field { + name: "current" + number: 1 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.RdmaEntry" + json_name: "current" + } + field { + name: "limit" + number: 2 + label: LABEL_REPEATED + type: TYPE_MESSAGE + type_name: ".io.containerd.cgroups.v1.RdmaEntry" + json_name: "limit" + } + } + message_type { + name: "RdmaEntry" + field { + name: "device" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "device" + } + field { + name: "hca_handles" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT32 + json_name: "hcaHandles" + } + field { + name: "hca_objects" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT32 + json_name: "hcaObjects" + } + } + message_type { + name: "NetworkStat" + field { + name: "name" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "name" + } + field { + name: "rx_bytes" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "rxBytes" + } + field { + name: "rx_packets" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "rxPackets" + } + field { + name: "rx_errors" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "rxErrors" + } + field { + name: "rx_dropped" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "rxDropped" + } + field { + name: "tx_bytes" + number: 6 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "txBytes" + } + field { + name: "tx_packets" + number: 7 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "txPackets" + } + field { + name: "tx_errors" + number: 8 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "txErrors" + } + field { + name: "tx_dropped" + number: 9 + label: LABEL_OPTIONAL + type: TYPE_UINT64 + json_name: "txDropped" + } + } + syntax: "proto3" +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/metrics.proto b/src/runtime/vendor/github.com/containerd/cgroups/metrics.proto new file mode 100644 index 000000000..62b519806 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/metrics.proto @@ -0,0 +1,136 @@ +syntax = "proto3"; + +package io.containerd.cgroups.v1; + +import "gogoproto/gogo.proto"; + +message Metrics { + repeated HugetlbStat hugetlb = 1; + PidsStat pids = 2; + CPUStat cpu = 3 [(gogoproto.customname) = "CPU"]; + MemoryStat memory = 4; + BlkIOStat blkio = 5; + RdmaStat rdma = 6; + repeated NetworkStat network = 7; +} + +message HugetlbStat { + uint64 usage = 1; + uint64 max = 2; + uint64 failcnt = 3; + string pagesize = 4; +} + +message PidsStat { + uint64 current = 1; + uint64 limit = 2; +} + +message CPUStat { + CPUUsage usage = 1; + Throttle throttling = 2; +} + +message CPUUsage { + // values in nanoseconds + uint64 total = 1; + uint64 kernel = 2; + uint64 user = 3; + repeated uint64 per_cpu = 4 [(gogoproto.customname) = "PerCPU"]; + +} + +message Throttle { + uint64 periods = 1; + uint64 throttled_periods = 2; + uint64 throttled_time = 3; +} + +message MemoryStat { + uint64 cache = 1; + uint64 rss = 2 [(gogoproto.customname) = "RSS"]; + uint64 rss_huge = 3 [(gogoproto.customname) = "RSSHuge"]; + uint64 mapped_file = 4; + uint64 dirty = 5; + uint64 writeback = 6; + uint64 pg_pg_in = 7; + uint64 pg_pg_out = 8; + uint64 pg_fault = 9; + uint64 pg_maj_fault = 10; + uint64 inactive_anon = 11; + uint64 active_anon = 12; + uint64 inactive_file = 13; + uint64 active_file = 14; + uint64 unevictable = 15; + uint64 hierarchical_memory_limit = 16; + uint64 hierarchical_swap_limit = 17; + uint64 total_cache = 18; + uint64 total_rss = 19 [(gogoproto.customname) = "TotalRSS"]; + uint64 total_rss_huge = 20 [(gogoproto.customname) = "TotalRSSHuge"]; + uint64 total_mapped_file = 21; + uint64 total_dirty = 22; + uint64 total_writeback = 23; + uint64 total_pg_pg_in = 24; + uint64 total_pg_pg_out = 25; + uint64 total_pg_fault = 26; + uint64 total_pg_maj_fault = 27; + uint64 total_inactive_anon = 28; + uint64 total_active_anon = 29; + uint64 total_inactive_file = 30; + uint64 total_active_file = 31; + uint64 total_unevictable = 32; + MemoryEntry usage = 33; + MemoryEntry swap = 34; + MemoryEntry kernel = 35; + MemoryEntry kernel_tcp = 36 [(gogoproto.customname) = "KernelTCP"]; + +} + +message MemoryEntry { + uint64 limit = 1; + uint64 usage = 2; + uint64 max = 3; + uint64 failcnt = 4; +} + +message BlkIOStat { + repeated BlkIOEntry io_service_bytes_recursive = 1; + repeated BlkIOEntry io_serviced_recursive = 2; + repeated BlkIOEntry io_queued_recursive = 3; + repeated BlkIOEntry io_service_time_recursive = 4; + repeated BlkIOEntry io_wait_time_recursive = 5; + repeated BlkIOEntry io_merged_recursive = 6; + repeated BlkIOEntry io_time_recursive = 7; + repeated BlkIOEntry sectors_recursive = 8; +} + +message BlkIOEntry { + string op = 1; + string device = 2; + uint64 major = 3; + uint64 minor = 4; + uint64 value = 5; +} + +message RdmaStat { + repeated RdmaEntry current = 1; + repeated RdmaEntry limit = 2; +} + +message RdmaEntry { + string device = 1; + uint32 hca_handles = 2; + uint32 hca_objects = 3; +} + +message NetworkStat { + string name = 1; + uint64 rx_bytes = 2; + uint64 rx_packets = 3; + uint64 rx_errors = 4; + uint64 rx_dropped = 5; + uint64 tx_bytes = 6; + uint64 tx_packets = 7; + uint64 tx_errors = 8; + uint64 tx_dropped = 9; +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/named.go b/src/runtime/vendor/github.com/containerd/cgroups/named.go new file mode 100644 index 000000000..06b16c3b1 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/named.go @@ -0,0 +1,39 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import "path/filepath" + +func NewNamed(root string, name Name) *namedController { + return &namedController{ + root: root, + name: name, + } +} + +type namedController struct { + root string + name Name +} + +func (n *namedController) Name() Name { + return n.name +} + +func (n *namedController) Path(path string) string { + return filepath.Join(n.root, string(n.name), path) +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/net_cls.go b/src/runtime/vendor/github.com/containerd/cgroups/net_cls.go new file mode 100644 index 000000000..8f1a2651f --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/net_cls.go @@ -0,0 +1,58 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "io/ioutil" + "os" + "path/filepath" + "strconv" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func NewNetCls(root string) *netclsController { + return &netclsController{ + root: filepath.Join(root, string(NetCLS)), + } +} + +type netclsController struct { + root string +} + +func (n *netclsController) Name() Name { + return NetCLS +} + +func (n *netclsController) Path(path string) string { + return filepath.Join(n.root, path) +} + +func (n *netclsController) Create(path string, resources *specs.LinuxResources) error { + if err := os.MkdirAll(n.Path(path), defaultDirPerm); err != nil { + return err + } + if resources.Network != nil && resources.Network.ClassID != nil && *resources.Network.ClassID > 0 { + return ioutil.WriteFile( + filepath.Join(n.Path(path), "net_cls.classid"), + []byte(strconv.FormatUint(uint64(*resources.Network.ClassID), 10)), + defaultFilePerm, + ) + } + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/net_prio.go b/src/runtime/vendor/github.com/containerd/cgroups/net_prio.go new file mode 100644 index 000000000..612e1bcd2 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/net_prio.go @@ -0,0 +1,66 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func NewNetPrio(root string) *netprioController { + return &netprioController{ + root: filepath.Join(root, string(NetPrio)), + } +} + +type netprioController struct { + root string +} + +func (n *netprioController) Name() Name { + return NetPrio +} + +func (n *netprioController) Path(path string) string { + return filepath.Join(n.root, path) +} + +func (n *netprioController) Create(path string, resources *specs.LinuxResources) error { + if err := os.MkdirAll(n.Path(path), defaultDirPerm); err != nil { + return err + } + if resources.Network != nil { + for _, prio := range resources.Network.Priorities { + if err := ioutil.WriteFile( + filepath.Join(n.Path(path), "net_prio.ifpriomap"), + formatPrio(prio.Name, prio.Priority), + defaultFilePerm, + ); err != nil { + return err + } + } + } + return nil +} + +func formatPrio(name string, prio uint32) []byte { + return []byte(fmt.Sprintf("%s %d", name, prio)) +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/opts.go b/src/runtime/vendor/github.com/containerd/cgroups/opts.go new file mode 100644 index 000000000..7c5d9fb9c --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/opts.go @@ -0,0 +1,61 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "github.com/pkg/errors" +) + +var ( + // ErrIgnoreSubsystem allows the specific subsystem to be skipped + ErrIgnoreSubsystem = errors.New("skip subsystem") + // ErrDevicesRequired is returned when the devices subsystem is required but + // does not exist or is not active + ErrDevicesRequired = errors.New("devices subsystem is required") +) + +// InitOpts allows configuration for the creation or loading of a cgroup +type InitOpts func(*InitConfig) error + +// InitConfig provides configuration options for the creation +// or loading of a cgroup and its subsystems +type InitConfig struct { + // InitCheck can be used to check initialization errors from the subsystem + InitCheck InitCheck +} + +func newInitConfig() *InitConfig { + return &InitConfig{ + InitCheck: RequireDevices, + } +} + +// InitCheck allows subsystems errors to be checked when initialized or loaded +type InitCheck func(Subsystem, Path, error) error + +// AllowAny allows any subsystem errors to be skipped +func AllowAny(s Subsystem, p Path, err error) error { + return ErrIgnoreSubsystem +} + +// RequireDevices requires the device subsystem but no others +func RequireDevices(s Subsystem, p Path, err error) error { + if s.Name() == Devices { + return ErrDevicesRequired + } + return ErrIgnoreSubsystem +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/paths.go b/src/runtime/vendor/github.com/containerd/cgroups/paths.go new file mode 100644 index 000000000..f45fd4256 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/paths.go @@ -0,0 +1,107 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "fmt" + "path/filepath" + + "github.com/pkg/errors" +) + +type Path func(subsystem Name) (string, error) + +func RootPath(subsysem Name) (string, error) { + return "/", nil +} + +// StaticPath returns a static path to use for all cgroups +func StaticPath(path string) Path { + return func(_ Name) (string, error) { + return path, nil + } +} + +// NestedPath will nest the cgroups based on the calling processes cgroup +// placing its child processes inside its own path +func NestedPath(suffix string) Path { + paths, err := parseCgroupFile("/proc/self/cgroup") + if err != nil { + return errorPath(err) + } + return existingPath(paths, suffix) +} + +// PidPath will return the correct cgroup paths for an existing process running inside a cgroup +// This is commonly used for the Load function to restore an existing container +func PidPath(pid int) Path { + p := fmt.Sprintf("/proc/%d/cgroup", pid) + paths, err := parseCgroupFile(p) + if err != nil { + return errorPath(errors.Wrapf(err, "parse cgroup file %s", p)) + } + return existingPath(paths, "") +} + +// ErrControllerNotActive is returned when a controller is not supported or enabled +var ErrControllerNotActive = errors.New("controller is not supported") + +func existingPath(paths map[string]string, suffix string) Path { + // localize the paths based on the root mount dest for nested cgroups + for n, p := range paths { + dest, err := getCgroupDestination(string(n)) + if err != nil { + return errorPath(err) + } + rel, err := filepath.Rel(dest, p) + if err != nil { + return errorPath(err) + } + if rel == "." { + rel = dest + } + paths[n] = filepath.Join("/", rel) + } + return func(name Name) (string, error) { + root, ok := paths[string(name)] + if !ok { + if root, ok = paths[fmt.Sprintf("name=%s", name)]; !ok { + return "", ErrControllerNotActive + } + } + if suffix != "" { + return filepath.Join(root, suffix), nil + } + return root, nil + } +} + +func subPath(path Path, subName string) Path { + return func(name Name) (string, error) { + p, err := path(name) + if err != nil { + return "", err + } + return filepath.Join(p, subName), nil + } +} + +func errorPath(err error) Path { + return func(_ Name) (string, error) { + return "", err + } +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/perf_event.go b/src/runtime/vendor/github.com/containerd/cgroups/perf_event.go new file mode 100644 index 000000000..648786db6 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/perf_event.go @@ -0,0 +1,37 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import "path/filepath" + +func NewPerfEvent(root string) *PerfEventController { + return &PerfEventController{ + root: filepath.Join(root, string(PerfEvent)), + } +} + +type PerfEventController struct { + root string +} + +func (p *PerfEventController) Name() Name { + return PerfEvent +} + +func (p *PerfEventController) Path(path string) string { + return filepath.Join(p.root, path) +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/pids.go b/src/runtime/vendor/github.com/containerd/cgroups/pids.go new file mode 100644 index 000000000..a1cfcb88d --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/pids.go @@ -0,0 +1,85 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func NewPids(root string) *pidsController { + return &pidsController{ + root: filepath.Join(root, string(Pids)), + } +} + +type pidsController struct { + root string +} + +func (p *pidsController) Name() Name { + return Pids +} + +func (p *pidsController) Path(path string) string { + return filepath.Join(p.root, path) +} + +func (p *pidsController) Create(path string, resources *specs.LinuxResources) error { + if err := os.MkdirAll(p.Path(path), defaultDirPerm); err != nil { + return err + } + if resources.Pids != nil && resources.Pids.Limit > 0 { + return ioutil.WriteFile( + filepath.Join(p.Path(path), "pids.max"), + []byte(strconv.FormatInt(resources.Pids.Limit, 10)), + defaultFilePerm, + ) + } + return nil +} + +func (p *pidsController) Update(path string, resources *specs.LinuxResources) error { + return p.Create(path, resources) +} + +func (p *pidsController) Stat(path string, stats *Metrics) error { + current, err := readUint(filepath.Join(p.Path(path), "pids.current")) + if err != nil { + return err + } + var max uint64 + maxData, err := ioutil.ReadFile(filepath.Join(p.Path(path), "pids.max")) + if err != nil { + return err + } + if maxS := strings.TrimSpace(string(maxData)); maxS != "max" { + if max, err = parseUint(maxS, 10, 64); err != nil { + return err + } + } + stats.Pids = &PidsStat{ + Current: current, + Limit: max, + } + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/rdma.go b/src/runtime/vendor/github.com/containerd/cgroups/rdma.go new file mode 100644 index 000000000..4f423d33a --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/rdma.go @@ -0,0 +1,153 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "io/ioutil" + "math" + "os" + "path/filepath" + "strconv" + "strings" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +type rdmaController struct { + root string +} + +func (p *rdmaController) Name() Name { + return Rdma +} + +func (p *rdmaController) Path(path string) string { + return filepath.Join(p.root, path) +} + +func NewRdma(root string) *rdmaController { + return &rdmaController{ + root: filepath.Join(root, string(Rdma)), + } +} + +func createCmdString(device string, limits *specs.LinuxRdma) string { + var cmdString string + + cmdString = device + if limits.HcaHandles != nil { + cmdString = cmdString + " " + "hca_handle=" + strconv.FormatUint(uint64(*limits.HcaHandles), 10) + } + + if limits.HcaObjects != nil { + cmdString = cmdString + " " + "hca_object=" + strconv.FormatUint(uint64(*limits.HcaObjects), 10) + } + return cmdString +} + +func (p *rdmaController) Create(path string, resources *specs.LinuxResources) error { + if err := os.MkdirAll(p.Path(path), defaultDirPerm); err != nil { + return err + } + + for device, limit := range resources.Rdma { + if device != "" && (limit.HcaHandles != nil || limit.HcaObjects != nil) { + return ioutil.WriteFile( + filepath.Join(p.Path(path), "rdma.max"), + []byte(createCmdString(device, &limit)), + defaultFilePerm, + ) + } + } + return nil +} + +func (p *rdmaController) Update(path string, resources *specs.LinuxResources) error { + return p.Create(path, resources) +} + +func parseRdmaKV(raw string, entry *RdmaEntry) { + var value uint64 + var err error + + parts := strings.Split(raw, "=") + switch len(parts) { + case 2: + if parts[1] == "max" { + value = math.MaxUint32 + } else { + value, err = parseUint(parts[1], 10, 32) + if err != nil { + return + } + } + if parts[0] == "hca_handle" { + entry.HcaHandles = uint32(value) + } else if parts[0] == "hca_object" { + entry.HcaObjects = uint32(value) + } + } +} + +func toRdmaEntry(strEntries []string) []*RdmaEntry { + var rdmaEntries []*RdmaEntry + for i := range strEntries { + parts := strings.Fields(strEntries[i]) + switch len(parts) { + case 3: + entry := new(RdmaEntry) + entry.Device = parts[0] + parseRdmaKV(parts[1], entry) + parseRdmaKV(parts[2], entry) + + rdmaEntries = append(rdmaEntries, entry) + default: + continue + } + } + return rdmaEntries +} + +func (p *rdmaController) Stat(path string, stats *Metrics) error { + + currentData, err := ioutil.ReadFile(filepath.Join(p.Path(path), "rdma.current")) + if err != nil { + return err + } + currentPerDevices := strings.Split(string(currentData), "\n") + + maxData, err := ioutil.ReadFile(filepath.Join(p.Path(path), "rdma.max")) + if err != nil { + return err + } + maxPerDevices := strings.Split(string(maxData), "\n") + + // If device got removed between reading two files, ignore returning + // stats. + if len(currentPerDevices) != len(maxPerDevices) { + return nil + } + + currentEntries := toRdmaEntry(currentPerDevices) + maxEntries := toRdmaEntry(maxPerDevices) + + stats.Rdma = &RdmaStat{ + Current: currentEntries, + Limit: maxEntries, + } + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/state.go b/src/runtime/vendor/github.com/containerd/cgroups/state.go new file mode 100644 index 000000000..cfeabbbc6 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/state.go @@ -0,0 +1,28 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +// State is a type that represents the state of the current cgroup +type State string + +const ( + Unknown State = "" + Thawed State = "thawed" + Frozen State = "frozen" + Freezing State = "freezing" + Deleted State = "deleted" +) diff --git a/src/runtime/vendor/github.com/containerd/cgroups/subsystem.go b/src/runtime/vendor/github.com/containerd/cgroups/subsystem.go new file mode 100644 index 000000000..23de04d49 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/subsystem.go @@ -0,0 +1,112 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "fmt" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +// Name is a typed name for a cgroup subsystem +type Name string + +const ( + Devices Name = "devices" + Hugetlb Name = "hugetlb" + Freezer Name = "freezer" + Pids Name = "pids" + NetCLS Name = "net_cls" + NetPrio Name = "net_prio" + PerfEvent Name = "perf_event" + Cpuset Name = "cpuset" + Cpu Name = "cpu" + Cpuacct Name = "cpuacct" + Memory Name = "memory" + Blkio Name = "blkio" + Rdma Name = "rdma" +) + +// Subsystems returns a complete list of the default cgroups +// available on most linux systems +func Subsystems() []Name { + n := []Name{ + Hugetlb, + Freezer, + Pids, + NetCLS, + NetPrio, + PerfEvent, + Cpuset, + Cpu, + Cpuacct, + Memory, + Blkio, + Rdma, + } + if !isUserNS { + n = append(n, Devices) + } + return n +} + +type Subsystem interface { + Name() Name +} + +type pather interface { + Subsystem + Path(path string) string +} + +type creator interface { + Subsystem + Create(path string, resources *specs.LinuxResources) error +} + +type deleter interface { + Subsystem + Delete(path string) error +} + +type stater interface { + Subsystem + Stat(path string, stats *Metrics) error +} + +type updater interface { + Subsystem + Update(path string, resources *specs.LinuxResources) error +} + +// SingleSubsystem returns a single cgroup subsystem within the base Hierarchy +func SingleSubsystem(baseHierarchy Hierarchy, subsystem Name) Hierarchy { + return func() ([]Subsystem, error) { + subsystems, err := baseHierarchy() + if err != nil { + return nil, err + } + for _, s := range subsystems { + if s.Name() == subsystem { + return []Subsystem{ + s, + }, nil + } + } + return nil, fmt.Errorf("unable to find subsystem %s", subsystem) + } +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/systemd.go b/src/runtime/vendor/github.com/containerd/cgroups/systemd.go new file mode 100644 index 000000000..c5d4e3081 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/systemd.go @@ -0,0 +1,160 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "fmt" + "path/filepath" + "strings" + "sync" + + systemdDbus "github.com/coreos/go-systemd/dbus" + "github.com/godbus/dbus" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +const ( + SystemdDbus Name = "systemd" + defaultSlice = "system.slice" +) + +var ( + canDelegate bool + once sync.Once +) + +func Systemd() ([]Subsystem, error) { + root, err := v1MountPoint() + if err != nil { + return nil, err + } + defaultSubsystems, err := defaults(root) + if err != nil { + return nil, err + } + s, err := NewSystemd(root) + if err != nil { + return nil, err + } + // make sure the systemd controller is added first + return append([]Subsystem{s}, defaultSubsystems...), nil +} + +func Slice(slice, name string) Path { + if slice == "" { + slice = defaultSlice + } + return func(subsystem Name) (string, error) { + return filepath.Join(slice, name), nil + } +} + +func NewSystemd(root string) (*SystemdController, error) { + return &SystemdController{ + root: root, + }, nil +} + +type SystemdController struct { + mu sync.Mutex + root string +} + +func (s *SystemdController) Name() Name { + return SystemdDbus +} + +func (s *SystemdController) Create(path string, resources *specs.LinuxResources) error { + conn, err := systemdDbus.New() + if err != nil { + return err + } + defer conn.Close() + slice, name := splitName(path) + // We need to see if systemd can handle the delegate property + // Systemd will return an error if it cannot handle delegate regardless + // of its bool setting. + checkDelegate := func() { + canDelegate = true + dlSlice := newProperty("Delegate", true) + if _, err := conn.StartTransientUnit(slice, "testdelegate", []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") { + canDelegate = false + } + } + } + + conn.StopUnit(slice, "testDelegate", nil) + } + once.Do(checkDelegate) + properties := []systemdDbus.Property{ + systemdDbus.PropDescription(fmt.Sprintf("cgroup %s", name)), + systemdDbus.PropWants(slice), + newProperty("DefaultDependencies", false), + newProperty("MemoryAccounting", true), + newProperty("CPUAccounting", true), + newProperty("BlockIOAccounting", true), + } + + // If we can delegate, we add the property back in + if canDelegate { + properties = append(properties, newProperty("Delegate", true)) + } + + ch := make(chan string) + _, err = conn.StartTransientUnit(name, "replace", properties, ch) + if err != nil { + return err + } + <-ch + return nil +} + +func (s *SystemdController) Delete(path string) error { + conn, err := systemdDbus.New() + if err != nil { + return err + } + defer conn.Close() + _, name := splitName(path) + ch := make(chan string) + _, err = conn.StopUnit(name, "replace", ch) + if err != nil { + return err + } + <-ch + return nil +} + +func newProperty(name string, units interface{}) systemdDbus.Property { + return systemdDbus.Property{ + Name: name, + Value: dbus.MakeVariant(units), + } +} + +func unitName(name string) string { + return fmt.Sprintf("%s.slice", name) +} + +func splitName(path string) (slice string, unit string) { + slice, unit = filepath.Split(path) + return strings.TrimSuffix(slice, "/"), unit +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/ticks.go b/src/runtime/vendor/github.com/containerd/cgroups/ticks.go new file mode 100644 index 000000000..84dc38d0c --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/ticks.go @@ -0,0 +1,26 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +func getClockTicks() uint64 { + // The value comes from `C.sysconf(C._SC_CLK_TCK)`, and + // on Linux it's a constant which is safe to be hard coded, + // so we can avoid using cgo here. + // See https://github.com/containerd/cgroups/pull/12 for + // more details. + return 100 +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/utils.go b/src/runtime/vendor/github.com/containerd/cgroups/utils.go new file mode 100644 index 000000000..8a97d04dd --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/utils.go @@ -0,0 +1,324 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + units "github.com/docker/go-units" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +var isUserNS = runningInUserNS() + +// runningInUserNS detects whether we are currently running in a user namespace. +// Copied from github.com/lxc/lxd/shared/util.go +func runningInUserNS() bool { + file, err := os.Open("/proc/self/uid_map") + if err != nil { + // This kernel-provided file only exists if user namespaces are supported + return false + } + defer file.Close() + + 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) + /* + * 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 { + return false + } + return true +} + +// defaults returns all known groups +func defaults(root string) ([]Subsystem, error) { + h, err := NewHugetlb(root) + if err != nil && !os.IsNotExist(err) { + return nil, err + } + s := []Subsystem{ + NewNamed(root, "systemd"), + NewFreezer(root), + NewPids(root), + NewNetCls(root), + NewNetPrio(root), + NewPerfEvent(root), + NewCputset(root), + NewCpu(root), + NewCpuacct(root), + NewMemory(root), + NewBlkio(root), + NewRdma(root), + } + // only add the devices cgroup if we are not in a user namespace + // because modifications are not allowed + if !isUserNS { + s = append(s, NewDevices(root)) + } + // add the hugetlb cgroup if error wasn't due to missing hugetlb + // cgroup support on the host + if err == nil { + s = append(s, h) + } + return s, nil +} + +// remove will remove a cgroup path handling EAGAIN and EBUSY errors and +// retrying the remove after a exp timeout +func remove(path string) error { + delay := 10 * time.Millisecond + for i := 0; i < 5; i++ { + if i != 0 { + time.Sleep(delay) + delay *= 2 + } + if err := os.RemoveAll(path); err == nil { + return nil + } + } + return fmt.Errorf("cgroups: unable to remove path %q", path) +} + +// readPids will read all the pids of processes in a cgroup by the provided path +func readPids(path string, subsystem Name) ([]Process, error) { + f, err := os.Open(filepath.Join(path, cgroupProcs)) + if err != nil { + return nil, err + } + defer f.Close() + var ( + out []Process + s = bufio.NewScanner(f) + ) + for s.Scan() { + if t := s.Text(); t != "" { + pid, err := strconv.Atoi(t) + if err != nil { + return nil, err + } + out = append(out, Process{ + Pid: pid, + Subsystem: subsystem, + Path: path, + }) + } + } + return out, nil +} + +// readTasksPids will read all the pids of tasks in a cgroup by the provided path +func readTasksPids(path string, subsystem Name) ([]Task, error) { + f, err := os.Open(filepath.Join(path, cgroupTasks)) + if err != nil { + return nil, err + } + defer f.Close() + var ( + out []Task + s = bufio.NewScanner(f) + ) + for s.Scan() { + if t := s.Text(); t != "" { + pid, err := strconv.Atoi(t) + if err != nil { + return nil, err + } + out = append(out, Task{ + Pid: pid, + Subsystem: subsystem, + Path: path, + }) + } + } + return out, nil +} + +func hugePageSizes() ([]string, error) { + var ( + pageSizes []string + sizeList = []string{"B", "KB", "MB", "GB", "TB", "PB"} + ) + files, err := ioutil.ReadDir("/sys/kernel/mm/hugepages") + if err != nil { + return nil, err + } + for _, st := range files { + nameArray := strings.Split(st.Name(), "-") + pageSize, err := units.RAMInBytes(nameArray[1]) + if err != nil { + return nil, err + } + pageSizes = append(pageSizes, units.CustomSize("%g%s", float64(pageSize), 1024.0, sizeList)) + } + return pageSizes, nil +} + +func readUint(path string) (uint64, error) { + v, err := ioutil.ReadFile(path) + if err != nil { + return 0, err + } + return parseUint(strings.TrimSpace(string(v)), 10, 64) +} + +func parseUint(s string, base, bitSize int) (uint64, error) { + v, err := strconv.ParseUint(s, base, bitSize) + if err != nil { + intValue, intErr := strconv.ParseInt(s, base, bitSize) + // 1. Handle negative values greater than MinInt64 (and) + // 2. Handle negative values lesser than MinInt64 + if intErr == nil && intValue < 0 { + return 0, nil + } else if intErr != nil && + intErr.(*strconv.NumError).Err == strconv.ErrRange && + intValue < 0 { + return 0, nil + } + return 0, err + } + return v, nil +} + +func parseKV(raw string) (string, uint64, error) { + parts := strings.Fields(raw) + switch len(parts) { + case 2: + v, err := parseUint(parts[1], 10, 64) + if err != nil { + return "", 0, err + } + return parts[0], v, nil + default: + return "", 0, ErrInvalidFormat + } +} + +func parseCgroupFile(path string) (map[string]string, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + return parseCgroupFromReader(f) +} + +func parseCgroupFromReader(r io.Reader) (map[string]string, error) { + var ( + cgroups = make(map[string]string) + s = bufio.NewScanner(r) + ) + for s.Scan() { + if err := s.Err(); err != nil { + return nil, err + } + var ( + text = s.Text() + parts = strings.SplitN(text, ":", 3) + ) + if len(parts) < 3 { + return nil, fmt.Errorf("invalid cgroup entry: %q", text) + } + for _, subs := range strings.Split(parts[1], ",") { + if subs != "" { + cgroups[subs] = parts[2] + } + } + } + return cgroups, nil +} + +func getCgroupDestination(subsystem string) (string, error) { + f, err := os.Open("/proc/self/mountinfo") + if err != nil { + return "", err + } + defer f.Close() + s := bufio.NewScanner(f) + for s.Scan() { + if err := s.Err(); err != nil { + return "", err + } + fields := strings.Fields(s.Text()) + for _, opt := range strings.Split(fields[len(fields)-1], ",") { + if opt == subsystem { + return fields[3], nil + } + } + } + return "", ErrNoCgroupMountDestination +} + +func pathers(subystems []Subsystem) []pather { + var out []pather + for _, s := range subystems { + if p, ok := s.(pather); ok { + out = append(out, p) + } + } + return out +} + +func initializeSubsystem(s Subsystem, path Path, resources *specs.LinuxResources) error { + if c, ok := s.(creator); ok { + p, err := path(s.Name()) + if err != nil { + return err + } + if err := c.Create(p, resources); err != nil { + return err + } + } else if c, ok := s.(pather); ok { + p, err := path(s.Name()) + if err != nil { + return err + } + // do the default create if the group does not have a custom one + if err := os.MkdirAll(c.Path(p), defaultDirPerm); err != nil { + return err + } + } + return nil +} + +func cleanPath(path string) string { + if path == "" { + return "" + } + path = filepath.Clean(path) + if !filepath.IsAbs(path) { + path, _ = filepath.Rel(string(os.PathSeparator), filepath.Clean(string(os.PathSeparator)+path)) + } + return filepath.Clean(path) +} diff --git a/src/runtime/vendor/github.com/containerd/cgroups/v1.go b/src/runtime/vendor/github.com/containerd/cgroups/v1.go new file mode 100644 index 000000000..a076d4692 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cgroups/v1.go @@ -0,0 +1,81 @@ +/* + Copyright The containerd Authors. + + 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 cgroups + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "strings" +) + +// V1 returns all the groups in the default cgroups mountpoint in a single hierarchy +func V1() ([]Subsystem, error) { + root, err := v1MountPoint() + if err != nil { + return nil, err + } + subsystems, err := defaults(root) + if err != nil { + return nil, err + } + var enabled []Subsystem + for _, s := range pathers(subsystems) { + // check and remove the default groups that do not exist + if _, err := os.Lstat(s.Path("/")); err == nil { + enabled = append(enabled, s) + } + } + return enabled, nil +} + +// v1MountPoint returns the mount point where the cgroup +// mountpoints are mounted in a single hiearchy +func v1MountPoint() (string, error) { + f, err := os.Open("/proc/self/mountinfo") + if err != nil { + return "", err + } + defer f.Close() + scanner := bufio.NewScanner(f) + for scanner.Scan() { + if err := scanner.Err(); err != nil { + return "", err + } + var ( + text = scanner.Text() + fields = strings.Split(text, " ") + // safe as mountinfo encodes mountpoints with spaces as \040. + index = strings.Index(text, " - ") + postSeparatorFields = strings.Fields(text[index+3:]) + numPostFields = len(postSeparatorFields) + ) + // this is an error as we can't detect if the mount is for "cgroup" + if numPostFields == 0 { + return "", fmt.Errorf("Found no fields post '-' in %q", text) + } + if postSeparatorFields[0] == "cgroup" { + // check that the mount is properly formated. + if numPostFields < 3 { + return "", fmt.Errorf("Error found less than 3 fields post '-' in %q", text) + } + return filepath.Dir(fields[4]), nil + } + } + return "", ErrMountPointNotExist +} diff --git a/src/runtime/vendor/github.com/containerd/console/.travis.yml b/src/runtime/vendor/github.com/containerd/console/.travis.yml new file mode 100644 index 000000000..16827ec3e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/.travis.yml @@ -0,0 +1,27 @@ +language: go +go: + - "1.12.x" + - "1.13.x" + +go_import_path: github.com/containerd/console + +env: + - GO111MODULE=on + +install: + - pushd ..; go get -u github.com/vbatts/git-validation; popd + - pushd ..; go get -u github.com/kunalkushwaha/ltag; popd + +before_script: + - pushd ..; git clone https://github.com/containerd/project; popd + +script: + - DCO_VERBOSITY=-q ../project/script/validate/dco + - ../project/script/validate/fileheader ../project/ + - travis_wait ../project/script/validate/vendor + - go test -race + - GOOS=openbsd go build + - GOOS=openbsd go test -c + - GOOS=solaris go build + - GOOS=solaris go test -c + - GOOS=windows go test diff --git a/src/runtime/vendor/github.com/containerd/console/LICENSE b/src/runtime/vendor/github.com/containerd/console/LICENSE new file mode 100644 index 000000000..584149b6e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright The containerd Authors + + 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 + + https://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. diff --git a/src/runtime/vendor/github.com/containerd/console/README.md b/src/runtime/vendor/github.com/containerd/console/README.md new file mode 100644 index 000000000..5392fdaf1 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/README.md @@ -0,0 +1,27 @@ +# console + +[![Build Status](https://travis-ci.org/containerd/console.svg?branch=master)](https://travis-ci.org/containerd/console) + +Golang package for dealing with consoles. Light on deps and a simple API. + +## Modifying the current process + +```go +current := console.Current() +defer current.Reset() + +if err := current.SetRaw(); err != nil { +} +ws, err := current.Size() +current.Resize(ws) +``` + +## Project details + +console is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/src/runtime/vendor/github.com/containerd/console/console.go b/src/runtime/vendor/github.com/containerd/console/console.go new file mode 100644 index 000000000..6a36d1477 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/console.go @@ -0,0 +1,81 @@ +/* + Copyright The containerd Authors. + + 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 console + +import ( + "errors" + "io" + "os" +) + +var ErrNotAConsole = errors.New("provided file is not a console") + +type File interface { + io.ReadWriteCloser + + // Fd returns its file descriptor + Fd() uintptr + // Name returns its file name + Name() string +} + +type Console interface { + File + + // Resize resizes the console to the provided window size + Resize(WinSize) error + // ResizeFrom resizes the calling console to the size of the + // provided console + ResizeFrom(Console) error + // SetRaw sets the console in raw mode + SetRaw() error + // DisableEcho disables echo on the console + DisableEcho() error + // Reset restores the console to its orignal state + Reset() error + // Size returns the window size of the console + Size() (WinSize, error) +} + +// WinSize specifies the window size of the console +type WinSize struct { + // Height of the console + Height uint16 + // Width of the console + Width uint16 + x uint16 + y uint16 +} + +// Current returns the current processes console +func Current() Console { + c, err := ConsoleFromFile(os.Stdin) + if err != nil { + // stdin should always be a console for the design + // of this function + panic(err) + } + return c +} + +// ConsoleFromFile returns a console using the provided file +func ConsoleFromFile(f File) (Console, error) { + if err := checkConsole(f); err != nil { + return nil, err + } + return newMaster(f) +} diff --git a/src/runtime/vendor/github.com/containerd/console/console_linux.go b/src/runtime/vendor/github.com/containerd/console/console_linux.go new file mode 100644 index 000000000..42274e100 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/console_linux.go @@ -0,0 +1,275 @@ +// +build linux + +/* + Copyright The containerd Authors. + + 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 console + +import ( + "io" + "os" + "sync" + + "golang.org/x/sys/unix" +) + +const ( + maxEvents = 128 +) + +// Epoller manages multiple epoll consoles using edge-triggered epoll api so we +// dont have to deal with repeated wake-up of EPOLLER or EPOLLHUP. +// For more details, see: +// - https://github.com/systemd/systemd/pull/4262 +// - https://github.com/moby/moby/issues/27202 +// +// Example usage of Epoller and EpollConsole can be as follow: +// +// epoller, _ := NewEpoller() +// epollConsole, _ := epoller.Add(console) +// go epoller.Wait() +// var ( +// b bytes.Buffer +// wg sync.WaitGroup +// ) +// wg.Add(1) +// go func() { +// io.Copy(&b, epollConsole) +// wg.Done() +// }() +// // perform I/O on the console +// epollConsole.Shutdown(epoller.CloseConsole) +// wg.Wait() +// epollConsole.Close() +type Epoller struct { + efd int + mu sync.Mutex + fdMapping map[int]*EpollConsole +} + +// NewEpoller returns an instance of epoller with a valid epoll fd. +func NewEpoller() (*Epoller, error) { + efd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC) + if err != nil { + return nil, err + } + return &Epoller{ + efd: efd, + fdMapping: make(map[int]*EpollConsole), + }, nil +} + +// Add creates an epoll console based on the provided console. The console will +// be registered with EPOLLET (i.e. using edge-triggered notification) and its +// file descriptor will be set to non-blocking mode. After this, user should use +// the return console to perform I/O. +func (e *Epoller) Add(console Console) (*EpollConsole, error) { + sysfd := int(console.Fd()) + // Set sysfd to non-blocking mode + if err := unix.SetNonblock(sysfd, true); err != nil { + return nil, err + } + + ev := unix.EpollEvent{ + Events: unix.EPOLLIN | unix.EPOLLOUT | unix.EPOLLRDHUP | unix.EPOLLET, + Fd: int32(sysfd), + } + if err := unix.EpollCtl(e.efd, unix.EPOLL_CTL_ADD, sysfd, &ev); err != nil { + return nil, err + } + ef := &EpollConsole{ + Console: console, + sysfd: sysfd, + readc: sync.NewCond(&sync.Mutex{}), + writec: sync.NewCond(&sync.Mutex{}), + } + e.mu.Lock() + e.fdMapping[sysfd] = ef + e.mu.Unlock() + return ef, nil +} + +// Wait starts the loop to wait for its consoles' notifications and signal +// appropriate console that it can perform I/O. +func (e *Epoller) Wait() error { + events := make([]unix.EpollEvent, maxEvents) + for { + n, err := unix.EpollWait(e.efd, events, -1) + if err != nil { + // EINTR: The call was interrupted by a signal handler before either + // any of the requested events occurred or the timeout expired + if err == unix.EINTR { + continue + } + return err + } + for i := 0; i < n; i++ { + ev := &events[i] + // the console is ready to be read from + if ev.Events&(unix.EPOLLIN|unix.EPOLLHUP|unix.EPOLLERR) != 0 { + if epfile := e.getConsole(int(ev.Fd)); epfile != nil { + epfile.signalRead() + } + } + // the console is ready to be written to + if ev.Events&(unix.EPOLLOUT|unix.EPOLLHUP|unix.EPOLLERR) != 0 { + if epfile := e.getConsole(int(ev.Fd)); epfile != nil { + epfile.signalWrite() + } + } + } + } +} + +// CloseConsole unregisters the console's file descriptor from epoll interface +func (e *Epoller) CloseConsole(fd int) error { + e.mu.Lock() + defer e.mu.Unlock() + delete(e.fdMapping, fd) + return unix.EpollCtl(e.efd, unix.EPOLL_CTL_DEL, fd, &unix.EpollEvent{}) +} + +func (e *Epoller) getConsole(sysfd int) *EpollConsole { + e.mu.Lock() + f := e.fdMapping[sysfd] + e.mu.Unlock() + return f +} + +// Close closes the epoll fd +func (e *Epoller) Close() error { + return unix.Close(e.efd) +} + +// EpollConsole acts like a console but registers its file descriptor with an +// epoll fd and uses epoll API to perform I/O. +type EpollConsole struct { + Console + readc *sync.Cond + writec *sync.Cond + sysfd int + closed bool +} + +// Read reads up to len(p) bytes into p. It returns the number of bytes read +// (0 <= n <= len(p)) and any error encountered. +// +// If the console's read returns EAGAIN or EIO, we assume that it's a +// temporary error because the other side went away and wait for the signal +// generated by epoll event to continue. +func (ec *EpollConsole) Read(p []byte) (n int, err error) { + var read int + ec.readc.L.Lock() + defer ec.readc.L.Unlock() + for { + read, err = ec.Console.Read(p[n:]) + n += read + if err != nil { + var hangup bool + if perr, ok := err.(*os.PathError); ok { + hangup = (perr.Err == unix.EAGAIN || perr.Err == unix.EIO) + } else { + hangup = (err == unix.EAGAIN || err == unix.EIO) + } + // if the other end disappear, assume this is temporary and wait for the + // signal to continue again. Unless we didnt read anything and the + // console is already marked as closed then we should exit + if hangup && !(n == 0 && len(p) > 0 && ec.closed) { + ec.readc.Wait() + continue + } + } + break + } + // if we didnt read anything then return io.EOF to end gracefully + if n == 0 && len(p) > 0 && err == nil { + err = io.EOF + } + // signal for others that we finished the read + ec.readc.Signal() + return n, err +} + +// Writes len(p) bytes from p to the console. It returns the number of bytes +// written from p (0 <= n <= len(p)) and any error encountered that caused +// the write to stop early. +// +// If writes to the console returns EAGAIN or EIO, we assume that it's a +// temporary error because the other side went away and wait for the signal +// generated by epoll event to continue. +func (ec *EpollConsole) Write(p []byte) (n int, err error) { + var written int + ec.writec.L.Lock() + defer ec.writec.L.Unlock() + for { + written, err = ec.Console.Write(p[n:]) + n += written + if err != nil { + var hangup bool + if perr, ok := err.(*os.PathError); ok { + hangup = (perr.Err == unix.EAGAIN || perr.Err == unix.EIO) + } else { + hangup = (err == unix.EAGAIN || err == unix.EIO) + } + // if the other end disappears, assume this is temporary and wait for the + // signal to continue again. + if hangup { + ec.writec.Wait() + continue + } + } + // unrecoverable error, break the loop and return the error + break + } + if n < len(p) && err == nil { + err = io.ErrShortWrite + } + // signal for others that we finished the write + ec.writec.Signal() + return n, err +} + +// Shutdown closes the file descriptor and signals call waiters for this fd. +// It accepts a callback which will be called with the console's fd. The +// callback typically will be used to do further cleanup such as unregister the +// console's fd from the epoll interface. +// User should call Shutdown and wait for all I/O operation to be finished +// before closing the console. +func (ec *EpollConsole) Shutdown(close func(int) error) error { + ec.readc.L.Lock() + defer ec.readc.L.Unlock() + ec.writec.L.Lock() + defer ec.writec.L.Unlock() + + ec.readc.Broadcast() + ec.writec.Broadcast() + ec.closed = true + return close(ec.sysfd) +} + +// signalRead signals that the console is readable. +func (ec *EpollConsole) signalRead() { + ec.readc.L.Lock() + ec.readc.Signal() + ec.readc.L.Unlock() +} + +// signalWrite signals that the console is writable. +func (ec *EpollConsole) signalWrite() { + ec.writec.L.Lock() + ec.writec.Signal() + ec.writec.L.Unlock() +} diff --git a/src/runtime/vendor/github.com/containerd/console/console_unix.go b/src/runtime/vendor/github.com/containerd/console/console_unix.go new file mode 100644 index 000000000..315f1d0c9 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/console_unix.go @@ -0,0 +1,158 @@ +// +build darwin freebsd linux openbsd solaris + +/* + Copyright The containerd Authors. + + 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 console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// NewPty creates a new pty pair +// The master is returned as the first console and a string +// with the path to the pty slave is returned as the second +func NewPty() (Console, string, error) { + f, err := os.OpenFile("/dev/ptmx", unix.O_RDWR|unix.O_NOCTTY|unix.O_CLOEXEC, 0) + if err != nil { + return nil, "", err + } + slave, err := ptsname(f) + if err != nil { + return nil, "", err + } + if err := unlockpt(f); err != nil { + return nil, "", err + } + m, err := newMaster(f) + if err != nil { + return nil, "", err + } + return m, slave, nil +} + +type master struct { + f File + original *unix.Termios +} + +func (m *master) Read(b []byte) (int, error) { + return m.f.Read(b) +} + +func (m *master) Write(b []byte) (int, error) { + return m.f.Write(b) +} + +func (m *master) Close() error { + return m.f.Close() +} + +func (m *master) Resize(ws WinSize) error { + return tcswinsz(m.f.Fd(), ws) +} + +func (m *master) ResizeFrom(c Console) error { + ws, err := c.Size() + if err != nil { + return err + } + return m.Resize(ws) +} + +func (m *master) Reset() error { + if m.original == nil { + return nil + } + return tcset(m.f.Fd(), m.original) +} + +func (m *master) getCurrent() (unix.Termios, error) { + var termios unix.Termios + if err := tcget(m.f.Fd(), &termios); err != nil { + return unix.Termios{}, err + } + return termios, nil +} + +func (m *master) SetRaw() error { + rawState, err := m.getCurrent() + if err != nil { + return err + } + rawState = cfmakeraw(rawState) + rawState.Oflag = rawState.Oflag | unix.OPOST + return tcset(m.f.Fd(), &rawState) +} + +func (m *master) DisableEcho() error { + rawState, err := m.getCurrent() + if err != nil { + return err + } + rawState.Lflag = rawState.Lflag &^ unix.ECHO + return tcset(m.f.Fd(), &rawState) +} + +func (m *master) Size() (WinSize, error) { + return tcgwinsz(m.f.Fd()) +} + +func (m *master) Fd() uintptr { + return m.f.Fd() +} + +func (m *master) Name() string { + return m.f.Name() +} + +// checkConsole checks if the provided file is a console +func checkConsole(f File) error { + var termios unix.Termios + if tcget(f.Fd(), &termios) != nil { + return ErrNotAConsole + } + return nil +} + +func newMaster(f File) (Console, error) { + m := &master{ + f: f, + } + t, err := m.getCurrent() + if err != nil { + return nil, err + } + m.original = &t + return m, nil +} + +// ClearONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair +// created by us acts normally. In particular, a not-very-well-known default of +// Linux unix98 ptys is that they have +onlcr by default. While this isn't a +// problem for terminal emulators, because we relay data from the terminal we +// also relay that funky line discipline. +func ClearONLCR(fd uintptr) error { + return setONLCR(fd, false) +} + +// SetONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair +// created by us acts as intended for a terminal emulator. +func SetONLCR(fd uintptr) error { + return setONLCR(fd, true) +} diff --git a/src/runtime/vendor/github.com/containerd/console/console_windows.go b/src/runtime/vendor/github.com/containerd/console/console_windows.go new file mode 100644 index 000000000..129a92883 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/console_windows.go @@ -0,0 +1,216 @@ +/* + Copyright The containerd Authors. + + 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 console + +import ( + "fmt" + "os" + + "github.com/pkg/errors" + "golang.org/x/sys/windows" +) + +var ( + vtInputSupported bool + ErrNotImplemented = errors.New("not implemented") +) + +func (m *master) initStdios() { + m.in = windows.Handle(os.Stdin.Fd()) + if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil { + // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. + if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil { + vtInputSupported = true + } + // Unconditionally set the console mode back even on failure because SetConsoleMode + // remembers invalid bits on input handles. + windows.SetConsoleMode(m.in, m.inMode) + } else { + fmt.Printf("failed to get console mode for stdin: %v\n", err) + } + + m.out = windows.Handle(os.Stdout.Fd()) + if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil { + if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { + m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + } else { + windows.SetConsoleMode(m.out, m.outMode) + } + } else { + fmt.Printf("failed to get console mode for stdout: %v\n", err) + } + + m.err = windows.Handle(os.Stderr.Fd()) + if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil { + if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { + m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + } else { + windows.SetConsoleMode(m.err, m.errMode) + } + } else { + fmt.Printf("failed to get console mode for stderr: %v\n", err) + } +} + +type master struct { + in windows.Handle + inMode uint32 + + out windows.Handle + outMode uint32 + + err windows.Handle + errMode uint32 +} + +func (m *master) SetRaw() error { + if err := makeInputRaw(m.in, m.inMode); err != nil { + return err + } + + // Set StdOut and StdErr to raw mode, we ignore failures since + // windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of + // Windows. + + windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN) + + windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN) + + return nil +} + +func (m *master) Reset() error { + for _, s := range []struct { + fd windows.Handle + mode uint32 + }{ + {m.in, m.inMode}, + {m.out, m.outMode}, + {m.err, m.errMode}, + } { + if err := windows.SetConsoleMode(s.fd, s.mode); err != nil { + return errors.Wrap(err, "unable to restore console mode") + } + } + + return nil +} + +func (m *master) Size() (WinSize, error) { + var info windows.ConsoleScreenBufferInfo + err := windows.GetConsoleScreenBufferInfo(m.out, &info) + if err != nil { + return WinSize{}, errors.Wrap(err, "unable to get console info") + } + + winsize := WinSize{ + Width: uint16(info.Window.Right - info.Window.Left + 1), + Height: uint16(info.Window.Bottom - info.Window.Top + 1), + } + + return winsize, nil +} + +func (m *master) Resize(ws WinSize) error { + return ErrNotImplemented +} + +func (m *master) ResizeFrom(c Console) error { + return ErrNotImplemented +} + +func (m *master) DisableEcho() error { + mode := m.inMode &^ windows.ENABLE_ECHO_INPUT + mode |= windows.ENABLE_PROCESSED_INPUT + mode |= windows.ENABLE_LINE_INPUT + + if err := windows.SetConsoleMode(m.in, mode); err != nil { + return errors.Wrap(err, "unable to set console to disable echo") + } + + return nil +} + +func (m *master) Close() error { + return nil +} + +func (m *master) Read(b []byte) (int, error) { + return os.Stdin.Read(b) +} + +func (m *master) Write(b []byte) (int, error) { + return os.Stdout.Write(b) +} + +func (m *master) Fd() uintptr { + return uintptr(m.in) +} + +// on windows, console can only be made from os.Std{in,out,err}, hence there +// isnt a single name here we can use. Return a dummy "console" value in this +// case should be sufficient. +func (m *master) Name() string { + return "console" +} + +// makeInputRaw puts the terminal (Windows Console) connected to the given +// file descriptor into raw mode +func makeInputRaw(fd windows.Handle, mode uint32) error { + // See + // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx + // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx + + // Disable these modes + mode &^= windows.ENABLE_ECHO_INPUT + mode &^= windows.ENABLE_LINE_INPUT + mode &^= windows.ENABLE_MOUSE_INPUT + mode &^= windows.ENABLE_WINDOW_INPUT + mode &^= windows.ENABLE_PROCESSED_INPUT + + // Enable these modes + mode |= windows.ENABLE_EXTENDED_FLAGS + mode |= windows.ENABLE_INSERT_MODE + mode |= windows.ENABLE_QUICK_EDIT_MODE + + if vtInputSupported { + mode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT + } + + if err := windows.SetConsoleMode(fd, mode); err != nil { + return errors.Wrap(err, "unable to set console to raw mode") + } + + return nil +} + +func checkConsole(f File) error { + var mode uint32 + if err := windows.GetConsoleMode(windows.Handle(f.Fd()), &mode); err != nil { + return err + } + return nil +} + +func newMaster(f File) (Console, error) { + if f != os.Stdin && f != os.Stdout && f != os.Stderr { + return nil, errors.New("creating a console from a file is not supported on windows") + } + m := &master{} + m.initStdios() + return m, nil +} diff --git a/src/runtime/vendor/github.com/containerd/console/go.mod b/src/runtime/vendor/github.com/containerd/console/go.mod new file mode 100644 index 000000000..97b587d62 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/go.mod @@ -0,0 +1,8 @@ +module github.com/containerd/console + +go 1.13 + +require ( + github.com/pkg/errors v0.8.1 + golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e +) diff --git a/src/runtime/vendor/github.com/containerd/console/go.sum b/src/runtime/vendor/github.com/containerd/console/go.sum new file mode 100644 index 000000000..25205cc9b --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/go.sum @@ -0,0 +1,4 @@ +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/src/runtime/vendor/github.com/containerd/console/tc_darwin.go b/src/runtime/vendor/github.com/containerd/console/tc_darwin.go new file mode 100644 index 000000000..b0128abb0 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/tc_darwin.go @@ -0,0 +1,53 @@ +/* + Copyright The containerd Authors. + + 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 console + +import ( + "fmt" + "os" + "unsafe" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +func ioctl(fd, flag, data uintptr) error { + if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, flag, data); err != 0 { + return err + } + return nil +} + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + var u int32 + return ioctl(f.Fd(), unix.TIOCPTYUNLK, uintptr(unsafe.Pointer(&u))) +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCPTYGNAME) + if err != nil { + return "", err + } + return fmt.Sprintf("/dev/pts/%d", n), nil +} diff --git a/src/runtime/vendor/github.com/containerd/console/tc_freebsd.go b/src/runtime/vendor/github.com/containerd/console/tc_freebsd.go new file mode 100644 index 000000000..04583a615 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/tc_freebsd.go @@ -0,0 +1,45 @@ +/* + Copyright The containerd Authors. + + 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 console + +import ( + "fmt" + "os" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +// This does not exist on FreeBSD, it does not allocate controlling terminals on open +func unlockpt(f *os.File) error { + return nil +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN) + if err != nil { + return "", err + } + return fmt.Sprintf("/dev/pts/%d", n), nil +} diff --git a/src/runtime/vendor/github.com/containerd/console/tc_linux.go b/src/runtime/vendor/github.com/containerd/console/tc_linux.go new file mode 100644 index 000000000..1bdd68e6d --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/tc_linux.go @@ -0,0 +1,49 @@ +/* + Copyright The containerd Authors. + + 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 console + +import ( + "fmt" + "os" + "unsafe" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TCGETS + cmdTcSet = unix.TCSETS +) + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + var u int32 + if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))); err != 0 { + return err + } + return nil +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + var u uint32 + if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCGPTN, uintptr(unsafe.Pointer(&u))); err != 0 { + return "", err + } + return fmt.Sprintf("/dev/pts/%d", u), nil +} diff --git a/src/runtime/vendor/github.com/containerd/console/tc_openbsd_cgo.go b/src/runtime/vendor/github.com/containerd/console/tc_openbsd_cgo.go new file mode 100644 index 000000000..f0cec06a7 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/tc_openbsd_cgo.go @@ -0,0 +1,51 @@ +// +build openbsd,cgo + +/* + Copyright The containerd Authors. + + 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 console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +//#include +import "C" + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + ptspath, err := C.ptsname(C.int(f.Fd())) + if err != nil { + return "", err + } + return C.GoString(ptspath), nil +} + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + if _, err := C.grantpt(C.int(f.Fd())); err != nil { + return err + } + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/console/tc_openbsd_nocgo.go b/src/runtime/vendor/github.com/containerd/console/tc_openbsd_nocgo.go new file mode 100644 index 000000000..daccce205 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/tc_openbsd_nocgo.go @@ -0,0 +1,47 @@ +// +build openbsd,!cgo + +/* + Copyright The containerd Authors. + + 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. +*/ + +// +// Implementing the functions below requires cgo support. Non-cgo stubs +// versions are defined below to enable cross-compilation of source code +// that depends on these functions, but the resultant cross-compiled +// binaries cannot actually be used. If the stub function(s) below are +// actually invoked they will display an error message and cause the +// calling process to exit. +// + +package console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +func ptsname(f *os.File) (string, error) { + panic("ptsname() support requires cgo.") +} + +func unlockpt(f *os.File) error { + panic("unlockpt() support requires cgo.") +} diff --git a/src/runtime/vendor/github.com/containerd/console/tc_solaris_cgo.go b/src/runtime/vendor/github.com/containerd/console/tc_solaris_cgo.go new file mode 100644 index 000000000..e36a68edd --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/tc_solaris_cgo.go @@ -0,0 +1,51 @@ +// +build solaris,cgo + +/* + Copyright The containerd Authors. + + 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 console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +//#include +import "C" + +const ( + cmdTcGet = unix.TCGETS + cmdTcSet = unix.TCSETS +) + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + ptspath, err := C.ptsname(C.int(f.Fd())) + if err != nil { + return "", err + } + return C.GoString(ptspath), nil +} + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + if _, err := C.grantpt(C.int(f.Fd())); err != nil { + return err + } + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/console/tc_solaris_nocgo.go b/src/runtime/vendor/github.com/containerd/console/tc_solaris_nocgo.go new file mode 100644 index 000000000..eb0bd2c36 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/tc_solaris_nocgo.go @@ -0,0 +1,47 @@ +// +build solaris,!cgo + +/* + Copyright The containerd Authors. + + 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. +*/ + +// +// Implementing the functions below requires cgo support. Non-cgo stubs +// versions are defined below to enable cross-compilation of source code +// that depends on these functions, but the resultant cross-compiled +// binaries cannot actually be used. If the stub function(s) below are +// actually invoked they will display an error message and cause the +// calling process to exit. +// + +package console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TCGETS + cmdTcSet = unix.TCSETS +) + +func ptsname(f *os.File) (string, error) { + panic("ptsname() support requires cgo.") +} + +func unlockpt(f *os.File) error { + panic("unlockpt() support requires cgo.") +} diff --git a/src/runtime/vendor/github.com/containerd/console/tc_unix.go b/src/runtime/vendor/github.com/containerd/console/tc_unix.go new file mode 100644 index 000000000..7ae773c53 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/console/tc_unix.go @@ -0,0 +1,91 @@ +// +build darwin freebsd linux openbsd solaris + +/* + Copyright The containerd Authors. + + 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 console + +import ( + "golang.org/x/sys/unix" +) + +func tcget(fd uintptr, p *unix.Termios) error { + termios, err := unix.IoctlGetTermios(int(fd), cmdTcGet) + if err != nil { + return err + } + *p = *termios + return nil +} + +func tcset(fd uintptr, p *unix.Termios) error { + return unix.IoctlSetTermios(int(fd), cmdTcSet, p) +} + +func tcgwinsz(fd uintptr) (WinSize, error) { + var ws WinSize + + uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ) + if err != nil { + return ws, err + } + + // Translate from unix.Winsize to console.WinSize + ws.Height = uws.Row + ws.Width = uws.Col + ws.x = uws.Xpixel + ws.y = uws.Ypixel + return ws, nil +} + +func tcswinsz(fd uintptr, ws WinSize) error { + // Translate from console.WinSize to unix.Winsize + + var uws unix.Winsize + uws.Row = ws.Height + uws.Col = ws.Width + uws.Xpixel = ws.x + uws.Ypixel = ws.y + + return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, &uws) +} + +func setONLCR(fd uintptr, enable bool) error { + var termios unix.Termios + if err := tcget(fd, &termios); err != nil { + return err + } + if enable { + // Set +onlcr so we can act like a real terminal + termios.Oflag |= unix.ONLCR + } else { + // Set -onlcr so we don't have to deal with \r. + termios.Oflag &^= unix.ONLCR + } + return tcset(fd, &termios) +} + +func cfmakeraw(t unix.Termios) unix.Termios { + t.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON) + t.Oflag &^= unix.OPOST + t.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN) + t.Cflag &^= (unix.CSIZE | unix.PARENB) + t.Cflag &^= unix.CS8 + t.Cc[unix.VMIN] = 1 + t.Cc[unix.VTIME] = 0 + + return t +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/LICENSE b/src/runtime/vendor/github.com/containerd/containerd/LICENSE new file mode 100644 index 000000000..584149b6e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright The containerd Authors + + 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 + + https://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. diff --git a/src/runtime/vendor/github.com/containerd/containerd/NOTICE b/src/runtime/vendor/github.com/containerd/containerd/NOTICE new file mode 100644 index 000000000..8915f0277 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/NOTICE @@ -0,0 +1,16 @@ +Docker +Copyright 2012-2015 Docker, Inc. + +This product includes software developed at Docker, Inc. (https://www.docker.com). + +The following is courtesy of our legal counsel: + + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see https://www.bis.doc.gov + +See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/container.pb.go b/src/runtime/vendor/github.com/containerd/containerd/api/events/container.pb.go new file mode 100644 index 000000000..c89d97f3e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/container.pb.go @@ -0,0 +1,1238 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/api/events/container.proto + +/* + Package events is a generated protocol buffer package. + + It is generated from these files: + github.com/containerd/containerd/api/events/container.proto + github.com/containerd/containerd/api/events/content.proto + github.com/containerd/containerd/api/events/image.proto + github.com/containerd/containerd/api/events/namespace.proto + github.com/containerd/containerd/api/events/snapshot.proto + github.com/containerd/containerd/api/events/task.proto + + It has these top-level messages: + ContainerCreate + ContainerUpdate + ContainerDelete + ContentDelete + ImageCreate + ImageUpdate + ImageDelete + NamespaceCreate + NamespaceUpdate + NamespaceDelete + SnapshotPrepare + SnapshotCommit + SnapshotRemove + TaskCreate + TaskStart + TaskDelete + TaskIO + TaskExit + TaskOOM + TaskExecAdded + TaskExecStarted + TaskPaused + TaskResumed + TaskCheckpointed +*/ +package events + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import google_protobuf "github.com/gogo/protobuf/types" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" +// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" + +import typeurl "github.com/containerd/typeurl" + +import strings "strings" +import reflect "reflect" +import sortkeys "github.com/gogo/protobuf/sortkeys" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type ContainerCreate struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Image string `protobuf:"bytes,2,opt,name=image,proto3" json:"image,omitempty"` + Runtime *ContainerCreate_Runtime `protobuf:"bytes,3,opt,name=runtime" json:"runtime,omitempty"` +} + +func (m *ContainerCreate) Reset() { *m = ContainerCreate{} } +func (*ContainerCreate) ProtoMessage() {} +func (*ContainerCreate) Descriptor() ([]byte, []int) { return fileDescriptorContainer, []int{0} } + +type ContainerCreate_Runtime struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Options *google_protobuf.Any `protobuf:"bytes,2,opt,name=options" json:"options,omitempty"` +} + +func (m *ContainerCreate_Runtime) Reset() { *m = ContainerCreate_Runtime{} } +func (*ContainerCreate_Runtime) ProtoMessage() {} +func (*ContainerCreate_Runtime) Descriptor() ([]byte, []int) { + return fileDescriptorContainer, []int{0, 0} +} + +type ContainerUpdate struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Image string `protobuf:"bytes,2,opt,name=image,proto3" json:"image,omitempty"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + SnapshotKey string `protobuf:"bytes,4,opt,name=snapshot_key,json=snapshotKey,proto3" json:"snapshot_key,omitempty"` +} + +func (m *ContainerUpdate) Reset() { *m = ContainerUpdate{} } +func (*ContainerUpdate) ProtoMessage() {} +func (*ContainerUpdate) Descriptor() ([]byte, []int) { return fileDescriptorContainer, []int{1} } + +type ContainerDelete struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (m *ContainerDelete) Reset() { *m = ContainerDelete{} } +func (*ContainerDelete) ProtoMessage() {} +func (*ContainerDelete) Descriptor() ([]byte, []int) { return fileDescriptorContainer, []int{2} } + +func init() { + proto.RegisterType((*ContainerCreate)(nil), "containerd.events.ContainerCreate") + proto.RegisterType((*ContainerCreate_Runtime)(nil), "containerd.events.ContainerCreate.Runtime") + proto.RegisterType((*ContainerUpdate)(nil), "containerd.events.ContainerUpdate") + proto.RegisterType((*ContainerDelete)(nil), "containerd.events.ContainerDelete") +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *ContainerCreate) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "id": + return string(m.ID), len(m.ID) > 0 + case "image": + return string(m.Image), len(m.Image) > 0 + case "runtime": + // NOTE(stevvooe): This is probably not correct in many cases. + // We assume that the target message also implements the Field + // method, which isn't likely true in a lot of cases. + // + // If you have a broken build and have found this comment, + // you may be closer to a solution. + if m.Runtime == nil { + return "", false + } + + return m.Runtime.Field(fieldpath[1:]) + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *ContainerCreate_Runtime) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "name": + return string(m.Name), len(m.Name) > 0 + case "options": + decoded, err := typeurl.UnmarshalAny(m.Options) + if err != nil { + return "", false + } + + adaptor, ok := decoded.(interface{ Field([]string) (string, bool) }) + if !ok { + return "", false + } + return adaptor.Field(fieldpath[1:]) + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *ContainerUpdate) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "id": + return string(m.ID), len(m.ID) > 0 + case "image": + return string(m.Image), len(m.Image) > 0 + case "labels": + // Labels fields have been special-cased by name. If this breaks, + // add better special casing to fieldpath plugin. + if len(m.Labels) == 0 { + return "", false + } + value, ok := m.Labels[strings.Join(fieldpath[1:], ".")] + return value, ok + case "snapshot_key": + return string(m.SnapshotKey), len(m.SnapshotKey) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *ContainerDelete) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "id": + return string(m.ID), len(m.ID) > 0 + } + return "", false +} +func (m *ContainerCreate) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ContainerCreate) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintContainer(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.Image) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintContainer(dAtA, i, uint64(len(m.Image))) + i += copy(dAtA[i:], m.Image) + } + if m.Runtime != nil { + dAtA[i] = 0x1a + i++ + i = encodeVarintContainer(dAtA, i, uint64(m.Runtime.Size())) + n1, err := m.Runtime.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + } + return i, nil +} + +func (m *ContainerCreate_Runtime) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ContainerCreate_Runtime) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Name) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintContainer(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if m.Options != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintContainer(dAtA, i, uint64(m.Options.Size())) + n2, err := m.Options.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + } + return i, nil +} + +func (m *ContainerUpdate) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ContainerUpdate) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintContainer(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.Image) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintContainer(dAtA, i, uint64(len(m.Image))) + i += copy(dAtA[i:], m.Image) + } + if len(m.Labels) > 0 { + for k, _ := range m.Labels { + dAtA[i] = 0x1a + i++ + v := m.Labels[k] + mapSize := 1 + len(k) + sovContainer(uint64(len(k))) + 1 + len(v) + sovContainer(uint64(len(v))) + i = encodeVarintContainer(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintContainer(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintContainer(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + if len(m.SnapshotKey) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintContainer(dAtA, i, uint64(len(m.SnapshotKey))) + i += copy(dAtA[i:], m.SnapshotKey) + } + return i, nil +} + +func (m *ContainerDelete) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ContainerDelete) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintContainer(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + return i, nil +} + +func encodeVarintContainer(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *ContainerCreate) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovContainer(uint64(l)) + } + l = len(m.Image) + if l > 0 { + n += 1 + l + sovContainer(uint64(l)) + } + if m.Runtime != nil { + l = m.Runtime.Size() + n += 1 + l + sovContainer(uint64(l)) + } + return n +} + +func (m *ContainerCreate_Runtime) Size() (n int) { + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovContainer(uint64(l)) + } + if m.Options != nil { + l = m.Options.Size() + n += 1 + l + sovContainer(uint64(l)) + } + return n +} + +func (m *ContainerUpdate) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovContainer(uint64(l)) + } + l = len(m.Image) + if l > 0 { + n += 1 + l + sovContainer(uint64(l)) + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovContainer(uint64(len(k))) + 1 + len(v) + sovContainer(uint64(len(v))) + n += mapEntrySize + 1 + sovContainer(uint64(mapEntrySize)) + } + } + l = len(m.SnapshotKey) + if l > 0 { + n += 1 + l + sovContainer(uint64(l)) + } + return n +} + +func (m *ContainerDelete) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovContainer(uint64(l)) + } + return n +} + +func sovContainer(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozContainer(x uint64) (n int) { + return sovContainer(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *ContainerCreate) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ContainerCreate{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Image:` + fmt.Sprintf("%v", this.Image) + `,`, + `Runtime:` + strings.Replace(fmt.Sprintf("%v", this.Runtime), "ContainerCreate_Runtime", "ContainerCreate_Runtime", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ContainerCreate_Runtime) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ContainerCreate_Runtime{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "google_protobuf.Any", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ContainerUpdate) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&ContainerUpdate{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Image:` + fmt.Sprintf("%v", this.Image) + `,`, + `Labels:` + mapStringForLabels + `,`, + `SnapshotKey:` + fmt.Sprintf("%v", this.SnapshotKey) + `,`, + `}`, + }, "") + return s +} +func (this *ContainerDelete) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ContainerDelete{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `}`, + }, "") + return s +} +func valueToStringContainer(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *ContainerCreate) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ContainerCreate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ContainerCreate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthContainer + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthContainer + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Image = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Runtime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthContainer + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Runtime == nil { + m.Runtime = &ContainerCreate_Runtime{} + } + if err := m.Runtime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipContainer(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthContainer + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ContainerCreate_Runtime) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Runtime: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Runtime: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthContainer + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthContainer + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Options == nil { + m.Options = &google_protobuf.Any{} + } + if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipContainer(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthContainer + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ContainerUpdate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ContainerUpdate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthContainer + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthContainer + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Image = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthContainer + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthContainer + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthContainer + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipContainer(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthContainer + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SnapshotKey", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthContainer + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SnapshotKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipContainer(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthContainer + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ContainerDelete) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ContainerDelete: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ContainerDelete: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContainer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthContainer + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipContainer(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthContainer + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipContainer(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowContainer + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowContainer + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowContainer + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthContainer + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowContainer + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipContainer(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthContainer = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowContainer = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/container.proto", fileDescriptorContainer) +} + +var fileDescriptorContainer = []byte{ + // 413 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xc1, 0x0a, 0xd3, 0x30, + 0x18, 0xc7, 0x97, 0x76, 0x6e, 0x98, 0x0a, 0x6a, 0x18, 0x52, 0x7b, 0xa8, 0x73, 0xa7, 0xe9, 0x21, + 0x85, 0x7a, 0x51, 0x77, 0xd1, 0x6d, 0x0a, 0xa2, 0x82, 0x14, 0x84, 0xe1, 0x45, 0xd2, 0x35, 0xeb, + 0x82, 0x6d, 0x52, 0xda, 0x74, 0xd0, 0x9b, 0x8f, 0xe2, 0xe3, 0xec, 0xe8, 0xc1, 0x83, 0x27, 0x71, + 0x05, 0xdf, 0xc0, 0x07, 0x90, 0x26, 0xeb, 0x56, 0x14, 0x95, 0x9d, 0xfa, 0xcf, 0xd7, 0xff, 0x3f, + 0xdf, 0xf7, 0xfb, 0x08, 0x9c, 0xc5, 0x4c, 0x6e, 0xcb, 0x10, 0xaf, 0x45, 0xea, 0xad, 0x05, 0x97, + 0x84, 0x71, 0x9a, 0x47, 0x5d, 0x49, 0x32, 0xe6, 0xd1, 0x1d, 0xe5, 0xb2, 0x38, 0x57, 0x71, 0x96, + 0x0b, 0x29, 0xd0, 0xcd, 0xb3, 0x0d, 0x6b, 0x8b, 0x73, 0x3b, 0x16, 0x22, 0x4e, 0xa8, 0xa7, 0x0c, + 0x61, 0xb9, 0xf1, 0x08, 0xaf, 0xb4, 0xdb, 0x19, 0xc5, 0x22, 0x16, 0x4a, 0x7a, 0x8d, 0x3a, 0x56, + 0x9f, 0xfc, 0x77, 0x80, 0xd3, 0x55, 0x59, 0x52, 0xc6, 0x8c, 0x7b, 0x1b, 0x46, 0x93, 0x28, 0x23, + 0x72, 0xab, 0x6f, 0x98, 0x7c, 0x01, 0xf0, 0xfa, 0xa2, 0xb5, 0x2f, 0x72, 0x4a, 0x24, 0x45, 0xb7, + 0xa0, 0xc1, 0x22, 0x1b, 0x8c, 0xc1, 0xf4, 0xea, 0x7c, 0x50, 0x7f, 0xbb, 0x63, 0xbc, 0x58, 0x06, + 0x06, 0x8b, 0xd0, 0x08, 0x5e, 0x61, 0x29, 0x89, 0xa9, 0x6d, 0x34, 0xbf, 0x02, 0x7d, 0x40, 0x4b, + 0x38, 0xcc, 0x4b, 0x2e, 0x59, 0x4a, 0x6d, 0x73, 0x0c, 0xa6, 0x96, 0x7f, 0x1f, 0xff, 0x41, 0x86, + 0x7f, 0x6b, 0x81, 0x03, 0x9d, 0x08, 0xda, 0xa8, 0xf3, 0x1a, 0x0e, 0x8f, 0x35, 0x84, 0x60, 0x9f, + 0x93, 0x94, 0xea, 0x01, 0x02, 0xa5, 0x11, 0x86, 0x43, 0x91, 0x49, 0x26, 0x78, 0xa1, 0x9a, 0x5b, + 0xfe, 0x08, 0xeb, 0x5d, 0xe1, 0x16, 0x10, 0x3f, 0xe5, 0x55, 0xd0, 0x9a, 0x26, 0x3f, 0xba, 0x58, + 0x6f, 0xb3, 0xe8, 0x72, 0xac, 0xe7, 0x70, 0x90, 0x90, 0x90, 0x26, 0x85, 0x6d, 0x8e, 0xcd, 0xa9, + 0xe5, 0xe3, 0x7f, 0x51, 0xe9, 0x0e, 0xf8, 0x95, 0x0a, 0x3c, 0xe3, 0x32, 0xaf, 0x82, 0x63, 0x1a, + 0xdd, 0x85, 0xd7, 0x0a, 0x4e, 0xb2, 0x62, 0x2b, 0xe4, 0xfb, 0x0f, 0xb4, 0xb2, 0xfb, 0xaa, 0x89, + 0xd5, 0xd6, 0x5e, 0xd2, 0xca, 0x79, 0x04, 0xad, 0x4e, 0x12, 0xdd, 0x80, 0x66, 0x63, 0xd4, 0xf8, + 0x8d, 0x6c, 0x26, 0xdc, 0x91, 0xa4, 0x3c, 0x4d, 0xa8, 0x0e, 0x8f, 0x8d, 0x87, 0x60, 0x72, 0xaf, + 0x83, 0xb9, 0xa4, 0x09, 0xfd, 0x3b, 0xe6, 0xfc, 0xcd, 0xfe, 0xe0, 0xf6, 0xbe, 0x1e, 0xdc, 0xde, + 0xc7, 0xda, 0x05, 0xfb, 0xda, 0x05, 0x9f, 0x6b, 0x17, 0x7c, 0xaf, 0x5d, 0xf0, 0xe9, 0xa7, 0x0b, + 0xde, 0xf9, 0x17, 0x3c, 0xe5, 0x99, 0xfe, 0xac, 0xc0, 0xca, 0x08, 0x07, 0x6a, 0xff, 0x0f, 0x7e, + 0x05, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x09, 0xe0, 0xd6, 0x0b, 0x03, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/container.proto b/src/runtime/vendor/github.com/containerd/containerd/api/events/container.proto new file mode 100644 index 000000000..13aa5848c --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/container.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package containerd.events; + +import "google/protobuf/any.proto"; +import weak "gogoproto/gogo.proto"; +import weak "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto"; + +option go_package = "github.com/containerd/containerd/api/events;events"; +option (containerd.plugin.fieldpath_all) = true; + +message ContainerCreate { + string id = 1; + string image = 2; + message Runtime { + string name = 1; + google.protobuf.Any options = 2; + } + Runtime runtime = 3; +} + +message ContainerUpdate { + string id = 1; + string image = 2; + map labels = 3; + string snapshot_key = 4; +} + +message ContainerDelete { + string id = 1; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/content.pb.go b/src/runtime/vendor/github.com/containerd/containerd/api/events/content.pb.go new file mode 100644 index 000000000..87648d193 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/content.pb.go @@ -0,0 +1,329 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/api/events/content.proto + +package events + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" +// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" + +import github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type ContentDelete struct { + Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` +} + +func (m *ContentDelete) Reset() { *m = ContentDelete{} } +func (*ContentDelete) ProtoMessage() {} +func (*ContentDelete) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{0} } + +func init() { + proto.RegisterType((*ContentDelete)(nil), "containerd.events.ContentDelete") +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *ContentDelete) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "digest": + return string(m.Digest), len(m.Digest) > 0 + } + return "", false +} +func (m *ContentDelete) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ContentDelete) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Digest) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintContent(dAtA, i, uint64(len(m.Digest))) + i += copy(dAtA[i:], m.Digest) + } + return i, nil +} + +func encodeVarintContent(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *ContentDelete) Size() (n int) { + var l int + _ = l + l = len(m.Digest) + if l > 0 { + n += 1 + l + sovContent(uint64(l)) + } + return n +} + +func sovContent(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozContent(x uint64) (n int) { + return sovContent(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *ContentDelete) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ContentDelete{`, + `Digest:` + fmt.Sprintf("%v", this.Digest) + `,`, + `}`, + }, "") + return s +} +func valueToStringContent(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *ContentDelete) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ContentDelete: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ContentDelete: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Digest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowContent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthContent + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Digest = github_com_opencontainers_go_digest.Digest(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipContent(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthContent + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipContent(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowContent + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowContent + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowContent + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthContent + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowContent + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipContent(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthContent = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowContent = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/content.proto", fileDescriptorContent) +} + +var fileDescriptorContent = []byte{ + // 228 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0x83, 0x45, 0x53, + 0xf3, 0x4a, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x04, 0x11, 0x8a, 0xf4, 0x20, 0x0a, 0xa4, + 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xb2, 0xfa, 0x20, 0x16, 0x44, 0xa1, 0x94, 0x03, 0x41, 0x3b, + 0xc0, 0xea, 0x92, 0x4a, 0xd3, 0xf4, 0x0b, 0x72, 0x4a, 0xd3, 0x33, 0xf3, 0xf4, 0xd3, 0x32, 0x53, + 0x73, 0x52, 0x0a, 0x12, 0x4b, 0x32, 0x20, 0x26, 0x28, 0x45, 0x73, 0xf1, 0x3a, 0x43, 0xec, 0x76, + 0x49, 0xcd, 0x49, 0x2d, 0x49, 0x15, 0xf2, 0xe2, 0x62, 0x4b, 0xc9, 0x4c, 0x4f, 0x2d, 0x2e, 0x91, + 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x74, 0x32, 0x3a, 0x71, 0x4f, 0x9e, 0xe1, 0xd6, 0x3d, 0x79, 0x2d, + 0x24, 0xab, 0xf2, 0x0b, 0x52, 0xf3, 0xe0, 0x76, 0x14, 0xeb, 0xa7, 0xe7, 0xeb, 0x42, 0xb4, 0xe8, + 0xb9, 0x80, 0xa9, 0x20, 0xa8, 0x09, 0x4e, 0x01, 0x27, 0x1e, 0xca, 0x31, 0xdc, 0x78, 0x28, 0xc7, + 0xd0, 0xf0, 0x48, 0x8e, 0xf1, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, + 0x63, 0x5c, 0xf0, 0x45, 0x8e, 0x31, 0xca, 0x88, 0x84, 0x00, 0xb2, 0x86, 0x50, 0x11, 0x0c, 0x11, + 0x8c, 0x49, 0x6c, 0x60, 0x97, 0x1b, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x4b, 0x78, 0x99, 0xee, + 0x61, 0x01, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/content.proto b/src/runtime/vendor/github.com/containerd/containerd/api/events/content.proto new file mode 100644 index 000000000..aba50716f --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/content.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package containerd.events; + +import weak "gogoproto/gogo.proto"; +import weak "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto"; + +option go_package = "github.com/containerd/containerd/api/events;events"; +option (containerd.plugin.fieldpath_all) = true; + +message ContentDelete { + string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/doc.go b/src/runtime/vendor/github.com/containerd/containerd/api/events/doc.go new file mode 100644 index 000000000..354bef79f --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/doc.go @@ -0,0 +1,19 @@ +/* + Copyright The containerd Authors. + + 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 events has protobuf types for various events that are used in +// containerd. +package events diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/image.pb.go b/src/runtime/vendor/github.com/containerd/containerd/api/events/image.pb.go new file mode 100644 index 000000000..8197005b6 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/image.pb.go @@ -0,0 +1,949 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/api/events/image.proto + +package events + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" + +import strings "strings" +import reflect "reflect" +import sortkeys "github.com/gogo/protobuf/sortkeys" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type ImageCreate struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *ImageCreate) Reset() { *m = ImageCreate{} } +func (*ImageCreate) ProtoMessage() {} +func (*ImageCreate) Descriptor() ([]byte, []int) { return fileDescriptorImage, []int{0} } + +type ImageUpdate struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *ImageUpdate) Reset() { *m = ImageUpdate{} } +func (*ImageUpdate) ProtoMessage() {} +func (*ImageUpdate) Descriptor() ([]byte, []int) { return fileDescriptorImage, []int{1} } + +type ImageDelete struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *ImageDelete) Reset() { *m = ImageDelete{} } +func (*ImageDelete) ProtoMessage() {} +func (*ImageDelete) Descriptor() ([]byte, []int) { return fileDescriptorImage, []int{2} } + +func init() { + proto.RegisterType((*ImageCreate)(nil), "containerd.services.images.v1.ImageCreate") + proto.RegisterType((*ImageUpdate)(nil), "containerd.services.images.v1.ImageUpdate") + proto.RegisterType((*ImageDelete)(nil), "containerd.services.images.v1.ImageDelete") +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *ImageCreate) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "name": + return string(m.Name), len(m.Name) > 0 + case "labels": + // Labels fields have been special-cased by name. If this breaks, + // add better special casing to fieldpath plugin. + if len(m.Labels) == 0 { + return "", false + } + value, ok := m.Labels[strings.Join(fieldpath[1:], ".")] + return value, ok + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *ImageUpdate) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "name": + return string(m.Name), len(m.Name) > 0 + case "labels": + // Labels fields have been special-cased by name. If this breaks, + // add better special casing to fieldpath plugin. + if len(m.Labels) == 0 { + return "", false + } + value, ok := m.Labels[strings.Join(fieldpath[1:], ".")] + return value, ok + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *ImageDelete) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "name": + return string(m.Name), len(m.Name) > 0 + } + return "", false +} +func (m *ImageCreate) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ImageCreate) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Name) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintImage(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if len(m.Labels) > 0 { + for k, _ := range m.Labels { + dAtA[i] = 0x12 + i++ + v := m.Labels[k] + mapSize := 1 + len(k) + sovImage(uint64(len(k))) + 1 + len(v) + sovImage(uint64(len(v))) + i = encodeVarintImage(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintImage(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintImage(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + return i, nil +} + +func (m *ImageUpdate) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ImageUpdate) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Name) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintImage(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if len(m.Labels) > 0 { + for k, _ := range m.Labels { + dAtA[i] = 0x12 + i++ + v := m.Labels[k] + mapSize := 1 + len(k) + sovImage(uint64(len(k))) + 1 + len(v) + sovImage(uint64(len(v))) + i = encodeVarintImage(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintImage(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintImage(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + return i, nil +} + +func (m *ImageDelete) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ImageDelete) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Name) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintImage(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + return i, nil +} + +func encodeVarintImage(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *ImageCreate) Size() (n int) { + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovImage(uint64(l)) + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovImage(uint64(len(k))) + 1 + len(v) + sovImage(uint64(len(v))) + n += mapEntrySize + 1 + sovImage(uint64(mapEntrySize)) + } + } + return n +} + +func (m *ImageUpdate) Size() (n int) { + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovImage(uint64(l)) + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovImage(uint64(len(k))) + 1 + len(v) + sovImage(uint64(len(v))) + n += mapEntrySize + 1 + sovImage(uint64(mapEntrySize)) + } + } + return n +} + +func (m *ImageDelete) Size() (n int) { + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovImage(uint64(l)) + } + return n +} + +func sovImage(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozImage(x uint64) (n int) { + return sovImage(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *ImageCreate) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&ImageCreate{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Labels:` + mapStringForLabels + `,`, + `}`, + }, "") + return s +} +func (this *ImageUpdate) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&ImageUpdate{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Labels:` + mapStringForLabels + `,`, + `}`, + }, "") + return s +} +func (this *ImageDelete) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ImageDelete{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `}`, + }, "") + return s +} +func valueToStringImage(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *ImageCreate) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ImageCreate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ImageCreate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthImage + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthImage + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthImage + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthImage + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipImage(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthImage + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipImage(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthImage + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ImageUpdate) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ImageUpdate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ImageUpdate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthImage + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthImage + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthImage + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthImage + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipImage(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthImage + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipImage(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthImage + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ImageDelete) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ImageDelete: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ImageDelete: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowImage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthImage + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipImage(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthImage + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipImage(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowImage + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowImage + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowImage + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthImage + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowImage + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipImage(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthImage = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowImage = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/image.proto", fileDescriptorImage) +} + +var fileDescriptorImage = []byte{ + // 292 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4f, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0xeb, 0x67, 0xe6, + 0x26, 0xa6, 0xa7, 0xea, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0xc9, 0x22, 0x94, 0xe8, 0x15, 0xa7, + 0x16, 0x95, 0x65, 0x26, 0xa7, 0x16, 0xeb, 0x81, 0x15, 0x14, 0xeb, 0x95, 0x19, 0x4a, 0x39, 0x10, + 0x34, 0x17, 0x6c, 0x4c, 0x52, 0x69, 0x9a, 0x7e, 0x41, 0x4e, 0x69, 0x7a, 0x66, 0x9e, 0x7e, 0x5a, + 0x66, 0x6a, 0x4e, 0x4a, 0x41, 0x62, 0x49, 0x06, 0xc4, 0x02, 0xa5, 0x35, 0x8c, 0x5c, 0xdc, 0x9e, + 0x20, 0xf3, 0x9c, 0x8b, 0x52, 0x13, 0x4b, 0x52, 0x85, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, + 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x21, 0x3f, 0x2e, 0xb6, 0x9c, 0xc4, 0xa4, + 0xd4, 0x9c, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x33, 0x3d, 0xbc, 0xae, 0xd2, 0x43, + 0x32, 0x4f, 0xcf, 0x07, 0xac, 0xd1, 0x35, 0xaf, 0xa4, 0xa8, 0x32, 0x08, 0x6a, 0x8a, 0x94, 0x25, + 0x17, 0x37, 0x92, 0xb0, 0x90, 0x00, 0x17, 0x73, 0x76, 0x6a, 0x25, 0xd4, 0x46, 0x10, 0x53, 0x48, + 0x84, 0x8b, 0xb5, 0x2c, 0x31, 0xa7, 0x34, 0x55, 0x82, 0x09, 0x2c, 0x06, 0xe1, 0x58, 0x31, 0x59, + 0x30, 0x22, 0x9c, 0x1b, 0x5a, 0x90, 0x42, 0x55, 0xe7, 0x42, 0xcc, 0xa3, 0xb6, 0x73, 0x15, 0xa1, + 0xae, 0x75, 0x49, 0xcd, 0x49, 0xc5, 0xee, 0x5a, 0xa7, 0x80, 0x13, 0x0f, 0xe5, 0x18, 0x6e, 0x3c, + 0x94, 0x63, 0x68, 0x78, 0x24, 0xc7, 0x78, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, + 0x1e, 0xc9, 0x31, 0x2e, 0xf8, 0x22, 0xc7, 0x18, 0x65, 0x44, 0x42, 0xc2, 0xb1, 0x86, 0x50, 0x11, + 0x0c, 0x49, 0x6c, 0xe0, 0xb8, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x41, 0x80, 0x92, 0x17, + 0x77, 0x02, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/image.proto b/src/runtime/vendor/github.com/containerd/containerd/api/events/image.proto new file mode 100644 index 000000000..470c3a2fa --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/image.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package containerd.services.images.v1; + +import weak "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto"; + +option go_package = "github.com/containerd/containerd/api/events;events"; +option (containerd.plugin.fieldpath_all) = true; + +message ImageCreate { + string name = 1; + map labels = 2; +} + +message ImageUpdate { + string name = 1; + map labels = 2; +} + +message ImageDelete { + string name = 1; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/namespace.pb.go b/src/runtime/vendor/github.com/containerd/containerd/api/events/namespace.pb.go new file mode 100644 index 000000000..1c81f9fc4 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/namespace.pb.go @@ -0,0 +1,950 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/api/events/namespace.proto + +package events + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" +// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" + +import strings "strings" +import reflect "reflect" +import sortkeys "github.com/gogo/protobuf/sortkeys" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type NamespaceCreate struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *NamespaceCreate) Reset() { *m = NamespaceCreate{} } +func (*NamespaceCreate) ProtoMessage() {} +func (*NamespaceCreate) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{0} } + +type NamespaceUpdate struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *NamespaceUpdate) Reset() { *m = NamespaceUpdate{} } +func (*NamespaceUpdate) ProtoMessage() {} +func (*NamespaceUpdate) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{1} } + +type NamespaceDelete struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *NamespaceDelete) Reset() { *m = NamespaceDelete{} } +func (*NamespaceDelete) ProtoMessage() {} +func (*NamespaceDelete) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{2} } + +func init() { + proto.RegisterType((*NamespaceCreate)(nil), "containerd.events.NamespaceCreate") + proto.RegisterType((*NamespaceUpdate)(nil), "containerd.events.NamespaceUpdate") + proto.RegisterType((*NamespaceDelete)(nil), "containerd.events.NamespaceDelete") +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *NamespaceCreate) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "name": + return string(m.Name), len(m.Name) > 0 + case "labels": + // Labels fields have been special-cased by name. If this breaks, + // add better special casing to fieldpath plugin. + if len(m.Labels) == 0 { + return "", false + } + value, ok := m.Labels[strings.Join(fieldpath[1:], ".")] + return value, ok + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *NamespaceUpdate) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "name": + return string(m.Name), len(m.Name) > 0 + case "labels": + // Labels fields have been special-cased by name. If this breaks, + // add better special casing to fieldpath plugin. + if len(m.Labels) == 0 { + return "", false + } + value, ok := m.Labels[strings.Join(fieldpath[1:], ".")] + return value, ok + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *NamespaceDelete) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "name": + return string(m.Name), len(m.Name) > 0 + } + return "", false +} +func (m *NamespaceCreate) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NamespaceCreate) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Name) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if len(m.Labels) > 0 { + for k, _ := range m.Labels { + dAtA[i] = 0x12 + i++ + v := m.Labels[k] + mapSize := 1 + len(k) + sovNamespace(uint64(len(k))) + 1 + len(v) + sovNamespace(uint64(len(v))) + i = encodeVarintNamespace(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintNamespace(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintNamespace(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + return i, nil +} + +func (m *NamespaceUpdate) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NamespaceUpdate) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Name) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if len(m.Labels) > 0 { + for k, _ := range m.Labels { + dAtA[i] = 0x12 + i++ + v := m.Labels[k] + mapSize := 1 + len(k) + sovNamespace(uint64(len(k))) + 1 + len(v) + sovNamespace(uint64(len(v))) + i = encodeVarintNamespace(dAtA, i, uint64(mapSize)) + dAtA[i] = 0xa + i++ + i = encodeVarintNamespace(dAtA, i, uint64(len(k))) + i += copy(dAtA[i:], k) + dAtA[i] = 0x12 + i++ + i = encodeVarintNamespace(dAtA, i, uint64(len(v))) + i += copy(dAtA[i:], v) + } + } + return i, nil +} + +func (m *NamespaceDelete) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NamespaceDelete) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Name) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + return i, nil +} + +func encodeVarintNamespace(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *NamespaceCreate) Size() (n int) { + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovNamespace(uint64(l)) + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovNamespace(uint64(len(k))) + 1 + len(v) + sovNamespace(uint64(len(v))) + n += mapEntrySize + 1 + sovNamespace(uint64(mapEntrySize)) + } + } + return n +} + +func (m *NamespaceUpdate) Size() (n int) { + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovNamespace(uint64(l)) + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovNamespace(uint64(len(k))) + 1 + len(v) + sovNamespace(uint64(len(v))) + n += mapEntrySize + 1 + sovNamespace(uint64(mapEntrySize)) + } + } + return n +} + +func (m *NamespaceDelete) Size() (n int) { + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovNamespace(uint64(l)) + } + return n +} + +func sovNamespace(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozNamespace(x uint64) (n int) { + return sovNamespace(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *NamespaceCreate) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&NamespaceCreate{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Labels:` + mapStringForLabels + `,`, + `}`, + }, "") + return s +} +func (this *NamespaceUpdate) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&NamespaceUpdate{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Labels:` + mapStringForLabels + `,`, + `}`, + }, "") + return s +} +func (this *NamespaceDelete) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&NamespaceDelete{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `}`, + }, "") + return s +} +func valueToStringNamespace(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NamespaceCreate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NamespaceCreate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthNamespace + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthNamespace + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthNamespace + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthNamespace + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipNamespace(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthNamespace + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipNamespace(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthNamespace + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NamespaceUpdate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NamespaceUpdate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthNamespace + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthNamespace + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthNamespace + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthNamespace + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipNamespace(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthNamespace + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipNamespace(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthNamespace + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NamespaceDelete) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NamespaceDelete: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NamespaceDelete: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowNamespace + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthNamespace + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipNamespace(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthNamespace + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipNamespace(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowNamespace + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowNamespace + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowNamespace + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthNamespace + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowNamespace + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipNamespace(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthNamespace = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowNamespace = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/namespace.proto", fileDescriptorNamespace) +} + +var fileDescriptorNamespace = []byte{ + // 296 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0xeb, 0xe7, 0x25, + 0xe6, 0xa6, 0x16, 0x17, 0x24, 0x26, 0xa7, 0xea, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x22, + 0x94, 0xe9, 0x41, 0x94, 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x65, 0xf5, 0x41, 0x2c, 0x88, + 0x42, 0x29, 0x07, 0x82, 0xb6, 0x80, 0xd5, 0x25, 0x95, 0xa6, 0xe9, 0x17, 0xe4, 0x94, 0xa6, 0x67, + 0xe6, 0xe9, 0xa7, 0x65, 0xa6, 0xe6, 0xa4, 0x14, 0x24, 0x96, 0x64, 0x40, 0x4c, 0x50, 0x5a, 0xc1, + 0xc8, 0xc5, 0xef, 0x07, 0xb3, 0xde, 0xb9, 0x28, 0x35, 0xb1, 0x24, 0x55, 0x48, 0x88, 0x8b, 0x05, + 0xe4, 0x22, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x30, 0x5b, 0xc8, 0x8d, 0x8b, 0x2d, 0x27, + 0x31, 0x29, 0x35, 0xa7, 0x58, 0x82, 0x49, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x4f, 0x0f, 0xc3, 0x8d, + 0x7a, 0x68, 0xe6, 0xe8, 0xf9, 0x80, 0x35, 0xb8, 0xe6, 0x95, 0x14, 0x55, 0x06, 0x41, 0x75, 0x4b, + 0x59, 0x72, 0x71, 0x23, 0x09, 0x0b, 0x09, 0x70, 0x31, 0x67, 0xa7, 0x56, 0x42, 0x6d, 0x02, 0x31, + 0x85, 0x44, 0xb8, 0x58, 0xcb, 0x12, 0x73, 0x4a, 0x53, 0x25, 0x98, 0xc0, 0x62, 0x10, 0x8e, 0x15, + 0x93, 0x05, 0x23, 0xaa, 0x53, 0x43, 0x0b, 0x52, 0xa8, 0xe2, 0x54, 0x88, 0x39, 0xd4, 0x76, 0xaa, + 0x2a, 0x92, 0x4b, 0x5d, 0x52, 0x73, 0x52, 0xb1, 0xbb, 0xd4, 0x29, 0xe0, 0xc4, 0x43, 0x39, 0x86, + 0x1b, 0x0f, 0xe5, 0x18, 0x1a, 0x1e, 0xc9, 0x31, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, + 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x0b, 0xbe, 0xc8, 0x31, 0x46, 0x19, 0x91, 0x90, 0x84, 0xac, 0x21, + 0x54, 0x04, 0x43, 0x04, 0x63, 0x12, 0x1b, 0x38, 0x66, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x50, 0x87, 0x59, 0x83, 0x02, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/namespace.proto b/src/runtime/vendor/github.com/containerd/containerd/api/events/namespace.proto new file mode 100644 index 000000000..45deae79a --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/namespace.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package containerd.events; + +import weak "gogoproto/gogo.proto"; +import weak "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto"; + +option go_package = "github.com/containerd/containerd/api/events;events"; +option (containerd.plugin.fieldpath_all) = true; + +message NamespaceCreate { + string name = 1; + map labels = 2; +} + +message NamespaceUpdate { + string name = 1; + map labels = 2; +} + +message NamespaceDelete { + string name = 1; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/snapshot.pb.go b/src/runtime/vendor/github.com/containerd/containerd/api/events/snapshot.pb.go new file mode 100644 index 000000000..e1f8f5c58 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/snapshot.pb.go @@ -0,0 +1,704 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/api/events/snapshot.proto + +package events + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type SnapshotPrepare struct { + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Parent string `protobuf:"bytes,2,opt,name=parent,proto3" json:"parent,omitempty"` +} + +func (m *SnapshotPrepare) Reset() { *m = SnapshotPrepare{} } +func (*SnapshotPrepare) ProtoMessage() {} +func (*SnapshotPrepare) Descriptor() ([]byte, []int) { return fileDescriptorSnapshot, []int{0} } + +type SnapshotCommit struct { + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *SnapshotCommit) Reset() { *m = SnapshotCommit{} } +func (*SnapshotCommit) ProtoMessage() {} +func (*SnapshotCommit) Descriptor() ([]byte, []int) { return fileDescriptorSnapshot, []int{1} } + +type SnapshotRemove struct { + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` +} + +func (m *SnapshotRemove) Reset() { *m = SnapshotRemove{} } +func (*SnapshotRemove) ProtoMessage() {} +func (*SnapshotRemove) Descriptor() ([]byte, []int) { return fileDescriptorSnapshot, []int{2} } + +func init() { + proto.RegisterType((*SnapshotPrepare)(nil), "containerd.events.SnapshotPrepare") + proto.RegisterType((*SnapshotCommit)(nil), "containerd.events.SnapshotCommit") + proto.RegisterType((*SnapshotRemove)(nil), "containerd.events.SnapshotRemove") +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *SnapshotPrepare) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "key": + return string(m.Key), len(m.Key) > 0 + case "parent": + return string(m.Parent), len(m.Parent) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *SnapshotCommit) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "key": + return string(m.Key), len(m.Key) > 0 + case "name": + return string(m.Name), len(m.Name) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *SnapshotRemove) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "key": + return string(m.Key), len(m.Key) > 0 + } + return "", false +} +func (m *SnapshotPrepare) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SnapshotPrepare) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Key) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Key))) + i += copy(dAtA[i:], m.Key) + } + if len(m.Parent) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Parent))) + i += copy(dAtA[i:], m.Parent) + } + return i, nil +} + +func (m *SnapshotCommit) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SnapshotCommit) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Key) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Key))) + i += copy(dAtA[i:], m.Key) + } + if len(m.Name) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + return i, nil +} + +func (m *SnapshotRemove) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SnapshotRemove) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Key) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Key))) + i += copy(dAtA[i:], m.Key) + } + return i, nil +} + +func encodeVarintSnapshot(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *SnapshotPrepare) Size() (n int) { + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovSnapshot(uint64(l)) + } + l = len(m.Parent) + if l > 0 { + n += 1 + l + sovSnapshot(uint64(l)) + } + return n +} + +func (m *SnapshotCommit) Size() (n int) { + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovSnapshot(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovSnapshot(uint64(l)) + } + return n +} + +func (m *SnapshotRemove) Size() (n int) { + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovSnapshot(uint64(l)) + } + return n +} + +func sovSnapshot(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozSnapshot(x uint64) (n int) { + return sovSnapshot(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *SnapshotPrepare) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&SnapshotPrepare{`, + `Key:` + fmt.Sprintf("%v", this.Key) + `,`, + `Parent:` + fmt.Sprintf("%v", this.Parent) + `,`, + `}`, + }, "") + return s +} +func (this *SnapshotCommit) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&SnapshotCommit{`, + `Key:` + fmt.Sprintf("%v", this.Key) + `,`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `}`, + }, "") + return s +} +func (this *SnapshotRemove) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&SnapshotRemove{`, + `Key:` + fmt.Sprintf("%v", this.Key) + `,`, + `}`, + }, "") + return s +} +func valueToStringSnapshot(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *SnapshotPrepare) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SnapshotPrepare: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SnapshotPrepare: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Parent", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Parent = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSnapshot(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthSnapshot + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SnapshotCommit) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SnapshotCommit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SnapshotCommit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSnapshot(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthSnapshot + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SnapshotRemove) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SnapshotRemove: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SnapshotRemove: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshot + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSnapshot + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSnapshot(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthSnapshot + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSnapshot(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSnapshot + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSnapshot + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSnapshot + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthSnapshot + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSnapshot + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipSnapshot(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthSnapshot = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSnapshot = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/snapshot.proto", fileDescriptorSnapshot) +} + +var fileDescriptorSnapshot = []byte{ + // 235 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4a, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0xeb, 0x17, 0xe7, + 0x25, 0x16, 0x14, 0x67, 0xe4, 0x97, 0xe8, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x22, 0x54, + 0xe9, 0x41, 0x54, 0x48, 0x39, 0x10, 0x34, 0x0e, 0xac, 0x35, 0xa9, 0x34, 0x4d, 0xbf, 0x20, 0xa7, + 0x34, 0x3d, 0x33, 0x4f, 0x3f, 0x2d, 0x33, 0x35, 0x27, 0xa5, 0x20, 0xb1, 0x24, 0x03, 0x62, 0xa8, + 0x92, 0x35, 0x17, 0x7f, 0x30, 0xd4, 0x9a, 0x80, 0xa2, 0xd4, 0x82, 0xc4, 0xa2, 0x54, 0x21, 0x01, + 0x2e, 0xe6, 0xec, 0xd4, 0x4a, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x10, 0x53, 0x48, 0x8c, + 0x8b, 0x0d, 0x24, 0x93, 0x57, 0x22, 0xc1, 0x04, 0x16, 0x84, 0xf2, 0x94, 0xcc, 0xb8, 0xf8, 0x60, + 0x9a, 0x9d, 0xf3, 0x73, 0x73, 0x33, 0x4b, 0xb0, 0xe8, 0x15, 0xe2, 0x62, 0xc9, 0x4b, 0xcc, 0x4d, + 0x85, 0xea, 0x04, 0xb3, 0x95, 0x94, 0x10, 0xfa, 0x82, 0x52, 0x73, 0xf3, 0xcb, 0xb0, 0xd8, 0xe9, + 0x14, 0x70, 0xe2, 0xa1, 0x1c, 0xc3, 0x8d, 0x87, 0x72, 0x0c, 0x0d, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, + 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x05, 0x5f, 0xe4, 0x18, 0xa3, + 0x8c, 0x48, 0x08, 0x47, 0x6b, 0x08, 0x15, 0xc1, 0x90, 0xc4, 0x06, 0xf6, 0xb3, 0x31, 0x20, 0x00, + 0x00, 0xff, 0xff, 0x69, 0x66, 0xa9, 0x2a, 0x86, 0x01, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/snapshot.proto b/src/runtime/vendor/github.com/containerd/containerd/api/events/snapshot.proto new file mode 100644 index 000000000..425eeec8e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/snapshot.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package containerd.events; + +import weak "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto"; + +option go_package = "github.com/containerd/containerd/api/events;events"; +option (containerd.plugin.fieldpath_all) = true; + +message SnapshotPrepare { + string key = 1; + string parent = 2; +} + +message SnapshotCommit { + string key = 1; + string name = 2; +} + +message SnapshotRemove { + string key = 1; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/task.pb.go b/src/runtime/vendor/github.com/containerd/containerd/api/events/task.pb.go new file mode 100644 index 000000000..b0a6c2c24 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/task.pb.go @@ -0,0 +1,2610 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/api/events/task.proto + +package events + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" +import _ "github.com/gogo/protobuf/types" +import containerd_types "github.com/containerd/containerd/api/types" + +// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" + +import time "time" + +import types "github.com/gogo/protobuf/types" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +type TaskCreate struct { + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + Bundle string `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` + Rootfs []*containerd_types.Mount `protobuf:"bytes,3,rep,name=rootfs" json:"rootfs,omitempty"` + IO *TaskIO `protobuf:"bytes,4,opt,name=io" json:"io,omitempty"` + Checkpoint string `protobuf:"bytes,5,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` + Pid uint32 `protobuf:"varint,6,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (m *TaskCreate) Reset() { *m = TaskCreate{} } +func (*TaskCreate) ProtoMessage() {} +func (*TaskCreate) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{0} } + +type TaskStart struct { + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (m *TaskStart) Reset() { *m = TaskStart{} } +func (*TaskStart) ProtoMessage() {} +func (*TaskStart) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{1} } + +type TaskDelete struct { + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` + ExitStatus uint32 `protobuf:"varint,3,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,4,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` +} + +func (m *TaskDelete) Reset() { *m = TaskDelete{} } +func (*TaskDelete) ProtoMessage() {} +func (*TaskDelete) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{2} } + +type TaskIO struct { + Stdin string `protobuf:"bytes,1,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,2,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,3,opt,name=stderr,proto3" json:"stderr,omitempty"` + Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` +} + +func (m *TaskIO) Reset() { *m = TaskIO{} } +func (*TaskIO) ProtoMessage() {} +func (*TaskIO) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{3} } + +type TaskExit struct { + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` + ExitStatus uint32 `protobuf:"varint,4,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,5,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` +} + +func (m *TaskExit) Reset() { *m = TaskExit{} } +func (*TaskExit) ProtoMessage() {} +func (*TaskExit) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{4} } + +type TaskOOM struct { + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` +} + +func (m *TaskOOM) Reset() { *m = TaskOOM{} } +func (*TaskOOM) ProtoMessage() {} +func (*TaskOOM) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{5} } + +type TaskExecAdded struct { + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` +} + +func (m *TaskExecAdded) Reset() { *m = TaskExecAdded{} } +func (*TaskExecAdded) ProtoMessage() {} +func (*TaskExecAdded) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{6} } + +type TaskExecStarted struct { + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (m *TaskExecStarted) Reset() { *m = TaskExecStarted{} } +func (*TaskExecStarted) ProtoMessage() {} +func (*TaskExecStarted) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{7} } + +type TaskPaused struct { + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` +} + +func (m *TaskPaused) Reset() { *m = TaskPaused{} } +func (*TaskPaused) ProtoMessage() {} +func (*TaskPaused) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{8} } + +type TaskResumed struct { + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` +} + +func (m *TaskResumed) Reset() { *m = TaskResumed{} } +func (*TaskResumed) ProtoMessage() {} +func (*TaskResumed) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{9} } + +type TaskCheckpointed struct { + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + Checkpoint string `protobuf:"bytes,2,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` +} + +func (m *TaskCheckpointed) Reset() { *m = TaskCheckpointed{} } +func (*TaskCheckpointed) ProtoMessage() {} +func (*TaskCheckpointed) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{10} } + +func init() { + proto.RegisterType((*TaskCreate)(nil), "containerd.events.TaskCreate") + proto.RegisterType((*TaskStart)(nil), "containerd.events.TaskStart") + proto.RegisterType((*TaskDelete)(nil), "containerd.events.TaskDelete") + proto.RegisterType((*TaskIO)(nil), "containerd.events.TaskIO") + proto.RegisterType((*TaskExit)(nil), "containerd.events.TaskExit") + proto.RegisterType((*TaskOOM)(nil), "containerd.events.TaskOOM") + proto.RegisterType((*TaskExecAdded)(nil), "containerd.events.TaskExecAdded") + proto.RegisterType((*TaskExecStarted)(nil), "containerd.events.TaskExecStarted") + proto.RegisterType((*TaskPaused)(nil), "containerd.events.TaskPaused") + proto.RegisterType((*TaskResumed)(nil), "containerd.events.TaskResumed") + proto.RegisterType((*TaskCheckpointed)(nil), "containerd.events.TaskCheckpointed") +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *TaskCreate) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + // unhandled: rootfs + // unhandled: pid + case "container_id": + return string(m.ContainerID), len(m.ContainerID) > 0 + case "bundle": + return string(m.Bundle), len(m.Bundle) > 0 + case "io": + // NOTE(stevvooe): This is probably not correct in many cases. + // We assume that the target message also implements the Field + // method, which isn't likely true in a lot of cases. + // + // If you have a broken build and have found this comment, + // you may be closer to a solution. + if m.IO == nil { + return "", false + } + + return m.IO.Field(fieldpath[1:]) + case "checkpoint": + return string(m.Checkpoint), len(m.Checkpoint) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *TaskStart) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + // unhandled: pid + case "container_id": + return string(m.ContainerID), len(m.ContainerID) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *TaskDelete) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + // unhandled: pid + // unhandled: exit_status + // unhandled: exited_at + case "container_id": + return string(m.ContainerID), len(m.ContainerID) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *TaskIO) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "stdin": + return string(m.Stdin), len(m.Stdin) > 0 + case "stdout": + return string(m.Stdout), len(m.Stdout) > 0 + case "stderr": + return string(m.Stderr), len(m.Stderr) > 0 + case "terminal": + return fmt.Sprint(m.Terminal), true + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *TaskExit) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + // unhandled: pid + // unhandled: exit_status + // unhandled: exited_at + case "container_id": + return string(m.ContainerID), len(m.ContainerID) > 0 + case "id": + return string(m.ID), len(m.ID) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *TaskOOM) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "container_id": + return string(m.ContainerID), len(m.ContainerID) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *TaskExecAdded) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "container_id": + return string(m.ContainerID), len(m.ContainerID) > 0 + case "exec_id": + return string(m.ExecID), len(m.ExecID) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *TaskExecStarted) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + // unhandled: pid + case "container_id": + return string(m.ContainerID), len(m.ContainerID) > 0 + case "exec_id": + return string(m.ExecID), len(m.ExecID) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *TaskPaused) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "container_id": + return string(m.ContainerID), len(m.ContainerID) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *TaskResumed) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "container_id": + return string(m.ContainerID), len(m.ContainerID) > 0 + } + return "", false +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (m *TaskCheckpointed) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "container_id": + return string(m.ContainerID), len(m.ContainerID) > 0 + case "checkpoint": + return string(m.Checkpoint), len(m.Checkpoint) > 0 + } + return "", false +} +func (m *TaskCreate) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskCreate) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ContainerID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i += copy(dAtA[i:], m.ContainerID) + } + if len(m.Bundle) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.Bundle))) + i += copy(dAtA[i:], m.Bundle) + } + if len(m.Rootfs) > 0 { + for _, msg := range m.Rootfs { + dAtA[i] = 0x1a + i++ + i = encodeVarintTask(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.IO != nil { + dAtA[i] = 0x22 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.IO.Size())) + n1, err := m.IO.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + } + if len(m.Checkpoint) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.Checkpoint))) + i += copy(dAtA[i:], m.Checkpoint) + } + if m.Pid != 0 { + dAtA[i] = 0x30 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + } + return i, nil +} + +func (m *TaskStart) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskStart) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ContainerID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i += copy(dAtA[i:], m.ContainerID) + } + if m.Pid != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + } + return i, nil +} + +func (m *TaskDelete) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskDelete) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ContainerID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i += copy(dAtA[i:], m.ContainerID) + } + if m.Pid != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + } + if m.ExitStatus != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.ExitStatus)) + } + dAtA[i] = 0x22 + i++ + i = encodeVarintTask(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) + n2, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + return i, nil +} + +func (m *TaskIO) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskIO) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Stdin) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.Stdin))) + i += copy(dAtA[i:], m.Stdin) + } + if len(m.Stdout) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.Stdout))) + i += copy(dAtA[i:], m.Stdout) + } + if len(m.Stderr) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.Stderr))) + i += copy(dAtA[i:], m.Stderr) + } + if m.Terminal { + dAtA[i] = 0x20 + i++ + if m.Terminal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + return i, nil +} + +func (m *TaskExit) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskExit) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ContainerID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i += copy(dAtA[i:], m.ContainerID) + } + if len(m.ID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if m.Pid != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + } + if m.ExitStatus != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.ExitStatus)) + } + dAtA[i] = 0x2a + i++ + i = encodeVarintTask(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) + n3, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n3 + return i, nil +} + +func (m *TaskOOM) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskOOM) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ContainerID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i += copy(dAtA[i:], m.ContainerID) + } + return i, nil +} + +func (m *TaskExecAdded) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskExecAdded) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ContainerID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i += copy(dAtA[i:], m.ContainerID) + } + if len(m.ExecID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + return i, nil +} + +func (m *TaskExecStarted) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskExecStarted) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ContainerID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i += copy(dAtA[i:], m.ContainerID) + } + if len(m.ExecID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + if m.Pid != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + } + return i, nil +} + +func (m *TaskPaused) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskPaused) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ContainerID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i += copy(dAtA[i:], m.ContainerID) + } + return i, nil +} + +func (m *TaskResumed) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskResumed) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ContainerID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i += copy(dAtA[i:], m.ContainerID) + } + return i, nil +} + +func (m *TaskCheckpointed) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TaskCheckpointed) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ContainerID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i += copy(dAtA[i:], m.ContainerID) + } + if len(m.Checkpoint) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.Checkpoint))) + i += copy(dAtA[i:], m.Checkpoint) + } + return i, nil +} + +func encodeVarintTask(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *TaskCreate) Size() (n int) { + var l int + _ = l + l = len(m.ContainerID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + l = len(m.Bundle) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + if len(m.Rootfs) > 0 { + for _, e := range m.Rootfs { + l = e.Size() + n += 1 + l + sovTask(uint64(l)) + } + } + if m.IO != nil { + l = m.IO.Size() + n += 1 + l + sovTask(uint64(l)) + } + l = len(m.Checkpoint) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + if m.Pid != 0 { + n += 1 + sovTask(uint64(m.Pid)) + } + return n +} + +func (m *TaskStart) Size() (n int) { + var l int + _ = l + l = len(m.ContainerID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + if m.Pid != 0 { + n += 1 + sovTask(uint64(m.Pid)) + } + return n +} + +func (m *TaskDelete) Size() (n int) { + var l int + _ = l + l = len(m.ContainerID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + if m.Pid != 0 { + n += 1 + sovTask(uint64(m.Pid)) + } + if m.ExitStatus != 0 { + n += 1 + sovTask(uint64(m.ExitStatus)) + } + l = types.SizeOfStdTime(m.ExitedAt) + n += 1 + l + sovTask(uint64(l)) + return n +} + +func (m *TaskIO) Size() (n int) { + var l int + _ = l + l = len(m.Stdin) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + l = len(m.Stdout) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + l = len(m.Stderr) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + if m.Terminal { + n += 2 + } + return n +} + +func (m *TaskExit) Size() (n int) { + var l int + _ = l + l = len(m.ContainerID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + l = len(m.ID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + if m.Pid != 0 { + n += 1 + sovTask(uint64(m.Pid)) + } + if m.ExitStatus != 0 { + n += 1 + sovTask(uint64(m.ExitStatus)) + } + l = types.SizeOfStdTime(m.ExitedAt) + n += 1 + l + sovTask(uint64(l)) + return n +} + +func (m *TaskOOM) Size() (n int) { + var l int + _ = l + l = len(m.ContainerID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + return n +} + +func (m *TaskExecAdded) Size() (n int) { + var l int + _ = l + l = len(m.ContainerID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + return n +} + +func (m *TaskExecStarted) Size() (n int) { + var l int + _ = l + l = len(m.ContainerID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + if m.Pid != 0 { + n += 1 + sovTask(uint64(m.Pid)) + } + return n +} + +func (m *TaskPaused) Size() (n int) { + var l int + _ = l + l = len(m.ContainerID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + return n +} + +func (m *TaskResumed) Size() (n int) { + var l int + _ = l + l = len(m.ContainerID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + return n +} + +func (m *TaskCheckpointed) Size() (n int) { + var l int + _ = l + l = len(m.ContainerID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + l = len(m.Checkpoint) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + return n +} + +func sovTask(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozTask(x uint64) (n int) { + return sovTask(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *TaskCreate) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TaskCreate{`, + `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `Bundle:` + fmt.Sprintf("%v", this.Bundle) + `,`, + `Rootfs:` + strings.Replace(fmt.Sprintf("%v", this.Rootfs), "Mount", "containerd_types.Mount", 1) + `,`, + `IO:` + strings.Replace(fmt.Sprintf("%v", this.IO), "TaskIO", "TaskIO", 1) + `,`, + `Checkpoint:` + fmt.Sprintf("%v", this.Checkpoint) + `,`, + `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `}`, + }, "") + return s +} +func (this *TaskStart) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TaskStart{`, + `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `}`, + }, "") + return s +} +func (this *TaskDelete) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TaskDelete{`, + `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *TaskIO) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TaskIO{`, + `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, + `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, + `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, + `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, + `}`, + }, "") + return s +} +func (this *TaskExit) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TaskExit{`, + `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *TaskOOM) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TaskOOM{`, + `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `}`, + }, "") + return s +} +func (this *TaskExecAdded) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TaskExecAdded{`, + `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `}`, + }, "") + return s +} +func (this *TaskExecStarted) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TaskExecStarted{`, + `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `}`, + }, "") + return s +} +func (this *TaskPaused) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TaskPaused{`, + `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `}`, + }, "") + return s +} +func (this *TaskResumed) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TaskResumed{`, + `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `}`, + }, "") + return s +} +func (this *TaskCheckpointed) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TaskCheckpointed{`, + `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `Checkpoint:` + fmt.Sprintf("%v", this.Checkpoint) + `,`, + `}`, + }, "") + return s +} +func valueToStringTask(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *TaskCreate) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskCreate: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskCreate: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContainerID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bundle", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bundle = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rootfs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Rootfs = append(m.Rootfs, &containerd_types.Mount{}) + if err := m.Rootfs[len(m.Rootfs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IO", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.IO == nil { + m.IO = &TaskIO{} + } + if err := m.IO.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Checkpoint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Checkpoint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskStart) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskStart: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskStart: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContainerID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskDelete) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskDelete: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskDelete: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContainerID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitStatus", wireType) + } + m.ExitStatus = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExitStatus |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskIO) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskIO: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskIO: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdout = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stderr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Terminal", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Terminal = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskExit) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskExit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskExit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContainerID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitStatus", wireType) + } + m.ExitStatus = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExitStatus |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskOOM) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskOOM: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskOOM: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContainerID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskExecAdded) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskExecAdded: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskExecAdded: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContainerID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskExecStarted) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskExecStarted: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskExecStarted: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContainerID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskPaused) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskPaused: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskPaused: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContainerID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskResumed) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskResumed: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskResumed: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContainerID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TaskCheckpointed) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TaskCheckpointed: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TaskCheckpointed: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContainerID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Checkpoint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Checkpoint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTask(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTask + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTask + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTask + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthTask + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTask + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipTask(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthTask = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTask = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/task.proto", fileDescriptorTask) +} + +var fileDescriptorTask = []byte{ + // 637 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0xc7, 0x63, 0xa7, 0x75, 0x93, 0x09, 0x55, 0x8b, 0x55, 0x41, 0xc8, 0xc1, 0x8e, 0xcc, 0x25, + 0x27, 0x5b, 0x04, 0x89, 0x0b, 0x42, 0x6a, 0xd2, 0x70, 0xc8, 0xa1, 0x4a, 0x71, 0x7b, 0xa8, 0xb8, + 0x44, 0x4e, 0x76, 0x93, 0x2c, 0x8d, 0xbd, 0x96, 0x3d, 0x46, 0x45, 0xe2, 0xc0, 0x23, 0xf0, 0x08, + 0x3c, 0x05, 0xcf, 0xd0, 0x03, 0x07, 0x8e, 0x9c, 0x02, 0xf5, 0x03, 0x70, 0xe2, 0x01, 0xd0, 0x7a, + 0x1d, 0xb7, 0x50, 0xf1, 0x65, 0x89, 0x53, 0x76, 0x66, 0x67, 0xff, 0x33, 0xf3, 0xdb, 0xc9, 0x1a, + 0x1e, 0xcd, 0x19, 0x2e, 0x92, 0x89, 0x3d, 0xe5, 0xbe, 0x33, 0xe5, 0x01, 0x7a, 0x2c, 0xa0, 0x11, + 0xb9, 0xbe, 0xf4, 0x42, 0xe6, 0xd0, 0x97, 0x34, 0xc0, 0xd8, 0x41, 0x2f, 0x3e, 0xb3, 0xc3, 0x88, + 0x23, 0xd7, 0x6f, 0x5f, 0x45, 0xd8, 0x72, 0xb7, 0xb5, 0x37, 0xe7, 0x73, 0x9e, 0xed, 0x3a, 0x62, + 0x25, 0x03, 0x5b, 0xe6, 0x9c, 0xf3, 0xf9, 0x92, 0x3a, 0x99, 0x35, 0x49, 0x66, 0x0e, 0x32, 0x9f, + 0xc6, 0xe8, 0xf9, 0x61, 0x1e, 0xf0, 0x77, 0x15, 0xe0, 0xab, 0x90, 0xc6, 0x8e, 0xcf, 0x93, 0x00, + 0xf3, 0x73, 0xfb, 0x7f, 0x3c, 0x57, 0xa4, 0x0c, 0x97, 0xc9, 0x9c, 0x05, 0xce, 0x8c, 0xd1, 0x25, + 0x09, 0x3d, 0x5c, 0x48, 0x05, 0xeb, 0xab, 0x02, 0x70, 0xe2, 0xc5, 0x67, 0x07, 0x11, 0xf5, 0x90, + 0xea, 0x5d, 0xb8, 0x55, 0x1c, 0x1e, 0x33, 0xd2, 0x54, 0xda, 0x4a, 0xa7, 0xde, 0xdf, 0x49, 0x57, + 0x66, 0xe3, 0x60, 0xed, 0x1f, 0x0e, 0xdc, 0x46, 0x11, 0x34, 0x24, 0xfa, 0x1d, 0xd0, 0x26, 0x49, + 0x40, 0x96, 0xb4, 0xa9, 0x8a, 0x68, 0x37, 0xb7, 0x74, 0x07, 0xb4, 0x88, 0x73, 0x9c, 0xc5, 0xcd, + 0x6a, 0xbb, 0xda, 0x69, 0x74, 0xef, 0xda, 0xd7, 0x78, 0x65, 0xbd, 0xd8, 0x87, 0xa2, 0x17, 0x37, + 0x0f, 0xd3, 0x1f, 0x80, 0xca, 0x78, 0x73, 0xa3, 0xad, 0x74, 0x1a, 0xdd, 0x7b, 0xf6, 0x0d, 0xb8, + 0xb6, 0xa8, 0x73, 0x38, 0xea, 0x6b, 0xe9, 0xca, 0x54, 0x87, 0x23, 0x57, 0x65, 0x5c, 0x37, 0x00, + 0xa6, 0x0b, 0x3a, 0x3d, 0x0b, 0x39, 0x0b, 0xb0, 0xb9, 0x99, 0xe5, 0xbf, 0xe6, 0xd1, 0x77, 0xa1, + 0x1a, 0x32, 0xd2, 0xd4, 0xda, 0x4a, 0x67, 0xdb, 0x15, 0x4b, 0xeb, 0x19, 0xd4, 0x85, 0xce, 0x31, + 0x7a, 0x11, 0x96, 0x6a, 0x37, 0x97, 0x54, 0xaf, 0x24, 0xdf, 0xe7, 0x0c, 0x07, 0x74, 0x49, 0x4b, + 0x32, 0xbc, 0x21, 0xaa, 0x9b, 0xd0, 0xa0, 0xe7, 0x0c, 0xc7, 0x31, 0x7a, 0x98, 0x08, 0x84, 0x62, + 0x07, 0x84, 0xeb, 0x38, 0xf3, 0xe8, 0x3d, 0xa8, 0x0b, 0x8b, 0x92, 0xb1, 0x87, 0x39, 0xb4, 0x96, + 0x2d, 0x07, 0xcd, 0x5e, 0xdf, 0xba, 0x7d, 0xb2, 0x1e, 0xb4, 0x7e, 0xed, 0x62, 0x65, 0x56, 0xde, + 0x7e, 0x36, 0x15, 0xb7, 0x26, 0x8f, 0xf5, 0xd0, 0x7a, 0x01, 0x9a, 0x64, 0xaa, 0xef, 0xc1, 0x66, + 0x8c, 0x84, 0x05, 0xb2, 0x58, 0x57, 0x1a, 0xe2, 0x66, 0x63, 0x24, 0x3c, 0xc1, 0xf5, 0xcd, 0x4a, + 0x2b, 0xf7, 0xd3, 0x28, 0xca, 0xca, 0x92, 0x7e, 0x1a, 0x45, 0x7a, 0x0b, 0x6a, 0x48, 0x23, 0x9f, + 0x05, 0xde, 0x32, 0xab, 0xa8, 0xe6, 0x16, 0xb6, 0xf5, 0x41, 0x81, 0x9a, 0x48, 0xf6, 0xf4, 0x9c, + 0x61, 0xc9, 0x31, 0x53, 0x73, 0x42, 0xf5, 0x7c, 0x04, 0x06, 0xae, 0xca, 0x0a, 0x74, 0xd5, 0x5f, + 0xa2, 0xdb, 0xf8, 0x3d, 0xba, 0xcd, 0x52, 0xe8, 0x9e, 0xc0, 0x96, 0xe8, 0x66, 0x34, 0x3a, 0x2c, + 0xd3, 0x8c, 0xb5, 0x80, 0x6d, 0x09, 0x83, 0x4e, 0x7b, 0x84, 0x50, 0x52, 0x8a, 0xc8, 0x7d, 0xd8, + 0xa2, 0xe7, 0x74, 0x3a, 0x2e, 0xb0, 0x40, 0xba, 0x32, 0x35, 0xa1, 0x39, 0x1c, 0xb8, 0x9a, 0xd8, + 0x1a, 0x12, 0xeb, 0x35, 0xec, 0xac, 0x33, 0x65, 0x33, 0xff, 0x1f, 0x73, 0xdd, 0xbc, 0x0a, 0x6b, + 0x5f, 0xfe, 0x33, 0x8e, 0xbc, 0x24, 0x2e, 0x97, 0xd8, 0xea, 0x41, 0x43, 0x28, 0xb8, 0x34, 0x4e, + 0xfc, 0x92, 0x12, 0x33, 0xd8, 0xcd, 0x9e, 0xb8, 0xe2, 0x59, 0x28, 0xc9, 0xe0, 0xc7, 0xc7, 0x46, + 0xfd, 0xf9, 0xb1, 0xe9, 0x1f, 0x5d, 0x5c, 0x1a, 0x95, 0x4f, 0x97, 0x46, 0xe5, 0x4d, 0x6a, 0x28, + 0x17, 0xa9, 0xa1, 0x7c, 0x4c, 0x0d, 0xe5, 0x4b, 0x6a, 0x28, 0xef, 0xbe, 0x19, 0xca, 0xf3, 0xee, + 0x3f, 0x7c, 0x65, 0x1e, 0xcb, 0x9f, 0xd3, 0xca, 0x69, 0x75, 0xa2, 0x65, 0x13, 0xf9, 0xf0, 0x7b, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x07, 0x69, 0x62, 0x9d, 0xa6, 0x06, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/events/task.proto b/src/runtime/vendor/github.com/containerd/containerd/api/events/task.proto new file mode 100644 index 000000000..d69921365 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/events/task.proto @@ -0,0 +1,75 @@ +syntax = "proto3"; + +package containerd.events; + +import weak "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "github.com/containerd/containerd/api/types/mount.proto"; +import weak "github.com/containerd/containerd/protobuf/plugin/fieldpath.proto"; + +option go_package = "github.com/containerd/containerd/api/events;events"; +option (containerd.plugin.fieldpath_all) = true; + +message TaskCreate { + string container_id = 1; + string bundle = 2; + repeated containerd.types.Mount rootfs = 3; + TaskIO io = 4 [(gogoproto.customname) = "IO"]; + string checkpoint = 5; + uint32 pid = 6; +} + +message TaskStart { + string container_id = 1; + uint32 pid = 2; +} + +message TaskDelete { + string container_id = 1; + uint32 pid = 2; + uint32 exit_status = 3; + google.protobuf.Timestamp exited_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; +} + +message TaskIO { + string stdin = 1; + string stdout = 2; + string stderr = 3; + bool terminal = 4; +} + +message TaskExit { + string container_id = 1; + string id = 2; + uint32 pid = 3; + uint32 exit_status = 4; + google.protobuf.Timestamp exited_at = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; +} + +message TaskOOM { + string container_id = 1; +} + +message TaskExecAdded { + string container_id = 1; + string exec_id = 2; +} + +message TaskExecStarted { + string container_id = 1; + string exec_id = 2; + uint32 pid = 3; +} + +message TaskPaused { + string container_id = 1; +} + +message TaskResumed { + string container_id = 1; +} + +message TaskCheckpointed { + string container_id = 1; + string checkpoint = 2; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/types/descriptor.pb.go b/src/runtime/vendor/github.com/containerd/containerd/api/types/descriptor.pb.go new file mode 100644 index 000000000..93e88c0dc --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/types/descriptor.pb.go @@ -0,0 +1,410 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/api/types/descriptor.proto + +/* + Package types is a generated protocol buffer package. + + It is generated from these files: + github.com/containerd/containerd/api/types/descriptor.proto + github.com/containerd/containerd/api/types/metrics.proto + github.com/containerd/containerd/api/types/mount.proto + github.com/containerd/containerd/api/types/platform.proto + + It has these top-level messages: + Descriptor + Metric + Mount + Platform +*/ +package types + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" + +import github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +// Descriptor describes a blob in a content store. +// +// This descriptor can be used to reference content from an +// oci descriptor found in a manifest. +// See https://godoc.org/github.com/opencontainers/image-spec/specs-go/v1#Descriptor +type Descriptor struct { + MediaType string `protobuf:"bytes,1,opt,name=media_type,json=mediaType,proto3" json:"media_type,omitempty"` + Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,2,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + Size_ int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` +} + +func (m *Descriptor) Reset() { *m = Descriptor{} } +func (*Descriptor) ProtoMessage() {} +func (*Descriptor) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{0} } + +func init() { + proto.RegisterType((*Descriptor)(nil), "containerd.types.Descriptor") +} +func (m *Descriptor) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Descriptor) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.MediaType) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintDescriptor(dAtA, i, uint64(len(m.MediaType))) + i += copy(dAtA[i:], m.MediaType) + } + if len(m.Digest) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintDescriptor(dAtA, i, uint64(len(m.Digest))) + i += copy(dAtA[i:], m.Digest) + } + if m.Size_ != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintDescriptor(dAtA, i, uint64(m.Size_)) + } + return i, nil +} + +func encodeVarintDescriptor(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Descriptor) Size() (n int) { + var l int + _ = l + l = len(m.MediaType) + if l > 0 { + n += 1 + l + sovDescriptor(uint64(l)) + } + l = len(m.Digest) + if l > 0 { + n += 1 + l + sovDescriptor(uint64(l)) + } + if m.Size_ != 0 { + n += 1 + sovDescriptor(uint64(m.Size_)) + } + return n +} + +func sovDescriptor(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozDescriptor(x uint64) (n int) { + return sovDescriptor(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Descriptor) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Descriptor{`, + `MediaType:` + fmt.Sprintf("%v", this.MediaType) + `,`, + `Digest:` + fmt.Sprintf("%v", this.Digest) + `,`, + `Size_:` + fmt.Sprintf("%v", this.Size_) + `,`, + `}`, + }, "") + return s +} +func valueToStringDescriptor(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Descriptor) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDescriptor + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Descriptor: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Descriptor: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MediaType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDescriptor + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDescriptor + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MediaType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Digest", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDescriptor + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDescriptor + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Digest = github_com_opencontainers_go_digest.Digest(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size_", wireType) + } + m.Size_ = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDescriptor + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Size_ |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipDescriptor(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDescriptor + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipDescriptor(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDescriptor + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDescriptor + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDescriptor + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthDescriptor + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDescriptor + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipDescriptor(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthDescriptor = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDescriptor = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/types/descriptor.proto", fileDescriptorDescriptor) +} + +var fileDescriptorDescriptor = []byte{ + // 234 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xa7, 0xa4, 0x16, + 0x27, 0x17, 0x65, 0x16, 0x94, 0xe4, 0x17, 0xe9, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, + 0x94, 0xe9, 0x81, 0x95, 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf5, 0x41, 0x2c, 0x88, + 0x3a, 0xa5, 0x6e, 0x46, 0x2e, 0x2e, 0x17, 0xb8, 0x66, 0x21, 0x59, 0x2e, 0xae, 0xdc, 0xd4, 0x94, + 0xcc, 0xc4, 0x78, 0x90, 0x1e, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x4e, 0xb0, 0x48, 0x48, + 0x65, 0x41, 0xaa, 0x90, 0x17, 0x17, 0x5b, 0x4a, 0x66, 0x7a, 0x6a, 0x71, 0x89, 0x04, 0x13, 0x48, + 0xca, 0xc9, 0xe8, 0xc4, 0x3d, 0x79, 0x86, 0x5b, 0xf7, 0xe4, 0xb5, 0x90, 0x9c, 0x9a, 0x5f, 0x90, + 0x9a, 0x07, 0xb7, 0xbc, 0x58, 0x3f, 0x3d, 0x5f, 0x17, 0xa2, 0x45, 0xcf, 0x05, 0x4c, 0x05, 0x41, + 0x4d, 0x10, 0x12, 0xe2, 0x62, 0x29, 0xce, 0xac, 0x4a, 0x95, 0x60, 0x56, 0x60, 0xd4, 0x60, 0x0e, + 0x02, 0xb3, 0x9d, 0xbc, 0x4e, 0x3c, 0x94, 0x63, 0xb8, 0xf1, 0x50, 0x8e, 0xa1, 0xe1, 0x91, 0x1c, + 0xe3, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x18, 0x65, 0x40, + 0x7c, 0x60, 0x58, 0x83, 0xc9, 0x08, 0x86, 0x24, 0x36, 0xb0, 0x17, 0x8d, 0x01, 0x01, 0x00, 0x00, + 0xff, 0xff, 0xea, 0xac, 0x78, 0x9a, 0x49, 0x01, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/types/descriptor.proto b/src/runtime/vendor/github.com/containerd/containerd/api/types/descriptor.proto new file mode 100644 index 000000000..5c00dca4f --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/types/descriptor.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package containerd.types; + +import weak "gogoproto/gogo.proto"; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +// Descriptor describes a blob in a content store. +// +// This descriptor can be used to reference content from an +// oci descriptor found in a manifest. +// See https://godoc.org/github.com/opencontainers/image-spec/specs-go/v1#Descriptor +message Descriptor { + string media_type = 1; + string digest = 2 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; + int64 size = 3; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/types/doc.go b/src/runtime/vendor/github.com/containerd/containerd/api/types/doc.go new file mode 100644 index 000000000..475b465ed --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/types/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + 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 types diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/types/metrics.pb.go b/src/runtime/vendor/github.com/containerd/containerd/api/types/metrics.pb.go new file mode 100644 index 000000000..52e9f40a5 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/types/metrics.pb.go @@ -0,0 +1,412 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/api/types/metrics.proto + +package types + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" +import google_protobuf1 "github.com/gogo/protobuf/types" +import _ "github.com/gogo/protobuf/types" + +import time "time" + +import types1 "github.com/gogo/protobuf/types" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +type Metric struct { + Timestamp time.Time `protobuf:"bytes,1,opt,name=timestamp,stdtime" json:"timestamp"` + ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Data *google_protobuf1.Any `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"` +} + +func (m *Metric) Reset() { *m = Metric{} } +func (*Metric) ProtoMessage() {} +func (*Metric) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{0} } + +func init() { + proto.RegisterType((*Metric)(nil), "containerd.types.Metric") +} +func (m *Metric) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Metric) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0xa + i++ + i = encodeVarintMetrics(dAtA, i, uint64(types1.SizeOfStdTime(m.Timestamp))) + n1, err := types1.StdTimeMarshalTo(m.Timestamp, dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + if len(m.ID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintMetrics(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if m.Data != nil { + dAtA[i] = 0x1a + i++ + i = encodeVarintMetrics(dAtA, i, uint64(m.Data.Size())) + n2, err := m.Data.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + } + return i, nil +} + +func encodeVarintMetrics(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Metric) Size() (n int) { + var l int + _ = l + l = types1.SizeOfStdTime(m.Timestamp) + n += 1 + l + sovMetrics(uint64(l)) + l = len(m.ID) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Data != nil { + l = m.Data.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + return n +} + +func sovMetrics(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozMetrics(x uint64) (n int) { + return sovMetrics(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Metric) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Metric{`, + `Timestamp:` + strings.Replace(strings.Replace(this.Timestamp.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Data:` + strings.Replace(fmt.Sprintf("%v", this.Data), "Any", "google_protobuf1.Any", 1) + `,`, + `}`, + }, "") + return s +} +func valueToStringMetrics(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Metric) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Metric: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Metric: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := types1.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Data == nil { + m.Data = &google_protobuf1.Any{} + } + if err := m.Data.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMetrics(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthMetrics + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipMetrics(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthMetrics = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMetrics = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/types/metrics.proto", fileDescriptorMetrics) +} + +var fileDescriptorMetrics = []byte{ + // 258 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x48, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xe7, 0xa6, 0x96, + 0x14, 0x65, 0x26, 0x17, 0xeb, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0xd4, 0xe8, 0x81, + 0xe5, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x94, 0x64, + 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x3e, 0x98, 0x97, 0x54, 0x9a, 0xa6, 0x9f, 0x98, 0x57, 0x09, + 0x95, 0x92, 0x47, 0x97, 0x2a, 0xc9, 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0x80, 0x28, 0x50, + 0xea, 0x63, 0xe4, 0x62, 0xf3, 0x05, 0xdb, 0x2a, 0xe4, 0xc4, 0xc5, 0x09, 0x97, 0x95, 0x60, 0x54, + 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd2, 0x83, 0xe8, 0xd7, 0x83, 0xe9, 0xd7, 0x0b, 0x81, 0xa9, 0x70, + 0xe2, 0x38, 0x71, 0x4f, 0x9e, 0x61, 0xc2, 0x7d, 0x79, 0xc6, 0x20, 0x84, 0x36, 0x21, 0x31, 0x2e, + 0xa6, 0xcc, 0x14, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x4e, 0x27, 0xb6, 0x47, 0xf7, 0xe4, 0x99, 0x3c, + 0x5d, 0x82, 0x98, 0x32, 0x53, 0x84, 0x34, 0xb8, 0x58, 0x52, 0x12, 0x4b, 0x12, 0x25, 0x98, 0xc1, + 0xc6, 0x8a, 0x60, 0x18, 0xeb, 0x98, 0x57, 0x19, 0x04, 0x56, 0xe1, 0xe4, 0x75, 0xe2, 0xa1, 0x1c, + 0xc3, 0x8d, 0x87, 0x72, 0x0c, 0x0d, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, + 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x28, 0x03, 0xe2, 0x03, 0xd2, 0x1a, 0x4c, 0x46, 0x30, 0x24, + 0xb1, 0x81, 0x6d, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xde, 0x0d, 0x02, 0xfe, 0x85, 0x01, + 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/types/metrics.proto b/src/runtime/vendor/github.com/containerd/containerd/api/types/metrics.proto new file mode 100644 index 000000000..0e631d2ac --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/types/metrics.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package containerd.types; + +import weak "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +message Metric { + google.protobuf.Timestamp timestamp = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + string id = 2; + google.protobuf.Any data = 3; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/types/mount.pb.go b/src/runtime/vendor/github.com/containerd/containerd/api/types/mount.pb.go new file mode 100644 index 000000000..f7a9c3c1f --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/types/mount.pb.go @@ -0,0 +1,456 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/api/types/mount.proto + +package types + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// Mount describes mounts for a container. +// +// This type is the lingua franca of ContainerD. All services provide mounts +// to be used with the container at creation time. +// +// The Mount type follows the structure of the mount syscall, including a type, +// source, target and options. +type Mount struct { + // Type defines the nature of the mount. + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + // Source specifies the name of the mount. Depending on mount type, this + // may be a volume name or a host path, or even ignored. + Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` + // Target path in container + Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` + // Options specifies zero or more fstab style mount options. + Options []string `protobuf:"bytes,4,rep,name=options" json:"options,omitempty"` +} + +func (m *Mount) Reset() { *m = Mount{} } +func (*Mount) ProtoMessage() {} +func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorMount, []int{0} } + +func init() { + proto.RegisterType((*Mount)(nil), "containerd.types.Mount") +} +func (m *Mount) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Mount) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Type) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintMount(dAtA, i, uint64(len(m.Type))) + i += copy(dAtA[i:], m.Type) + } + if len(m.Source) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintMount(dAtA, i, uint64(len(m.Source))) + i += copy(dAtA[i:], m.Source) + } + if len(m.Target) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintMount(dAtA, i, uint64(len(m.Target))) + i += copy(dAtA[i:], m.Target) + } + if len(m.Options) > 0 { + for _, s := range m.Options { + dAtA[i] = 0x22 + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + return i, nil +} + +func encodeVarintMount(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Mount) Size() (n int) { + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + sovMount(uint64(l)) + } + l = len(m.Source) + if l > 0 { + n += 1 + l + sovMount(uint64(l)) + } + l = len(m.Target) + if l > 0 { + n += 1 + l + sovMount(uint64(l)) + } + if len(m.Options) > 0 { + for _, s := range m.Options { + l = len(s) + n += 1 + l + sovMount(uint64(l)) + } + } + return n +} + +func sovMount(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozMount(x uint64) (n int) { + return sovMount(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Mount) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Mount{`, + `Type:` + fmt.Sprintf("%v", this.Type) + `,`, + `Source:` + fmt.Sprintf("%v", this.Source) + `,`, + `Target:` + fmt.Sprintf("%v", this.Target) + `,`, + `Options:` + fmt.Sprintf("%v", this.Options) + `,`, + `}`, + }, "") + return s +} +func valueToStringMount(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Mount) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMount + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Mount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Mount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMount + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMount + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMount + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMount + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Source = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMount + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMount + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Target = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMount + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMount + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Options = append(m.Options, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMount(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMount + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMount(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMount + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMount + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMount + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthMount + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMount + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipMount(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthMount = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMount = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/types/mount.proto", fileDescriptorMount) +} + +var fileDescriptorMount = []byte{ + // 202 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4b, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xe7, 0xe6, 0x97, + 0xe6, 0x95, 0xe8, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0x54, 0xe8, 0x81, 0x65, 0xa5, + 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x52, 0x2a, 0x17, 0xab, + 0x2f, 0x48, 0x9b, 0x90, 0x10, 0x17, 0x0b, 0x48, 0x9d, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, + 0x98, 0x2d, 0x24, 0xc6, 0xc5, 0x56, 0x9c, 0x5f, 0x5a, 0x94, 0x9c, 0x2a, 0xc1, 0x04, 0x16, 0x85, + 0xf2, 0x40, 0xe2, 0x25, 0x89, 0x45, 0xe9, 0xa9, 0x25, 0x12, 0xcc, 0x10, 0x71, 0x08, 0x4f, 0x48, + 0x82, 0x8b, 0x3d, 0xbf, 0xa0, 0x24, 0x33, 0x3f, 0xaf, 0x58, 0x82, 0x45, 0x81, 0x59, 0x83, 0x33, + 0x08, 0xc6, 0x75, 0xf2, 0x3a, 0xf1, 0x50, 0x8e, 0xe1, 0xc6, 0x43, 0x39, 0x86, 0x86, 0x47, 0x72, + 0x8c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0x63, 0x94, 0x01, + 0xf1, 0x1e, 0xb4, 0x06, 0x93, 0x11, 0x0c, 0x49, 0x6c, 0x60, 0xb7, 0x1b, 0x03, 0x02, 0x00, 0x00, + 0xff, 0xff, 0x82, 0x1c, 0x02, 0x18, 0x1d, 0x01, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/types/mount.proto b/src/runtime/vendor/github.com/containerd/containerd/api/types/mount.proto new file mode 100644 index 000000000..cd80e44a2 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/types/mount.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package containerd.types; + +import weak "gogoproto/gogo.proto"; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +// Mount describes mounts for a container. +// +// This type is the lingua franca of ContainerD. All services provide mounts +// to be used with the container at creation time. +// +// The Mount type follows the structure of the mount syscall, including a type, +// source, target and options. +message Mount { + // Type defines the nature of the mount. + string type = 1; + + // Source specifies the name of the mount. Depending on mount type, this + // may be a volume name or a host path, or even ignored. + string source = 2; + + // Target path in container + string target = 3; + + // Options specifies zero or more fstab style mount options. + repeated string options = 4; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/types/platform.pb.go b/src/runtime/vendor/github.com/containerd/containerd/api/types/platform.pb.go new file mode 100644 index 000000000..ba9a3bf88 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/types/platform.pb.go @@ -0,0 +1,394 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/api/types/platform.proto + +package types + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// Platform follows the structure of the OCI platform specification, from +// descriptors. +type Platform struct { + OS string `protobuf:"bytes,1,opt,name=os,proto3" json:"os,omitempty"` + Architecture string `protobuf:"bytes,2,opt,name=architecture,proto3" json:"architecture,omitempty"` + Variant string `protobuf:"bytes,3,opt,name=variant,proto3" json:"variant,omitempty"` +} + +func (m *Platform) Reset() { *m = Platform{} } +func (*Platform) ProtoMessage() {} +func (*Platform) Descriptor() ([]byte, []int) { return fileDescriptorPlatform, []int{0} } + +func init() { + proto.RegisterType((*Platform)(nil), "containerd.types.Platform") +} +func (m *Platform) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Platform) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.OS) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintPlatform(dAtA, i, uint64(len(m.OS))) + i += copy(dAtA[i:], m.OS) + } + if len(m.Architecture) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintPlatform(dAtA, i, uint64(len(m.Architecture))) + i += copy(dAtA[i:], m.Architecture) + } + if len(m.Variant) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintPlatform(dAtA, i, uint64(len(m.Variant))) + i += copy(dAtA[i:], m.Variant) + } + return i, nil +} + +func encodeVarintPlatform(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Platform) Size() (n int) { + var l int + _ = l + l = len(m.OS) + if l > 0 { + n += 1 + l + sovPlatform(uint64(l)) + } + l = len(m.Architecture) + if l > 0 { + n += 1 + l + sovPlatform(uint64(l)) + } + l = len(m.Variant) + if l > 0 { + n += 1 + l + sovPlatform(uint64(l)) + } + return n +} + +func sovPlatform(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozPlatform(x uint64) (n int) { + return sovPlatform(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Platform) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Platform{`, + `OS:` + fmt.Sprintf("%v", this.OS) + `,`, + `Architecture:` + fmt.Sprintf("%v", this.Architecture) + `,`, + `Variant:` + fmt.Sprintf("%v", this.Variant) + `,`, + `}`, + }, "") + return s +} +func valueToStringPlatform(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Platform) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlatform + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Platform: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Platform: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OS", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlatform + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlatform + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OS = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Architecture", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlatform + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlatform + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Architecture = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Variant", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlatform + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlatform + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Variant = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPlatform(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthPlatform + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipPlatform(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPlatform + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPlatform + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPlatform + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthPlatform + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowPlatform + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipPlatform(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthPlatform = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowPlatform = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/types/platform.proto", fileDescriptorPlatform) +} + +var fileDescriptorPlatform = []byte{ + // 205 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0x17, 0xe4, 0x24, + 0x96, 0xa4, 0xe5, 0x17, 0xe5, 0xea, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0x14, 0xe9, + 0x81, 0x15, 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf5, 0x41, 0x2c, 0x88, 0x3a, 0xa5, + 0x04, 0x2e, 0x8e, 0x00, 0xa8, 0x4e, 0x21, 0x31, 0x2e, 0xa6, 0xfc, 0x62, 0x09, 0x46, 0x05, 0x46, + 0x0d, 0x4e, 0x27, 0xb6, 0x47, 0xf7, 0xe4, 0x99, 0xfc, 0x83, 0x83, 0x98, 0xf2, 0x8b, 0x85, 0x94, + 0xb8, 0x78, 0x12, 0x8b, 0x92, 0x33, 0x32, 0x4b, 0x52, 0x93, 0x4b, 0x4a, 0x8b, 0x52, 0x25, 0x98, + 0x40, 0x2a, 0x82, 0x50, 0xc4, 0x84, 0x24, 0xb8, 0xd8, 0xcb, 0x12, 0x8b, 0x32, 0x13, 0xf3, 0x4a, + 0x24, 0x98, 0xc1, 0xd2, 0x30, 0xae, 0x93, 0xd7, 0x89, 0x87, 0x72, 0x0c, 0x37, 0x1e, 0xca, 0x31, + 0x34, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, + 0x18, 0xa3, 0x0c, 0x88, 0xf7, 0x9e, 0x35, 0x98, 0x8c, 0x60, 0x48, 0x62, 0x03, 0x3b, 0xdb, 0x18, + 0x10, 0x00, 0x00, 0xff, 0xff, 0x05, 0xaa, 0xda, 0xa1, 0x1b, 0x01, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/types/platform.proto b/src/runtime/vendor/github.com/containerd/containerd/api/types/platform.proto new file mode 100644 index 000000000..4cf9834bd --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/types/platform.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package containerd.types; + +import weak "gogoproto/gogo.proto"; + +option go_package = "github.com/containerd/containerd/api/types;types"; + +// Platform follows the structure of the OCI platform specification, from +// descriptors. +message Platform { + string os = 1 [(gogoproto.customname) = "OS"]; + string architecture = 2; + string variant = 3; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/types/task/task.pb.go b/src/runtime/vendor/github.com/containerd/containerd/api/types/task/task.pb.go new file mode 100644 index 000000000..437abe8f4 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/types/task/task.pb.go @@ -0,0 +1,890 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/api/types/task/task.proto + +/* + Package task is a generated protocol buffer package. + + It is generated from these files: + github.com/containerd/containerd/api/types/task/task.proto + + It has these top-level messages: + Process + ProcessInfo +*/ +package task + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" +import _ "github.com/gogo/protobuf/types" +import google_protobuf2 "github.com/gogo/protobuf/types" + +import time "time" + +import types "github.com/gogo/protobuf/types" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type Status int32 + +const ( + StatusUnknown Status = 0 + StatusCreated Status = 1 + StatusRunning Status = 2 + StatusStopped Status = 3 + StatusPaused Status = 4 + StatusPausing Status = 5 +) + +var Status_name = map[int32]string{ + 0: "UNKNOWN", + 1: "CREATED", + 2: "RUNNING", + 3: "STOPPED", + 4: "PAUSED", + 5: "PAUSING", +} +var Status_value = map[string]int32{ + "UNKNOWN": 0, + "CREATED": 1, + "RUNNING": 2, + "STOPPED": 3, + "PAUSED": 4, + "PAUSING": 5, +} + +func (x Status) String() string { + return proto.EnumName(Status_name, int32(x)) +} +func (Status) EnumDescriptor() ([]byte, []int) { return fileDescriptorTask, []int{0} } + +type Process struct { + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` + Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=containerd.v1.types.Status" json:"status,omitempty"` + Stdin string `protobuf:"bytes,5,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,6,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,7,opt,name=stderr,proto3" json:"stderr,omitempty"` + Terminal bool `protobuf:"varint,8,opt,name=terminal,proto3" json:"terminal,omitempty"` + ExitStatus uint32 `protobuf:"varint,9,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,10,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` +} + +func (m *Process) Reset() { *m = Process{} } +func (*Process) ProtoMessage() {} +func (*Process) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{0} } + +type ProcessInfo struct { + // PID is the process ID. + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + // Info contains additional process information. + // + // Info varies by platform. + Info *google_protobuf2.Any `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"` +} + +func (m *ProcessInfo) Reset() { *m = ProcessInfo{} } +func (*ProcessInfo) ProtoMessage() {} +func (*ProcessInfo) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{1} } + +func init() { + proto.RegisterType((*Process)(nil), "containerd.v1.types.Process") + proto.RegisterType((*ProcessInfo)(nil), "containerd.v1.types.ProcessInfo") + proto.RegisterEnum("containerd.v1.types.Status", Status_name, Status_value) +} +func (m *Process) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Process) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ContainerID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i += copy(dAtA[i:], m.ContainerID) + } + if len(m.ID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if m.Pid != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + } + if m.Status != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.Status)) + } + if len(m.Stdin) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.Stdin))) + i += copy(dAtA[i:], m.Stdin) + } + if len(m.Stdout) > 0 { + dAtA[i] = 0x32 + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.Stdout))) + i += copy(dAtA[i:], m.Stdout) + } + if len(m.Stderr) > 0 { + dAtA[i] = 0x3a + i++ + i = encodeVarintTask(dAtA, i, uint64(len(m.Stderr))) + i += copy(dAtA[i:], m.Stderr) + } + if m.Terminal { + dAtA[i] = 0x40 + i++ + if m.Terminal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.ExitStatus != 0 { + dAtA[i] = 0x48 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.ExitStatus)) + } + dAtA[i] = 0x52 + i++ + i = encodeVarintTask(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) + n1, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + return i, nil +} + +func (m *ProcessInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProcessInfo) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Pid != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + } + if m.Info != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintTask(dAtA, i, uint64(m.Info.Size())) + n2, err := m.Info.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + } + return i, nil +} + +func encodeVarintTask(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Process) Size() (n int) { + var l int + _ = l + l = len(m.ContainerID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + l = len(m.ID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + if m.Pid != 0 { + n += 1 + sovTask(uint64(m.Pid)) + } + if m.Status != 0 { + n += 1 + sovTask(uint64(m.Status)) + } + l = len(m.Stdin) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + l = len(m.Stdout) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + l = len(m.Stderr) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + if m.Terminal { + n += 2 + } + if m.ExitStatus != 0 { + n += 1 + sovTask(uint64(m.ExitStatus)) + } + l = types.SizeOfStdTime(m.ExitedAt) + n += 1 + l + sovTask(uint64(l)) + return n +} + +func (m *ProcessInfo) Size() (n int) { + var l int + _ = l + if m.Pid != 0 { + n += 1 + sovTask(uint64(m.Pid)) + } + if m.Info != nil { + l = m.Info.Size() + n += 1 + l + sovTask(uint64(l)) + } + return n +} + +func sovTask(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozTask(x uint64) (n int) { + return sovTask(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Process) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Process{`, + `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `Status:` + fmt.Sprintf("%v", this.Status) + `,`, + `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, + `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, + `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, + `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, + `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf1.Timestamp", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *ProcessInfo) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ProcessInfo{`, + `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `Info:` + strings.Replace(fmt.Sprintf("%v", this.Info), "Any", "google_protobuf2.Any", 1) + `,`, + `}`, + }, "") + return s +} +func valueToStringTask(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Process) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Process: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Process: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContainerID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= (Status(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdout = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stderr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Terminal", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Terminal = bool(v != 0) + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitStatus", wireType) + } + m.ExitStatus = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExitStatus |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ProcessInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProcessInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProcessInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Info", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Info == nil { + m.Info = &google_protobuf2.Any{} + } + if err := m.Info.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTask(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTask + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTask(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTask + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTask + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTask + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthTask + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTask + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipTask(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthTask = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTask = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/types/task/task.proto", fileDescriptorTask) +} + +var fileDescriptorTask = []byte{ + // 545 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x3f, 0x6f, 0xd3, 0x40, + 0x18, 0xc6, 0x7d, 0x6e, 0xeb, 0xa6, 0xe7, 0xb6, 0x18, 0x13, 0x55, 0xc6, 0x20, 0xdb, 0xea, 0x64, + 0x31, 0xd8, 0x22, 0xdd, 0xd8, 0xf2, 0x4f, 0xc8, 0x42, 0x72, 0x23, 0x27, 0x11, 0x6c, 0x91, 0x13, + 0x5f, 0xcc, 0xa9, 0xcd, 0x9d, 0x65, 0x9f, 0x81, 0x6c, 0x8c, 0xa8, 0x13, 0x5f, 0xa0, 0x13, 0x7c, + 0x0a, 0x3e, 0x41, 0x46, 0x26, 0xc4, 0x14, 0xa8, 0x3f, 0x09, 0x3a, 0xdb, 0x49, 0x23, 0x60, 0x39, + 0xbd, 0xef, 0xf3, 0x7b, 0xee, 0xbd, 0xf7, 0x1e, 0xf8, 0x22, 0xc6, 0xec, 0x6d, 0x3e, 0x75, 0x66, + 0x74, 0xe1, 0xce, 0x28, 0x61, 0x21, 0x26, 0x28, 0x8d, 0x76, 0xcb, 0x30, 0xc1, 0x2e, 0x5b, 0x26, + 0x28, 0x73, 0x59, 0x98, 0x5d, 0x95, 0x87, 0x93, 0xa4, 0x94, 0x51, 0xf5, 0xd1, 0xbd, 0xcb, 0x79, + 0xf7, 0xdc, 0x29, 0x4d, 0x7a, 0x33, 0xa6, 0x31, 0x2d, 0xb9, 0xcb, 0xab, 0xca, 0xaa, 0x9b, 0x31, + 0xa5, 0xf1, 0x35, 0x72, 0xcb, 0x6e, 0x9a, 0xcf, 0x5d, 0x86, 0x17, 0x28, 0x63, 0xe1, 0x22, 0xa9, + 0x0d, 0x8f, 0xff, 0x36, 0x84, 0x64, 0x59, 0xa1, 0xf3, 0x42, 0x84, 0x87, 0x83, 0x94, 0xce, 0x50, + 0x96, 0xa9, 0x2d, 0x78, 0xbc, 0x7d, 0x74, 0x82, 0x23, 0x0d, 0x58, 0xc0, 0x3e, 0xea, 0x3c, 0x28, + 0xd6, 0xa6, 0xdc, 0xdd, 0xe8, 0x5e, 0x2f, 0x90, 0xb7, 0x26, 0x2f, 0x52, 0xcf, 0xa0, 0x88, 0x23, + 0x4d, 0x2c, 0x9d, 0x52, 0xb1, 0x36, 0x45, 0xaf, 0x17, 0x88, 0x38, 0x52, 0x15, 0xb8, 0x97, 0xe0, + 0x48, 0xdb, 0xb3, 0x80, 0x7d, 0x12, 0xf0, 0x52, 0xbd, 0x80, 0x52, 0xc6, 0x42, 0x96, 0x67, 0xda, + 0xbe, 0x05, 0xec, 0xd3, 0xd6, 0x13, 0xe7, 0x3f, 0x3f, 0x74, 0x86, 0xa5, 0x25, 0xa8, 0xad, 0x6a, + 0x13, 0x1e, 0x64, 0x2c, 0xc2, 0x44, 0x3b, 0xe0, 0x2f, 0x04, 0x55, 0xa3, 0x9e, 0xf1, 0x51, 0x11, + 0xcd, 0x99, 0x26, 0x95, 0x72, 0xdd, 0xd5, 0x3a, 0x4a, 0x53, 0xed, 0x70, 0xab, 0xa3, 0x34, 0x55, + 0x75, 0xd8, 0x60, 0x28, 0x5d, 0x60, 0x12, 0x5e, 0x6b, 0x0d, 0x0b, 0xd8, 0x8d, 0x60, 0xdb, 0xab, + 0x26, 0x94, 0xd1, 0x07, 0xcc, 0x26, 0xf5, 0x6e, 0x47, 0xe5, 0xc2, 0x90, 0x4b, 0xd5, 0x2a, 0x6a, + 0x1b, 0x1e, 0xf1, 0x0e, 0x45, 0x93, 0x90, 0x69, 0xd0, 0x02, 0xb6, 0xdc, 0xd2, 0x9d, 0x2a, 0x50, + 0x67, 0x13, 0xa8, 0x33, 0xda, 0x24, 0xde, 0x69, 0xac, 0xd6, 0xa6, 0xf0, 0xf9, 0x97, 0x09, 0x82, + 0x46, 0x75, 0xad, 0xcd, 0xce, 0x3d, 0x28, 0xd7, 0x19, 0x7b, 0x64, 0x4e, 0x37, 0xd9, 0x80, 0xfb, + 0x6c, 0x6c, 0xb8, 0x8f, 0xc9, 0x9c, 0x96, 0x39, 0xca, 0xad, 0xe6, 0x3f, 0xe3, 0xdb, 0x64, 0x19, + 0x94, 0x8e, 0x67, 0x3f, 0x00, 0x94, 0xea, 0xc5, 0x0c, 0x78, 0x38, 0xf6, 0x5f, 0xf9, 0x97, 0xaf, + 0x7d, 0x45, 0xd0, 0x1f, 0xde, 0xdc, 0x5a, 0x27, 0x15, 0x18, 0x93, 0x2b, 0x42, 0xdf, 0x13, 0xce, + 0xbb, 0x41, 0xbf, 0x3d, 0xea, 0xf7, 0x14, 0xb0, 0xcb, 0xbb, 0x29, 0x0a, 0x19, 0x8a, 0x38, 0x0f, + 0xc6, 0xbe, 0xef, 0xf9, 0x2f, 0x15, 0x71, 0x97, 0x07, 0x39, 0x21, 0x98, 0xc4, 0x9c, 0x0f, 0x47, + 0x97, 0x83, 0x41, 0xbf, 0xa7, 0xec, 0xed, 0xf2, 0x21, 0xa3, 0x49, 0x82, 0x22, 0xf5, 0x29, 0x94, + 0x06, 0xed, 0xf1, 0xb0, 0xdf, 0x53, 0xf6, 0x75, 0xe5, 0xe6, 0xd6, 0x3a, 0xae, 0xf0, 0x20, 0xcc, + 0xb3, 0x6a, 0x3a, 0xa7, 0x7c, 0xfa, 0xc1, 0xee, 0x6d, 0x8e, 0x31, 0x89, 0xf5, 0xd3, 0x4f, 0x5f, + 0x0c, 0xe1, 0xdb, 0x57, 0xa3, 0xfe, 0x4d, 0x47, 0x5b, 0xdd, 0x19, 0xc2, 0xcf, 0x3b, 0x43, 0xf8, + 0x58, 0x18, 0x60, 0x55, 0x18, 0xe0, 0x7b, 0x61, 0x80, 0xdf, 0x85, 0x01, 0xde, 0x08, 0x53, 0xa9, + 0x0c, 0xe2, 0xe2, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc3, 0x32, 0xd2, 0x86, 0x50, 0x03, 0x00, + 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/api/types/task/task.proto b/src/runtime/vendor/github.com/containerd/containerd/api/types/task/task.proto new file mode 100644 index 000000000..da91cb033 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/api/types/task/task.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; + +package containerd.v1.types; + +import weak "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/any.proto"; + +enum Status { + option (gogoproto.goproto_enum_prefix) = false; + option (gogoproto.enum_customname) = "Status"; + + UNKNOWN = 0 [(gogoproto.enumvalue_customname) = "StatusUnknown"]; + CREATED = 1 [(gogoproto.enumvalue_customname) = "StatusCreated"]; + RUNNING = 2 [(gogoproto.enumvalue_customname) = "StatusRunning"]; + STOPPED = 3 [(gogoproto.enumvalue_customname) = "StatusStopped"]; + PAUSED = 4 [(gogoproto.enumvalue_customname) = "StatusPaused"]; + PAUSING = 5 [(gogoproto.enumvalue_customname) = "StatusPausing"]; +} + +message Process { + string container_id = 1; + string id = 2; + uint32 pid = 3; + Status status = 4; + string stdin = 5; + string stdout = 6; + string stderr = 7; + bool terminal = 8; + uint32 exit_status = 9; + google.protobuf.Timestamp exited_at = 10 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; +} + +message ProcessInfo { + // PID is the process ID. + uint32 pid = 1; + // Info contains additional process information. + // + // Info varies by platform. + google.protobuf.Any info = 2; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/errdefs/errors.go b/src/runtime/vendor/github.com/containerd/containerd/errdefs/errors.go new file mode 100644 index 000000000..40427fc5a --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/errdefs/errors.go @@ -0,0 +1,78 @@ +/* + Copyright The containerd Authors. + + 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 errdefs defines the common errors used throughout containerd +// packages. +// +// Use with errors.Wrap and error.Wrapf to add context to an error. +// +// To detect an error class, use the IsXXX functions to tell whether an error +// is of a certain type. +// +// The functions ToGRPC and FromGRPC can be used to map server-side and +// client-side errors to the correct types. +package errdefs + +import "github.com/pkg/errors" + +// Definitions of common error types used throughout containerd. All containerd +// errors returned by most packages will map into one of these errors classes. +// Packages should return errors of these types when they want to instruct a +// client to take a particular action. +// +// For the most part, we just try to provide local grpc errors. Most conditions +// map very well to those defined by grpc. +var ( + ErrUnknown = errors.New("unknown") // used internally to represent a missed mapping. + ErrInvalidArgument = errors.New("invalid argument") + ErrNotFound = errors.New("not found") + ErrAlreadyExists = errors.New("already exists") + ErrFailedPrecondition = errors.New("failed precondition") + ErrUnavailable = errors.New("unavailable") + ErrNotImplemented = errors.New("not implemented") // represents not supported and unimplemented +) + +// IsInvalidArgument returns true if the error is due to an invalid argument +func IsInvalidArgument(err error) bool { + return errors.Cause(err) == ErrInvalidArgument +} + +// IsNotFound returns true if the error is due to a missing object +func IsNotFound(err error) bool { + return errors.Cause(err) == ErrNotFound +} + +// IsAlreadyExists returns true if the error is due to an already existing +// metadata item +func IsAlreadyExists(err error) bool { + return errors.Cause(err) == ErrAlreadyExists +} + +// IsFailedPrecondition returns true if an operation could not proceed to the +// lack of a particular condition +func IsFailedPrecondition(err error) bool { + return errors.Cause(err) == ErrFailedPrecondition +} + +// IsUnavailable returns true if the error is due to a resource being unavailable +func IsUnavailable(err error) bool { + return errors.Cause(err) == ErrUnavailable +} + +// IsNotImplemented returns true if the error is due to not being implemented +func IsNotImplemented(err error) bool { + return errors.Cause(err) == ErrNotImplemented +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/errdefs/grpc.go b/src/runtime/vendor/github.com/containerd/containerd/errdefs/grpc.go new file mode 100644 index 000000000..b1542f13d --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/errdefs/grpc.go @@ -0,0 +1,138 @@ +/* + Copyright The containerd Authors. + + 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 errdefs + +import ( + "strings" + + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// ToGRPC will attempt to map the backend containerd error into a grpc error, +// using the original error message as a description. +// +// Further information may be extracted from certain errors depending on their +// type. +// +// If the error is unmapped, the original error will be returned to be handled +// by the regular grpc error handling stack. +func ToGRPC(err error) error { + if err == nil { + return nil + } + + if isGRPCError(err) { + // error has already been mapped to grpc + return err + } + + switch { + case IsInvalidArgument(err): + return status.Errorf(codes.InvalidArgument, err.Error()) + case IsNotFound(err): + return status.Errorf(codes.NotFound, err.Error()) + case IsAlreadyExists(err): + return status.Errorf(codes.AlreadyExists, err.Error()) + case IsFailedPrecondition(err): + return status.Errorf(codes.FailedPrecondition, err.Error()) + case IsUnavailable(err): + return status.Errorf(codes.Unavailable, err.Error()) + case IsNotImplemented(err): + return status.Errorf(codes.Unimplemented, err.Error()) + } + + return err +} + +// ToGRPCf maps the error to grpc error codes, assembling the formatting string +// and combining it with the target error string. +// +// This is equivalent to errors.ToGRPC(errors.Wrapf(err, format, args...)) +func ToGRPCf(err error, format string, args ...interface{}) error { + return ToGRPC(errors.Wrapf(err, format, args...)) +} + +// FromGRPC returns the underlying error from a grpc service based on the grpc error code +func FromGRPC(err error) error { + if err == nil { + return nil + } + + var cls error // divide these into error classes, becomes the cause + + switch code(err) { + case codes.InvalidArgument: + cls = ErrInvalidArgument + case codes.AlreadyExists: + cls = ErrAlreadyExists + case codes.NotFound: + cls = ErrNotFound + case codes.Unavailable: + cls = ErrUnavailable + case codes.FailedPrecondition: + cls = ErrFailedPrecondition + case codes.Unimplemented: + cls = ErrNotImplemented + default: + cls = ErrUnknown + } + + msg := rebaseMessage(cls, err) + if msg != "" { + err = errors.Wrap(cls, msg) + } else { + err = errors.WithStack(cls) + } + + return err +} + +// rebaseMessage removes the repeats for an error at the end of an error +// string. This will happen when taking an error over grpc then remapping it. +// +// Effectively, we just remove the string of cls from the end of err if it +// appears there. +func rebaseMessage(cls error, err error) string { + desc := errDesc(err) + clss := cls.Error() + if desc == clss { + return "" + } + + return strings.TrimSuffix(desc, ": "+clss) +} + +func isGRPCError(err error) bool { + _, ok := status.FromError(err) + return ok +} + +func code(err error) codes.Code { + if s, ok := status.FromError(err); ok { + return s.Code() + } + return codes.Unknown +} + +func errDesc(err error) string { + if s, ok := status.FromError(err); ok { + return s.Message() + } + return err.Error() +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/events/events.go b/src/runtime/vendor/github.com/containerd/containerd/events/events.go new file mode 100644 index 000000000..b7eb86f1e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/events/events.go @@ -0,0 +1,81 @@ +/* + Copyright The containerd Authors. + + 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 events + +import ( + "context" + "time" + + "github.com/containerd/typeurl" + "github.com/gogo/protobuf/types" +) + +// Envelope provides the packaging for an event. +type Envelope struct { + Timestamp time.Time + Namespace string + Topic string + Event *types.Any +} + +// Field returns the value for the given fieldpath as a string, if defined. +// If the value is not defined, the second value will be false. +func (e *Envelope) Field(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + // unhandled: timestamp + case "namespace": + return e.Namespace, len(e.Namespace) > 0 + case "topic": + return e.Topic, len(e.Topic) > 0 + case "event": + decoded, err := typeurl.UnmarshalAny(e.Event) + if err != nil { + return "", false + } + + adaptor, ok := decoded.(interface { + Field([]string) (string, bool) + }) + if !ok { + return "", false + } + return adaptor.Field(fieldpath[1:]) + } + return "", false +} + +// Event is a generic interface for any type of event +type Event interface{} + +// Publisher posts the event. +type Publisher interface { + Publish(ctx context.Context, topic string, event Event) error +} + +// Forwarder forwards an event to the underlying event bus +type Forwarder interface { + Forward(ctx context.Context, envelope *Envelope) error +} + +// Subscriber allows callers to subscribe to events +type Subscriber interface { + Subscribe(ctx context.Context, filters ...string) (ch <-chan *Envelope, errs <-chan error) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/log/context.go b/src/runtime/vendor/github.com/containerd/containerd/log/context.go new file mode 100644 index 000000000..3fab96b85 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/log/context.go @@ -0,0 +1,90 @@ +/* + Copyright The containerd Authors. + + 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 log + +import ( + "context" + "sync/atomic" + + "github.com/sirupsen/logrus" +) + +var ( + // G is an alias for GetLogger. + // + // We may want to define this locally to a package to get package tagged log + // messages. + G = GetLogger + + // L is an alias for the the standard logger. + L = logrus.NewEntry(logrus.StandardLogger()) +) + +type ( + loggerKey struct{} +) + +// TraceLevel is the log level for tracing. Trace level is lower than debug level, +// and is usually used to trace detailed behavior of the program. +const TraceLevel = logrus.Level(uint32(logrus.DebugLevel + 1)) + +// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to +// ensure the formatted time is always the same number of characters. +const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" + +// ParseLevel takes a string level and returns the Logrus log level constant. +// It supports trace level. +func ParseLevel(lvl string) (logrus.Level, error) { + if lvl == "trace" { + return TraceLevel, nil + } + return logrus.ParseLevel(lvl) +} + +// WithLogger returns a new context with the provided logger. Use in +// combination with logger.WithField(s) for great effect. +func WithLogger(ctx context.Context, logger *logrus.Entry) context.Context { + return context.WithValue(ctx, loggerKey{}, logger) +} + +// GetLogger retrieves the current logger from the context. If no logger is +// available, the default logger is returned. +func GetLogger(ctx context.Context) *logrus.Entry { + logger := ctx.Value(loggerKey{}) + + if logger == nil { + return L + } + + return logger.(*logrus.Entry) +} + +// Trace logs a message at level Trace with the log entry passed-in. +func Trace(e *logrus.Entry, args ...interface{}) { + level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level))) + if level >= TraceLevel { + e.Debug(args...) + } +} + +// Tracef logs a message at level Trace with the log entry passed-in. +func Tracef(e *logrus.Entry, format string, args ...interface{}) { + level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level))) + if level >= TraceLevel { + e.Debugf(format, args...) + } +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/lookup_unix.go b/src/runtime/vendor/github.com/containerd/containerd/mount/lookup_unix.go new file mode 100644 index 000000000..e8b0a0b48 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/lookup_unix.go @@ -0,0 +1,53 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 mount + +import ( + "path/filepath" + "sort" + "strings" + + "github.com/pkg/errors" +) + +// Lookup returns the mount info corresponds to the path. +func Lookup(dir string) (Info, error) { + dir = filepath.Clean(dir) + + mounts, err := Self() + if err != nil { + return Info{}, err + } + + // Sort descending order by Info.Mountpoint + sort.SliceStable(mounts, func(i, j int) bool { + return mounts[j].Mountpoint < mounts[i].Mountpoint + }) + for _, m := range mounts { + // Note that m.{Major, Minor} are generally unreliable for our purpose here + // https://www.spinics.net/lists/linux-btrfs/msg58908.html + // Note that device number is not checked here, because for overlayfs files + // may have different device number with the mountpoint. + if strings.HasPrefix(dir, m.Mountpoint) { + return m, nil + } + } + + return Info{}, errors.Errorf("failed to find the mount info for %q", dir) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/lookup_unsupported.go b/src/runtime/vendor/github.com/containerd/containerd/mount/lookup_unsupported.go new file mode 100644 index 000000000..46ec66a90 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/lookup_unsupported.go @@ -0,0 +1,29 @@ +// +build windows + +/* + Copyright The containerd Authors. + + 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 mount + +import ( + "fmt" + "runtime" +) + +// Lookup returns the mount info corresponds to the path. +func Lookup(dir string) (Info, error) { + return Info{}, fmt.Errorf("mount.Lookup is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/mount.go b/src/runtime/vendor/github.com/containerd/containerd/mount/mount.go new file mode 100644 index 000000000..b25556b2e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/mount.go @@ -0,0 +1,40 @@ +/* + Copyright The containerd Authors. + + 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 mount + +// Mount is the lingua franca of containerd. A mount represents a +// serialized mount syscall. Components either emit or consume mounts. +type Mount struct { + // Type specifies the host-specific of the mount. + Type string + // Source specifies where to mount from. Depending on the host system, this + // can be a source path or device. + Source string + // Options contains zero or more fstab-style mount options. Typically, + // these are platform specific. + Options []string +} + +// All mounts all the provided mounts to the provided target +func All(mounts []Mount, target string) error { + for _, m := range mounts { + if err := m.Mount(target); err != nil { + return err + } + } + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/mount_linux.go b/src/runtime/vendor/github.com/containerd/containerd/mount/mount_linux.go new file mode 100644 index 000000000..b5a16148a --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/mount_linux.go @@ -0,0 +1,308 @@ +/* + Copyright The containerd Authors. + + 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 mount + +import ( + "fmt" + "os" + "path" + "strings" + "time" + + "github.com/containerd/containerd/sys" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +var pagesize = 4096 + +func init() { + pagesize = os.Getpagesize() +} + +// Mount to the provided target path +func (m *Mount) Mount(target string) error { + var ( + chdir string + options = m.Options + ) + + // avoid hitting one page limit of mount argument buffer + // + // NOTE: 512 is a buffer during pagesize check. + if m.Type == "overlay" && optionsSize(options) >= pagesize-512 { + chdir, options = compactLowerdirOption(options) + } + + flags, data := parseMountOptions(options) + if len(data) > pagesize { + return errors.Errorf("mount options is too long") + } + + // propagation types. + const ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE + + // Ensure propagation type change flags aren't included in other calls. + oflags := flags &^ ptypes + + // In the case of remounting with changed data (data != ""), need to call mount (moby/moby#34077). + if flags&unix.MS_REMOUNT == 0 || data != "" { + // Initial call applying all non-propagation flags for mount + // or remount with changed data + if err := mountAt(chdir, m.Source, target, m.Type, uintptr(oflags), data); err != nil { + return err + } + } + + if flags&ptypes != 0 { + // Change the propagation type. + const pflags = ptypes | unix.MS_REC | unix.MS_SILENT + if err := unix.Mount("", target, "", uintptr(flags&pflags), ""); err != nil { + return err + } + } + + const broflags = unix.MS_BIND | unix.MS_RDONLY + if oflags&broflags == broflags { + // Remount the bind to apply read only. + return unix.Mount("", target, "", uintptr(oflags|unix.MS_REMOUNT), "") + } + return nil +} + +// Unmount the provided mount path with the flags +func Unmount(target string, flags int) error { + if err := unmount(target, flags); err != nil && err != unix.EINVAL { + return err + } + return nil +} + +func unmount(target string, flags int) error { + for i := 0; i < 50; i++ { + if err := unix.Unmount(target, flags); err != nil { + switch err { + case unix.EBUSY: + time.Sleep(50 * time.Millisecond) + continue + default: + return err + } + } + return nil + } + return errors.Wrapf(unix.EBUSY, "failed to unmount target %s", target) +} + +// UnmountAll repeatedly unmounts the given mount point until there +// are no mounts remaining (EINVAL is returned by mount), which is +// useful for undoing a stack of mounts on the same mount point. +func UnmountAll(mount string, flags int) error { + for { + if err := unmount(mount, flags); err != nil { + // EINVAL is returned if the target is not a + // mount point, indicating that we are + // done. It can also indicate a few other + // things (such as invalid flags) which we + // unfortunately end up squelching here too. + if err == unix.EINVAL { + return nil + } + return err + } + } +} + +// parseMountOptions takes fstab style mount options and parses them for +// use with a standard mount() syscall +func parseMountOptions(options []string) (int, string) { + var ( + flag int + data []string + ) + flags := map[string]struct { + clear bool + flag int + }{ + "async": {true, unix.MS_SYNCHRONOUS}, + "atime": {true, unix.MS_NOATIME}, + "bind": {false, unix.MS_BIND}, + "defaults": {false, 0}, + "dev": {true, unix.MS_NODEV}, + "diratime": {true, unix.MS_NODIRATIME}, + "dirsync": {false, unix.MS_DIRSYNC}, + "exec": {true, unix.MS_NOEXEC}, + "mand": {false, unix.MS_MANDLOCK}, + "noatime": {false, unix.MS_NOATIME}, + "nodev": {false, unix.MS_NODEV}, + "nodiratime": {false, unix.MS_NODIRATIME}, + "noexec": {false, unix.MS_NOEXEC}, + "nomand": {true, unix.MS_MANDLOCK}, + "norelatime": {true, unix.MS_RELATIME}, + "nostrictatime": {true, unix.MS_STRICTATIME}, + "nosuid": {false, unix.MS_NOSUID}, + "rbind": {false, unix.MS_BIND | unix.MS_REC}, + "relatime": {false, unix.MS_RELATIME}, + "remount": {false, unix.MS_REMOUNT}, + "ro": {false, unix.MS_RDONLY}, + "rw": {true, unix.MS_RDONLY}, + "strictatime": {false, unix.MS_STRICTATIME}, + "suid": {true, unix.MS_NOSUID}, + "sync": {false, unix.MS_SYNCHRONOUS}, + } + for _, o := range options { + // If the option does not exist in the flags table or the flag + // is not supported on the platform, + // then it is a data value for a specific fs type + if f, exists := flags[o]; exists && f.flag != 0 { + if f.clear { + flag &^= f.flag + } else { + flag |= f.flag + } + } else { + data = append(data, o) + } + } + return flag, strings.Join(data, ",") +} + +// compactLowerdirOption updates overlay lowdir option and returns the common +// dir among all the lowdirs. +func compactLowerdirOption(opts []string) (string, []string) { + idx, dirs := findOverlayLowerdirs(opts) + if idx == -1 || len(dirs) == 1 { + // no need to compact if there is only one lowerdir + return "", opts + } + + // find out common dir + commondir := longestCommonPrefix(dirs) + if commondir == "" { + return "", opts + } + + // NOTE: the snapshot id is based on digits. + // in order to avoid to get snapshots/x, should be back to parent dir. + // however, there is assumption that the common dir is ${root}/io.containerd.v1.overlayfs/snapshots. + commondir = path.Dir(commondir) + if commondir == "/" { + return "", opts + } + commondir = commondir + "/" + + newdirs := make([]string, 0, len(dirs)) + for _, dir := range dirs { + newdirs = append(newdirs, dir[len(commondir):]) + } + + newopts := copyOptions(opts) + newopts = append(newopts[:idx], newopts[idx+1:]...) + newopts = append(newopts, fmt.Sprintf("lowerdir=%s", strings.Join(newdirs, ":"))) + return commondir, newopts +} + +// findOverlayLowerdirs returns the index of lowerdir in mount's options and +// all the lowerdir target. +func findOverlayLowerdirs(opts []string) (int, []string) { + var ( + idx = -1 + prefix = "lowerdir=" + ) + + for i, opt := range opts { + if strings.HasPrefix(opt, prefix) { + idx = i + break + } + } + + if idx == -1 { + return -1, nil + } + return idx, strings.Split(opts[idx][len(prefix):], ":") +} + +// longestCommonPrefix finds the longest common prefix in the string slice. +func longestCommonPrefix(strs []string) string { + if len(strs) == 0 { + return "" + } else if len(strs) == 1 { + return strs[0] + } + + // find out the min/max value by alphabetical order + min, max := strs[0], strs[0] + for _, str := range strs[1:] { + if min > str { + min = str + } + if max < str { + max = str + } + } + + // find out the common part between min and max + for i := 0; i < len(min) && i < len(max); i++ { + if min[i] != max[i] { + return min[:i] + } + } + return min +} + +// copyOptions copies the options. +func copyOptions(opts []string) []string { + if len(opts) == 0 { + return nil + } + + acopy := make([]string, len(opts)) + copy(acopy, opts) + return acopy +} + +// optionsSize returns the byte size of options of mount. +func optionsSize(opts []string) int { + size := 0 + for _, opt := range opts { + size += len(opt) + } + return size +} + +func mountAt(chdir string, source, target, fstype string, flags uintptr, data string) error { + if chdir == "" { + return unix.Mount(source, target, fstype, flags, data) + } + + f, err := os.Open(chdir) + if err != nil { + return errors.Wrap(err, "failed to mountat") + } + defer f.Close() + + fs, err := f.Stat() + if err != nil { + return errors.Wrap(err, "failed to mountat") + } + + if !fs.IsDir() { + return errors.Wrap(errors.Errorf("%s is not dir", chdir), "failed to mountat") + } + return errors.Wrap(sys.FMountat(f.Fd(), source, target, fstype, flags, data), "failed to mountat") +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/mount_unix.go b/src/runtime/vendor/github.com/containerd/containerd/mount/mount_unix.go new file mode 100644 index 000000000..95da9428e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/mount_unix.go @@ -0,0 +1,41 @@ +// +build darwin freebsd openbsd + +/* + Copyright The containerd Authors. + + 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 mount + +import "github.com/pkg/errors" + +var ( + // ErrNotImplementOnUnix is returned for methods that are not implemented + ErrNotImplementOnUnix = errors.New("not implemented under unix") +) + +// Mount is not implemented on this platform +func (m *Mount) Mount(target string) error { + return ErrNotImplementOnUnix +} + +// Unmount is not implemented on this platform +func Unmount(mount string, flags int) error { + return ErrNotImplementOnUnix +} + +// UnmountAll is not implemented on this platform +func UnmountAll(mount string, flags int) error { + return ErrNotImplementOnUnix +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/mount_windows.go b/src/runtime/vendor/github.com/containerd/containerd/mount/mount_windows.go new file mode 100644 index 000000000..5de25c4e0 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/mount_windows.go @@ -0,0 +1,105 @@ +/* + Copyright The containerd Authors. + + 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 mount + +import ( + "encoding/json" + "path/filepath" + "strings" + + "github.com/Microsoft/hcsshim" + "github.com/pkg/errors" +) + +var ( + // ErrNotImplementOnWindows is returned when an action is not implemented for windows + ErrNotImplementOnWindows = errors.New("not implemented under windows") +) + +// Mount to the provided target +func (m *Mount) Mount(target string) error { + if m.Type != "windows-layer" { + return errors.Errorf("invalid windows mount type: '%s'", m.Type) + } + + home, layerID := filepath.Split(m.Source) + + parentLayerPaths, err := m.GetParentPaths() + if err != nil { + return err + } + + var di = hcsshim.DriverInfo{ + HomeDir: home, + } + + if err = hcsshim.ActivateLayer(di, layerID); err != nil { + return errors.Wrapf(err, "failed to activate layer %s", m.Source) + } + defer func() { + if err != nil { + hcsshim.DeactivateLayer(di, layerID) + } + }() + + if err = hcsshim.PrepareLayer(di, layerID, parentLayerPaths); err != nil { + return errors.Wrapf(err, "failed to prepare layer %s", m.Source) + } + return nil +} + +// ParentLayerPathsFlag is the options flag used to represent the JSON encoded +// list of parent layers required to use the layer +const ParentLayerPathsFlag = "parentLayerPaths=" + +// GetParentPaths of the mount +func (m *Mount) GetParentPaths() ([]string, error) { + var parentLayerPaths []string + for _, option := range m.Options { + if strings.HasPrefix(option, ParentLayerPathsFlag) { + err := json.Unmarshal([]byte(option[len(ParentLayerPathsFlag):]), &parentLayerPaths) + if err != nil { + return nil, errors.Wrap(err, "failed to unmarshal parent layer paths from mount") + } + } + } + return parentLayerPaths, nil +} + +// Unmount the mount at the provided path +func Unmount(mount string, flags int) error { + var ( + home, layerID = filepath.Split(mount) + di = hcsshim.DriverInfo{ + HomeDir: home, + } + ) + + if err := hcsshim.UnprepareLayer(di, layerID); err != nil { + return errors.Wrapf(err, "failed to unprepare layer %s", mount) + } + if err := hcsshim.DeactivateLayer(di, layerID); err != nil { + return errors.Wrapf(err, "failed to deactivate layer %s", mount) + } + + return nil +} + +// UnmountAll unmounts from the provided path +func UnmountAll(mount string, flags int) error { + return Unmount(mount, flags) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo.go b/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo.go new file mode 100644 index 000000000..e7a68402f --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo.go @@ -0,0 +1,56 @@ +/* + Copyright The containerd Authors. + + 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 mount + +// Info reveals information about a particular mounted filesystem. This +// struct is populated from the content in the /proc//mountinfo file. +type Info struct { + // ID is a unique identifier of the mount (may be reused after umount). + ID int + + // Parent indicates the ID of the mount parent (or of self for the top of the + // mount tree). + Parent int + + // Major indicates one half of the device ID which identifies the device class. + Major int + + // Minor indicates one half of the device ID which identifies a specific + // instance of device. + Minor int + + // Root of the mount within the filesystem. + Root string + + // Mountpoint indicates the mount point relative to the process's root. + Mountpoint string + + // Options represents mount-specific options. + Options string + + // Optional represents optional fields. + Optional string + + // FSType indicates the type of filesystem, such as EXT3. + FSType string + + // Source indicates filesystem specific information or "none". + Source string + + // VFSOptions represents per super block options. + VFSOptions string +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo_bsd.go b/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo_bsd.go new file mode 100644 index 000000000..8f8dbf95a --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo_bsd.go @@ -0,0 +1,63 @@ +// +build freebsd openbsd + +/* + Copyright The containerd Authors. + + 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 mount + +/* +#include +#include +#include +*/ +import "C" + +import ( + "fmt" + "reflect" + "unsafe" +) + +// Self retrieves a list of mounts for the current running process. +func Self() ([]Info, error) { + var rawEntries *C.struct_statfs + + count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT)) + if count == 0 { + return nil, fmt.Errorf("Failed to call getmntinfo") + } + + var entries []C.struct_statfs + header := (*reflect.SliceHeader)(unsafe.Pointer(&entries)) + header.Cap = count + header.Len = count + header.Data = uintptr(unsafe.Pointer(rawEntries)) + + var out []Info + for _, entry := range entries { + var mountinfo Info + mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0]) + mountinfo.Source = C.GoString(&entry.f_mntfromname[0]) + mountinfo.FSType = C.GoString(&entry.f_fstypename[0]) + out = append(out, mountinfo) + } + return out, nil +} + +// PID collects the mounts for a specific process ID. +func PID(pid int) ([]Info, error) { + return nil, fmt.Errorf("mountinfo.PID is not implemented on freebsd") +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo_linux.go b/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo_linux.go new file mode 100644 index 000000000..369d045d7 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo_linux.go @@ -0,0 +1,135 @@ +// +build linux + +/* + Copyright The containerd Authors. + + 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 mount + +import ( + "bufio" + "fmt" + "io" + "os" + "strconv" + "strings" +) + +// Self retrieves a list of mounts for the current running process. +func Self() ([]Info, error) { + f, err := os.Open("/proc/self/mountinfo") + if err != nil { + return nil, err + } + defer f.Close() + + return parseInfoFile(f) +} + +func parseInfoFile(r io.Reader) ([]Info, error) { + s := bufio.NewScanner(r) + out := []Info{} + + for s.Scan() { + if err := s.Err(); err != nil { + return nil, err + } + + /* + 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue + (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) + (1) mount ID: unique identifier of the mount (may be reused after umount) + (2) parent ID: ID of parent (or of self for the top of the mount tree) + (3) major:minor: value of st_dev for files on filesystem + (4) root: root of the mount within the filesystem + (5) mount point: mount point relative to the process's root + (6) mount options: per mount options + (7) optional fields: zero or more fields of the form "tag[:value]" + (8) separator: marks the end of the optional fields + (9) filesystem type: name of filesystem of the form "type[.subtype]" + (10) mount source: filesystem specific information or "none" + (11) super options: per super block options + */ + + text := s.Text() + fields := strings.Split(text, " ") + numFields := len(fields) + if numFields < 10 { + // should be at least 10 fields + return nil, fmt.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields) + } + p := Info{} + // ignore any numbers parsing errors, as there should not be any + p.ID, _ = strconv.Atoi(fields[0]) + p.Parent, _ = strconv.Atoi(fields[1]) + mm := strings.Split(fields[2], ":") + if len(mm) != 2 { + return nil, fmt.Errorf("parsing '%s' failed: unexpected minor:major pair %s", text, mm) + } + p.Major, _ = strconv.Atoi(mm[0]) + p.Minor, _ = strconv.Atoi(mm[1]) + + p.Root = fields[3] + p.Mountpoint = fields[4] + p.Options = fields[5] + + // one or more optional fields, when a separator (-) + i := 6 + for ; i < numFields && fields[i] != "-"; i++ { + switch i { + case 6: + p.Optional = fields[6] + default: + /* NOTE there might be more optional fields before the separator + such as fields[7]...fields[N] (where N < separatorIndex), + although as of Linux kernel 4.15 the only known ones are + mount propagation flags in fields[6]. The correct + behavior is to ignore any unknown optional fields. + */ + } + } + if i == numFields { + return nil, fmt.Errorf("parsing '%s' failed: missing separator ('-')", text) + } + // There should be 3 fields after the separator... + if i+4 > numFields { + return nil, fmt.Errorf("parsing '%s' failed: not enough fields after a separator", text) + } + // ... but in Linux <= 3.9 mounting a cifs with spaces in a share name + // (like "//serv/My Documents") _may_ end up having a space in the last field + // of mountinfo (like "unc=//serv/My Documents"). Since kernel 3.10-rc1, cifs + // option unc= is ignored, so a space should not appear. In here we ignore + // those "extra" fields caused by extra spaces. + p.FSType = fields[i+1] + p.Source = fields[i+2] + p.VFSOptions = fields[i+3] + + out = append(out, p) + } + return out, nil +} + +// PID collects the mounts for a specific process ID. If the process +// ID is unknown, it is better to use `Self` which will inspect +// "/proc/self/mountinfo" instead. +func PID(pid int) ([]Info, error) { + f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) + if err != nil { + return nil, err + } + defer f.Close() + + return parseInfoFile(f) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go b/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go new file mode 100644 index 000000000..ae998db6b --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go @@ -0,0 +1,34 @@ +// +build !linux,!freebsd,!solaris,!openbsd freebsd,!cgo solaris,!cgo openbsd,!cgo + +/* + Copyright The containerd Authors. + + 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 mount + +import ( + "fmt" + "runtime" +) + +// Self retrieves a list of mounts for the current running process. +func Self() ([]Info, error) { + return nil, fmt.Errorf("mountinfo.Self is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} + +// PID collects the mounts for a specific process ID. +func PID(pid int) ([]Info, error) { + return nil, fmt.Errorf("mountinfo.PID is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/temp.go b/src/runtime/vendor/github.com/containerd/containerd/mount/temp.go new file mode 100644 index 000000000..9dc4010fe --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/temp.go @@ -0,0 +1,73 @@ +/* + Copyright The containerd Authors. + + 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 mount + +import ( + "context" + "io/ioutil" + "os" + + "github.com/containerd/containerd/log" + "github.com/pkg/errors" +) + +var tempMountLocation = getTempDir() + +// WithTempMount mounts the provided mounts to a temp dir, and pass the temp dir to f. +// The mounts are valid during the call to the f. +// Finally we will unmount and remove the temp dir regardless of the result of f. +func WithTempMount(ctx context.Context, mounts []Mount, f func(root string) error) (err error) { + root, uerr := ioutil.TempDir(tempMountLocation, "containerd-mount") + if uerr != nil { + return errors.Wrapf(uerr, "failed to create temp dir") + } + // We use Remove here instead of RemoveAll. + // The RemoveAll will delete the temp dir and all children it contains. + // When the Unmount fails, RemoveAll will incorrectly delete data from + // the mounted dir. However, if we use Remove, even though we won't + // successfully delete the temp dir and it may leak, we won't loss data + // from the mounted dir. + // For details, please refer to #1868 #1785. + defer func() { + if uerr = os.Remove(root); uerr != nil { + log.G(ctx).WithError(uerr).WithField("dir", root).Errorf("failed to remove mount temp dir") + } + }() + + // We should do defer first, if not we will not do Unmount when only a part of Mounts are failed. + defer func() { + if uerr = UnmountAll(root, 0); uerr != nil { + uerr = errors.Wrapf(uerr, "failed to unmount %s", root) + if err == nil { + err = uerr + } else { + err = errors.Wrap(err, uerr.Error()) + } + } + }() + if uerr = All(mounts, root); uerr != nil { + return errors.Wrapf(uerr, "failed to mount %s", root) + } + return errors.Wrapf(f(root), "mount callback failed on %s", root) +} + +func getTempDir() string { + if xdg := os.Getenv("XDG_RUNTIME_DIR"); xdg != "" { + return xdg + } + return os.TempDir() +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/temp_unix.go b/src/runtime/vendor/github.com/containerd/containerd/mount/temp_unix.go new file mode 100644 index 000000000..3d490e8a7 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/temp_unix.go @@ -0,0 +1,64 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 mount + +import ( + "os" + "path/filepath" + "sort" + "strings" +) + +// SetTempMountLocation sets the temporary mount location +func SetTempMountLocation(root string) error { + root, err := filepath.Abs(root) + if err != nil { + return err + } + if err := os.MkdirAll(root, 0700); err != nil { + return err + } + tempMountLocation = root + return nil +} + +// CleanupTempMounts all temp mounts and remove the directories +func CleanupTempMounts(flags int) (warnings []error, err error) { + mounts, err := Self() + if err != nil { + return nil, err + } + var toUnmount []string + for _, m := range mounts { + if strings.HasPrefix(m.Mountpoint, tempMountLocation) { + toUnmount = append(toUnmount, m.Mountpoint) + } + } + sort.Sort(sort.Reverse(sort.StringSlice(toUnmount))) + for _, path := range toUnmount { + if err := UnmountAll(path, flags); err != nil { + warnings = append(warnings, err) + continue + } + if err := os.Remove(path); err != nil { + warnings = append(warnings, err) + } + } + return warnings, nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/mount/temp_unsupported.go b/src/runtime/vendor/github.com/containerd/containerd/mount/temp_unsupported.go new file mode 100644 index 000000000..942be4128 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/mount/temp_unsupported.go @@ -0,0 +1,29 @@ +// +build windows + +/* + Copyright The containerd Authors. + + 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 mount + +// SetTempMountLocation sets the temporary mount location +func SetTempMountLocation(root string) error { + return nil +} + +// CleanupTempMounts all temp mounts and remove the directories +func CleanupTempMounts(flags int) ([]error, error) { + return nil, nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/namespaces/context.go b/src/runtime/vendor/github.com/containerd/containerd/namespaces/context.go new file mode 100644 index 000000000..cc5621a68 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/namespaces/context.go @@ -0,0 +1,79 @@ +/* + Copyright The containerd Authors. + + 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 namespaces + +import ( + "context" + "os" + + "github.com/containerd/containerd/errdefs" + "github.com/pkg/errors" +) + +const ( + // NamespaceEnvVar is the environment variable key name + NamespaceEnvVar = "CONTAINERD_NAMESPACE" + // Default is the name of the default namespace + Default = "default" +) + +type namespaceKey struct{} + +// WithNamespace sets a given namespace on the context +func WithNamespace(ctx context.Context, namespace string) context.Context { + ctx = context.WithValue(ctx, namespaceKey{}, namespace) // set our key for namespace + + // also store on the grpc headers so it gets picked up by any clients that + // are using this. + return withGRPCNamespaceHeader(ctx, namespace) +} + +// NamespaceFromEnv uses the namespace defined in CONTAINERD_NAMESPACE or +// default +func NamespaceFromEnv(ctx context.Context) context.Context { + namespace := os.Getenv(NamespaceEnvVar) + if namespace == "" { + namespace = Default + } + return WithNamespace(ctx, namespace) +} + +// Namespace returns the namespace from the context. +// +// The namespace is not guaranteed to be valid. +func Namespace(ctx context.Context) (string, bool) { + namespace, ok := ctx.Value(namespaceKey{}).(string) + if !ok { + return fromGRPCHeader(ctx) + } + + return namespace, ok +} + +// NamespaceRequired returns the valid namepace from the context or an error. +func NamespaceRequired(ctx context.Context) (string, error) { + namespace, ok := Namespace(ctx) + if !ok || namespace == "" { + return "", errors.Wrapf(errdefs.ErrFailedPrecondition, "namespace is required") + } + + if err := Validate(namespace); err != nil { + return "", errors.Wrap(err, "namespace validation") + } + + return namespace, nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/namespaces/grpc.go b/src/runtime/vendor/github.com/containerd/containerd/namespaces/grpc.go new file mode 100644 index 000000000..6991460da --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/namespaces/grpc.go @@ -0,0 +1,61 @@ +/* + Copyright The containerd Authors. + + 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 namespaces + +import ( + "context" + + "google.golang.org/grpc/metadata" +) + +const ( + // GRPCHeader defines the header name for specifying a containerd namespace. + GRPCHeader = "containerd-namespace" +) + +// NOTE(stevvooe): We can stub this file out if we don't want a grpc dependency here. + +func withGRPCNamespaceHeader(ctx context.Context, namespace string) context.Context { + // also store on the grpc headers so it gets picked up by any clients that + // are using this. + nsheader := metadata.Pairs(GRPCHeader, namespace) + md, ok := metadata.FromOutgoingContext(ctx) // merge with outgoing context. + if !ok { + md = nsheader + } else { + // order ensures the latest is first in this list. + md = metadata.Join(nsheader, md) + } + + return metadata.NewOutgoingContext(ctx, md) +} + +func fromGRPCHeader(ctx context.Context) (string, bool) { + // try to extract for use in grpc servers. + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + // TODO(stevvooe): Check outgoing context? + return "", false + } + + values := md[GRPCHeader] + if len(values) == 0 { + return "", false + } + + return values[0], true +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/namespaces/store.go b/src/runtime/vendor/github.com/containerd/containerd/namespaces/store.go new file mode 100644 index 000000000..0b5c98569 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/namespaces/store.go @@ -0,0 +1,37 @@ +/* + Copyright The containerd Authors. + + 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 namespaces + +import "context" + +// Store provides introspection about namespaces. +// +// Note that these are slightly different than other objects, which are record +// oriented. A namespace is really just a name and a set of labels. Objects +// that belong to a namespace are returned when the namespace is assigned to a +// given context. +// +// +type Store interface { + Create(ctx context.Context, namespace string, labels map[string]string) error + Labels(ctx context.Context, namespace string) (map[string]string, error) + SetLabel(ctx context.Context, namespace, key, value string) error + List(ctx context.Context) ([]string, error) + + // Delete removes the namespace. The namespace must be empty to be deleted. + Delete(ctx context.Context, namespace string) error +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/namespaces/validate.go b/src/runtime/vendor/github.com/containerd/containerd/namespaces/validate.go new file mode 100644 index 000000000..222da3ea4 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/namespaces/validate.go @@ -0,0 +1,83 @@ +/* + Copyright The containerd Authors. + + 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 namespaces provides tools for working with namespaces across +// containerd. +// +// Namespaces collect resources such as containers and images, into a unique +// identifier space. This means that two applications can use the same +// identifiers and not conflict while using containerd. +// +// This package can be used to ensure that client and server functions +// correctly store the namespace on the context. +package namespaces + +import ( + "regexp" + + "github.com/containerd/containerd/errdefs" + "github.com/pkg/errors" +) + +const ( + maxLength = 76 + alpha = `[A-Za-z]` + alphanum = `[A-Za-z0-9]+` + label = alpha + alphanum + `(:?[-]+` + alpha + alphanum + `)*` +) + +var ( + // namespaceRe validates that a namespace matches valid identifiers. + // + // Rules for domains, defined in RFC 1035, section 2.3.1, are used for + // namespaces. + namespaceRe = regexp.MustCompile(reAnchor(label + reGroup("[.]"+reGroup(label)) + "*")) +) + +// Validate returns nil if the string s is a valid namespace. +// +// To allow such namespace identifiers to be used across various contexts +// safely, the character set has been restricted to that defined for domains in +// RFC 1035, section 2.3.1. This will make namespace identifiers safe for use +// across networks, filesystems and other media. +// +// The identifier specification departs from RFC 1035 in that it allows +// "labels" to start with number and only enforces a total length restriction +// of 76 characters. +// +// While the character set may be expanded in the future, namespace identifiers +// are guaranteed to be safely used as filesystem path components. +// +// For the most part, this doesn't need to be called directly when using the +// context-oriented functions. +func Validate(s string) error { + if len(s) > maxLength { + return errors.Wrapf(errdefs.ErrInvalidArgument, "namespace %q greater than maximum length (%d characters)", s, maxLength) + } + + if !namespaceRe.MatchString(s) { + return errors.Wrapf(errdefs.ErrInvalidArgument, "namespace %q must match %v", s, namespaceRe) + } + return nil +} + +func reGroup(s string) string { + return `(?:` + s + `)` +} + +func reAnchor(s string) string { + return `^` + s + `$` +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/events.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/events.go new file mode 100644 index 000000000..4a064c885 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/events.go @@ -0,0 +1,42 @@ +/* + Copyright The containerd Authors. + + 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 runtime + +const ( + // TaskCreateEventTopic for task create + TaskCreateEventTopic = "/tasks/create" + // TaskStartEventTopic for task start + TaskStartEventTopic = "/tasks/start" + // TaskOOMEventTopic for task oom + TaskOOMEventTopic = "/tasks/oom" + // TaskExitEventTopic for task exit + TaskExitEventTopic = "/tasks/exit" + // TaskDeleteEventTopic for task delete + TaskDeleteEventTopic = "/tasks/delete" + // TaskExecAddedEventTopic for task exec create + TaskExecAddedEventTopic = "/tasks/exec-added" + // TaskExecStartedEventTopic for task exec start + TaskExecStartedEventTopic = "/tasks/exec-started" + // TaskPausedEventTopic for task pause + TaskPausedEventTopic = "/tasks/paused" + // TaskResumedEventTopic for task resume + TaskResumedEventTopic = "/tasks/resumed" + // TaskCheckpointedEventTopic for task checkpoint + TaskCheckpointedEventTopic = "/tasks/checkpointed" + // TaskUnknownTopic for unknown task events + TaskUnknownTopic = "/tasks/?" +) diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/1.0.pb.txt b/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/1.0.pb.txt new file mode 100644 index 000000000..05f41fada --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/1.0.pb.txt @@ -0,0 +1,183 @@ +file { + name: "github.com/containerd/containerd/linux/runctypes/runc.proto" + package: "containerd.linux.runc" + dependency: "gogoproto/gogo.proto" + message_type { + name: "RuncOptions" + field { + name: "runtime" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "runtime" + } + field { + name: "runtime_root" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "runtimeRoot" + } + field { + name: "criu_path" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "criuPath" + } + field { + name: "systemd_cgroup" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "systemdCgroup" + } + } + message_type { + name: "CreateOptions" + field { + name: "no_pivot_root" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "noPivotRoot" + } + field { + name: "open_tcp" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "openTcp" + } + field { + name: "external_unix_sockets" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "externalUnixSockets" + } + field { + name: "terminal" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "terminal" + } + field { + name: "file_locks" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "fileLocks" + } + field { + name: "empty_namespaces" + number: 6 + label: LABEL_REPEATED + type: TYPE_STRING + json_name: "emptyNamespaces" + } + field { + name: "cgroups_mode" + number: 7 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "cgroupsMode" + } + field { + name: "no_new_keyring" + number: 8 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "noNewKeyring" + } + field { + name: "shim_cgroup" + number: 9 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "shimCgroup" + } + field { + name: "io_uid" + number: 10 + label: LABEL_OPTIONAL + type: TYPE_UINT32 + json_name: "ioUid" + } + field { + name: "io_gid" + number: 11 + label: LABEL_OPTIONAL + type: TYPE_UINT32 + json_name: "ioGid" + } + } + message_type { + name: "CheckpointOptions" + field { + name: "exit" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "exit" + } + field { + name: "open_tcp" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "openTcp" + } + field { + name: "external_unix_sockets" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "externalUnixSockets" + } + field { + name: "terminal" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "terminal" + } + field { + name: "file_locks" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "fileLocks" + } + field { + name: "empty_namespaces" + number: 6 + label: LABEL_REPEATED + type: TYPE_STRING + json_name: "emptyNamespaces" + } + field { + name: "cgroups_mode" + number: 7 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "cgroupsMode" + } + } + message_type { + name: "ProcessDetails" + field { + name: "exec_id" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "execId" + } + } + options { + go_package: "github.com/containerd/containerd/linux/runctypes;runctypes" + } + weak_dependency: 0 + syntax: "proto3" +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/doc.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/doc.go new file mode 100644 index 000000000..8d074c534 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + 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 runctypes diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/next.pb.txt b/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/next.pb.txt new file mode 100644 index 000000000..710815c3d --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/next.pb.txt @@ -0,0 +1,183 @@ +file { + name: "github.com/containerd/containerd/runtime/linux/runctypes/runc.proto" + package: "containerd.linux.runc" + dependency: "gogoproto/gogo.proto" + message_type { + name: "RuncOptions" + field { + name: "runtime" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "runtime" + } + field { + name: "runtime_root" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "runtimeRoot" + } + field { + name: "criu_path" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "criuPath" + } + field { + name: "systemd_cgroup" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "systemdCgroup" + } + } + message_type { + name: "CreateOptions" + field { + name: "no_pivot_root" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "noPivotRoot" + } + field { + name: "open_tcp" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "openTcp" + } + field { + name: "external_unix_sockets" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "externalUnixSockets" + } + field { + name: "terminal" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "terminal" + } + field { + name: "file_locks" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "fileLocks" + } + field { + name: "empty_namespaces" + number: 6 + label: LABEL_REPEATED + type: TYPE_STRING + json_name: "emptyNamespaces" + } + field { + name: "cgroups_mode" + number: 7 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "cgroupsMode" + } + field { + name: "no_new_keyring" + number: 8 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "noNewKeyring" + } + field { + name: "shim_cgroup" + number: 9 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "shimCgroup" + } + field { + name: "io_uid" + number: 10 + label: LABEL_OPTIONAL + type: TYPE_UINT32 + json_name: "ioUid" + } + field { + name: "io_gid" + number: 11 + label: LABEL_OPTIONAL + type: TYPE_UINT32 + json_name: "ioGid" + } + } + message_type { + name: "CheckpointOptions" + field { + name: "exit" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "exit" + } + field { + name: "open_tcp" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "openTcp" + } + field { + name: "external_unix_sockets" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "externalUnixSockets" + } + field { + name: "terminal" + number: 4 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "terminal" + } + field { + name: "file_locks" + number: 5 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "fileLocks" + } + field { + name: "empty_namespaces" + number: 6 + label: LABEL_REPEATED + type: TYPE_STRING + json_name: "emptyNamespaces" + } + field { + name: "cgroups_mode" + number: 7 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "cgroupsMode" + } + } + message_type { + name: "ProcessDetails" + field { + name: "exec_id" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "execId" + } + } + options { + go_package: "github.com/containerd/containerd/runtime/linux/runctypes;runctypes" + } + weak_dependency: 0 + syntax: "proto3" +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.pb.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.pb.go new file mode 100644 index 000000000..c13d23359 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.pb.go @@ -0,0 +1,1450 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/runtime/linux/runctypes/runc.proto + +/* + Package runctypes is a generated protocol buffer package. + + It is generated from these files: + github.com/containerd/containerd/runtime/linux/runctypes/runc.proto + + It has these top-level messages: + RuncOptions + CreateOptions + CheckpointOptions + ProcessDetails +*/ +package runctypes + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type RuncOptions struct { + Runtime string `protobuf:"bytes,1,opt,name=runtime,proto3" json:"runtime,omitempty"` + RuntimeRoot string `protobuf:"bytes,2,opt,name=runtime_root,json=runtimeRoot,proto3" json:"runtime_root,omitempty"` + CriuPath string `protobuf:"bytes,3,opt,name=criu_path,json=criuPath,proto3" json:"criu_path,omitempty"` + SystemdCgroup bool `protobuf:"varint,4,opt,name=systemd_cgroup,json=systemdCgroup,proto3" json:"systemd_cgroup,omitempty"` +} + +func (m *RuncOptions) Reset() { *m = RuncOptions{} } +func (*RuncOptions) ProtoMessage() {} +func (*RuncOptions) Descriptor() ([]byte, []int) { return fileDescriptorRunc, []int{0} } + +type CreateOptions struct { + NoPivotRoot bool `protobuf:"varint,1,opt,name=no_pivot_root,json=noPivotRoot,proto3" json:"no_pivot_root,omitempty"` + OpenTcp bool `protobuf:"varint,2,opt,name=open_tcp,json=openTcp,proto3" json:"open_tcp,omitempty"` + ExternalUnixSockets bool `protobuf:"varint,3,opt,name=external_unix_sockets,json=externalUnixSockets,proto3" json:"external_unix_sockets,omitempty"` + Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` + FileLocks bool `protobuf:"varint,5,opt,name=file_locks,json=fileLocks,proto3" json:"file_locks,omitempty"` + EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces" json:"empty_namespaces,omitempty"` + CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"` + NoNewKeyring bool `protobuf:"varint,8,opt,name=no_new_keyring,json=noNewKeyring,proto3" json:"no_new_keyring,omitempty"` + ShimCgroup string `protobuf:"bytes,9,opt,name=shim_cgroup,json=shimCgroup,proto3" json:"shim_cgroup,omitempty"` + IoUid uint32 `protobuf:"varint,10,opt,name=io_uid,json=ioUid,proto3" json:"io_uid,omitempty"` + IoGid uint32 `protobuf:"varint,11,opt,name=io_gid,json=ioGid,proto3" json:"io_gid,omitempty"` +} + +func (m *CreateOptions) Reset() { *m = CreateOptions{} } +func (*CreateOptions) ProtoMessage() {} +func (*CreateOptions) Descriptor() ([]byte, []int) { return fileDescriptorRunc, []int{1} } + +type CheckpointOptions struct { + Exit bool `protobuf:"varint,1,opt,name=exit,proto3" json:"exit,omitempty"` + OpenTcp bool `protobuf:"varint,2,opt,name=open_tcp,json=openTcp,proto3" json:"open_tcp,omitempty"` + ExternalUnixSockets bool `protobuf:"varint,3,opt,name=external_unix_sockets,json=externalUnixSockets,proto3" json:"external_unix_sockets,omitempty"` + Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` + FileLocks bool `protobuf:"varint,5,opt,name=file_locks,json=fileLocks,proto3" json:"file_locks,omitempty"` + EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces" json:"empty_namespaces,omitempty"` + CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"` +} + +func (m *CheckpointOptions) Reset() { *m = CheckpointOptions{} } +func (*CheckpointOptions) ProtoMessage() {} +func (*CheckpointOptions) Descriptor() ([]byte, []int) { return fileDescriptorRunc, []int{2} } + +type ProcessDetails struct { + ExecID string `protobuf:"bytes,1,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` +} + +func (m *ProcessDetails) Reset() { *m = ProcessDetails{} } +func (*ProcessDetails) ProtoMessage() {} +func (*ProcessDetails) Descriptor() ([]byte, []int) { return fileDescriptorRunc, []int{3} } + +func init() { + proto.RegisterType((*RuncOptions)(nil), "containerd.linux.runc.RuncOptions") + proto.RegisterType((*CreateOptions)(nil), "containerd.linux.runc.CreateOptions") + proto.RegisterType((*CheckpointOptions)(nil), "containerd.linux.runc.CheckpointOptions") + proto.RegisterType((*ProcessDetails)(nil), "containerd.linux.runc.ProcessDetails") +} +func (m *RuncOptions) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RuncOptions) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Runtime) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintRunc(dAtA, i, uint64(len(m.Runtime))) + i += copy(dAtA[i:], m.Runtime) + } + if len(m.RuntimeRoot) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintRunc(dAtA, i, uint64(len(m.RuntimeRoot))) + i += copy(dAtA[i:], m.RuntimeRoot) + } + if len(m.CriuPath) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintRunc(dAtA, i, uint64(len(m.CriuPath))) + i += copy(dAtA[i:], m.CriuPath) + } + if m.SystemdCgroup { + dAtA[i] = 0x20 + i++ + if m.SystemdCgroup { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + return i, nil +} + +func (m *CreateOptions) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CreateOptions) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.NoPivotRoot { + dAtA[i] = 0x8 + i++ + if m.NoPivotRoot { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.OpenTcp { + dAtA[i] = 0x10 + i++ + if m.OpenTcp { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.ExternalUnixSockets { + dAtA[i] = 0x18 + i++ + if m.ExternalUnixSockets { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.Terminal { + dAtA[i] = 0x20 + i++ + if m.Terminal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.FileLocks { + dAtA[i] = 0x28 + i++ + if m.FileLocks { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if len(m.EmptyNamespaces) > 0 { + for _, s := range m.EmptyNamespaces { + dAtA[i] = 0x32 + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + if len(m.CgroupsMode) > 0 { + dAtA[i] = 0x3a + i++ + i = encodeVarintRunc(dAtA, i, uint64(len(m.CgroupsMode))) + i += copy(dAtA[i:], m.CgroupsMode) + } + if m.NoNewKeyring { + dAtA[i] = 0x40 + i++ + if m.NoNewKeyring { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if len(m.ShimCgroup) > 0 { + dAtA[i] = 0x4a + i++ + i = encodeVarintRunc(dAtA, i, uint64(len(m.ShimCgroup))) + i += copy(dAtA[i:], m.ShimCgroup) + } + if m.IoUid != 0 { + dAtA[i] = 0x50 + i++ + i = encodeVarintRunc(dAtA, i, uint64(m.IoUid)) + } + if m.IoGid != 0 { + dAtA[i] = 0x58 + i++ + i = encodeVarintRunc(dAtA, i, uint64(m.IoGid)) + } + return i, nil +} + +func (m *CheckpointOptions) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CheckpointOptions) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Exit { + dAtA[i] = 0x8 + i++ + if m.Exit { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.OpenTcp { + dAtA[i] = 0x10 + i++ + if m.OpenTcp { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.ExternalUnixSockets { + dAtA[i] = 0x18 + i++ + if m.ExternalUnixSockets { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.Terminal { + dAtA[i] = 0x20 + i++ + if m.Terminal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.FileLocks { + dAtA[i] = 0x28 + i++ + if m.FileLocks { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if len(m.EmptyNamespaces) > 0 { + for _, s := range m.EmptyNamespaces { + dAtA[i] = 0x32 + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + if len(m.CgroupsMode) > 0 { + dAtA[i] = 0x3a + i++ + i = encodeVarintRunc(dAtA, i, uint64(len(m.CgroupsMode))) + i += copy(dAtA[i:], m.CgroupsMode) + } + return i, nil +} + +func (m *ProcessDetails) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProcessDetails) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ExecID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintRunc(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + return i, nil +} + +func encodeVarintRunc(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *RuncOptions) Size() (n int) { + var l int + _ = l + l = len(m.Runtime) + if l > 0 { + n += 1 + l + sovRunc(uint64(l)) + } + l = len(m.RuntimeRoot) + if l > 0 { + n += 1 + l + sovRunc(uint64(l)) + } + l = len(m.CriuPath) + if l > 0 { + n += 1 + l + sovRunc(uint64(l)) + } + if m.SystemdCgroup { + n += 2 + } + return n +} + +func (m *CreateOptions) Size() (n int) { + var l int + _ = l + if m.NoPivotRoot { + n += 2 + } + if m.OpenTcp { + n += 2 + } + if m.ExternalUnixSockets { + n += 2 + } + if m.Terminal { + n += 2 + } + if m.FileLocks { + n += 2 + } + if len(m.EmptyNamespaces) > 0 { + for _, s := range m.EmptyNamespaces { + l = len(s) + n += 1 + l + sovRunc(uint64(l)) + } + } + l = len(m.CgroupsMode) + if l > 0 { + n += 1 + l + sovRunc(uint64(l)) + } + if m.NoNewKeyring { + n += 2 + } + l = len(m.ShimCgroup) + if l > 0 { + n += 1 + l + sovRunc(uint64(l)) + } + if m.IoUid != 0 { + n += 1 + sovRunc(uint64(m.IoUid)) + } + if m.IoGid != 0 { + n += 1 + sovRunc(uint64(m.IoGid)) + } + return n +} + +func (m *CheckpointOptions) Size() (n int) { + var l int + _ = l + if m.Exit { + n += 2 + } + if m.OpenTcp { + n += 2 + } + if m.ExternalUnixSockets { + n += 2 + } + if m.Terminal { + n += 2 + } + if m.FileLocks { + n += 2 + } + if len(m.EmptyNamespaces) > 0 { + for _, s := range m.EmptyNamespaces { + l = len(s) + n += 1 + l + sovRunc(uint64(l)) + } + } + l = len(m.CgroupsMode) + if l > 0 { + n += 1 + l + sovRunc(uint64(l)) + } + return n +} + +func (m *ProcessDetails) Size() (n int) { + var l int + _ = l + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovRunc(uint64(l)) + } + return n +} + +func sovRunc(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozRunc(x uint64) (n int) { + return sovRunc(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *RuncOptions) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RuncOptions{`, + `Runtime:` + fmt.Sprintf("%v", this.Runtime) + `,`, + `RuntimeRoot:` + fmt.Sprintf("%v", this.RuntimeRoot) + `,`, + `CriuPath:` + fmt.Sprintf("%v", this.CriuPath) + `,`, + `SystemdCgroup:` + fmt.Sprintf("%v", this.SystemdCgroup) + `,`, + `}`, + }, "") + return s +} +func (this *CreateOptions) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateOptions{`, + `NoPivotRoot:` + fmt.Sprintf("%v", this.NoPivotRoot) + `,`, + `OpenTcp:` + fmt.Sprintf("%v", this.OpenTcp) + `,`, + `ExternalUnixSockets:` + fmt.Sprintf("%v", this.ExternalUnixSockets) + `,`, + `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, + `FileLocks:` + fmt.Sprintf("%v", this.FileLocks) + `,`, + `EmptyNamespaces:` + fmt.Sprintf("%v", this.EmptyNamespaces) + `,`, + `CgroupsMode:` + fmt.Sprintf("%v", this.CgroupsMode) + `,`, + `NoNewKeyring:` + fmt.Sprintf("%v", this.NoNewKeyring) + `,`, + `ShimCgroup:` + fmt.Sprintf("%v", this.ShimCgroup) + `,`, + `IoUid:` + fmt.Sprintf("%v", this.IoUid) + `,`, + `IoGid:` + fmt.Sprintf("%v", this.IoGid) + `,`, + `}`, + }, "") + return s +} +func (this *CheckpointOptions) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CheckpointOptions{`, + `Exit:` + fmt.Sprintf("%v", this.Exit) + `,`, + `OpenTcp:` + fmt.Sprintf("%v", this.OpenTcp) + `,`, + `ExternalUnixSockets:` + fmt.Sprintf("%v", this.ExternalUnixSockets) + `,`, + `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, + `FileLocks:` + fmt.Sprintf("%v", this.FileLocks) + `,`, + `EmptyNamespaces:` + fmt.Sprintf("%v", this.EmptyNamespaces) + `,`, + `CgroupsMode:` + fmt.Sprintf("%v", this.CgroupsMode) + `,`, + `}`, + }, "") + return s +} +func (this *ProcessDetails) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ProcessDetails{`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `}`, + }, "") + return s +} +func valueToStringRunc(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *RuncOptions) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RuncOptions: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RuncOptions: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Runtime", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Runtime = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RuntimeRoot", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RuntimeRoot = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CriuPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CriuPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SystemdCgroup", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.SystemdCgroup = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipRunc(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthRunc + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CreateOptions) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CreateOptions: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CreateOptions: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NoPivotRoot", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.NoPivotRoot = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OpenTcp", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.OpenTcp = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalUnixSockets", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.ExternalUnixSockets = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Terminal", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Terminal = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FileLocks", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.FileLocks = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EmptyNamespaces", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EmptyNamespaces = append(m.EmptyNamespaces, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CgroupsMode", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CgroupsMode = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NoNewKeyring", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.NoNewKeyring = bool(v != 0) + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShimCgroup", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ShimCgroup = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IoUid", wireType) + } + m.IoUid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.IoUid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IoGid", wireType) + } + m.IoGid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.IoGid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipRunc(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthRunc + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CheckpointOptions: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CheckpointOptions: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Exit", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Exit = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OpenTcp", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.OpenTcp = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalUnixSockets", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.ExternalUnixSockets = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Terminal", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Terminal = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FileLocks", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.FileLocks = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EmptyNamespaces", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EmptyNamespaces = append(m.EmptyNamespaces, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CgroupsMode", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CgroupsMode = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRunc(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthRunc + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ProcessDetails) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProcessDetails: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProcessDetails: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRunc(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthRunc + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipRunc(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunc + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunc + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunc + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthRunc + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunc + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipRunc(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthRunc = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowRunc = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/runtime/linux/runctypes/runc.proto", fileDescriptorRunc) +} + +var fileDescriptorRunc = []byte{ + // 541 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x93, 0xc1, 0x6e, 0xd3, 0x40, + 0x10, 0x86, 0x6b, 0xda, 0x26, 0xce, 0xa4, 0x29, 0xb0, 0x50, 0xc9, 0x14, 0x91, 0x86, 0x00, 0x52, + 0xb8, 0xa4, 0x12, 0x88, 0x13, 0xb7, 0xa6, 0x08, 0x55, 0x40, 0xa9, 0x0c, 0x95, 0x10, 0x42, 0x5a, + 0xb9, 0xeb, 0x21, 0x59, 0xc5, 0xde, 0x59, 0x79, 0xd7, 0xd4, 0xb9, 0xf5, 0x09, 0x78, 0xae, 0x1e, + 0x39, 0x72, 0x42, 0x34, 0x2f, 0x02, 0xf2, 0xda, 0x0e, 0x9c, 0x39, 0x72, 0xfb, 0xe7, 0xfb, 0xc7, + 0x9e, 0xd1, 0xbf, 0x1a, 0x98, 0x4c, 0xa5, 0x9d, 0xe5, 0x67, 0x63, 0x41, 0xe9, 0xbe, 0x20, 0x65, + 0x23, 0xa9, 0x30, 0x8b, 0xff, 0x96, 0x59, 0xae, 0xac, 0x4c, 0x71, 0x3f, 0x91, 0x2a, 0x2f, 0xca, + 0x4a, 0xd8, 0x85, 0x46, 0xe3, 0xd4, 0x58, 0x67, 0x64, 0x89, 0xed, 0xfc, 0x69, 0x1f, 0xbb, 0xb6, + 0x71, 0x69, 0xee, 0xde, 0x9e, 0xd2, 0x94, 0x5c, 0xc7, 0x7e, 0xa9, 0xaa, 0xe6, 0xe1, 0x57, 0x0f, + 0xba, 0x61, 0xae, 0xc4, 0x5b, 0x6d, 0x25, 0x29, 0xc3, 0x02, 0x68, 0xd7, 0x23, 0x02, 0x6f, 0xe0, + 0x8d, 0x3a, 0x61, 0x53, 0xb2, 0xfb, 0xb0, 0x55, 0x4b, 0x9e, 0x11, 0xd9, 0xe0, 0x9a, 0xb3, 0xbb, + 0x35, 0x0b, 0x89, 0x2c, 0xbb, 0x0b, 0x1d, 0x91, 0xc9, 0x9c, 0xeb, 0xc8, 0xce, 0x82, 0x75, 0xe7, + 0xfb, 0x25, 0x38, 0x89, 0xec, 0x8c, 0x3d, 0x82, 0x6d, 0xb3, 0x30, 0x16, 0xd3, 0x98, 0x8b, 0x69, + 0x46, 0xb9, 0x0e, 0x36, 0x06, 0xde, 0xc8, 0x0f, 0x7b, 0x35, 0x9d, 0x38, 0x38, 0xbc, 0x58, 0x87, + 0xde, 0x24, 0xc3, 0xc8, 0x62, 0xb3, 0xd2, 0x10, 0x7a, 0x8a, 0xb8, 0x96, 0x5f, 0xc8, 0x56, 0x93, + 0x3d, 0xf7, 0x5d, 0x57, 0xd1, 0x49, 0xc9, 0xdc, 0xe4, 0x3b, 0xe0, 0x93, 0x46, 0xc5, 0xad, 0xd0, + 0x6e, 0x31, 0x3f, 0x6c, 0x97, 0xf5, 0x7b, 0xa1, 0xd9, 0x13, 0xd8, 0xc1, 0xc2, 0x62, 0xa6, 0xa2, + 0x84, 0xe7, 0x4a, 0x16, 0xdc, 0x90, 0x98, 0xa3, 0x35, 0x6e, 0x41, 0x3f, 0xbc, 0xd5, 0x98, 0xa7, + 0x4a, 0x16, 0xef, 0x2a, 0x8b, 0xed, 0x82, 0x6f, 0x31, 0x4b, 0xa5, 0x8a, 0x92, 0x7a, 0xcb, 0x55, + 0xcd, 0xee, 0x01, 0x7c, 0x96, 0x09, 0xf2, 0x84, 0xc4, 0xdc, 0x04, 0x9b, 0xce, 0xed, 0x94, 0xe4, + 0x75, 0x09, 0xd8, 0x63, 0xb8, 0x81, 0xa9, 0xb6, 0x0b, 0xae, 0xa2, 0x14, 0x8d, 0x8e, 0x04, 0x9a, + 0xa0, 0x35, 0x58, 0x1f, 0x75, 0xc2, 0xeb, 0x8e, 0x1f, 0xaf, 0x70, 0x99, 0x68, 0x95, 0x84, 0xe1, + 0x29, 0xc5, 0x18, 0xb4, 0xab, 0x44, 0x6b, 0xf6, 0x86, 0x62, 0x64, 0x0f, 0x61, 0x5b, 0x11, 0x57, + 0x78, 0xce, 0xe7, 0xb8, 0xc8, 0xa4, 0x9a, 0x06, 0xbe, 0x1b, 0xb8, 0xa5, 0xe8, 0x18, 0xcf, 0x5f, + 0x55, 0x8c, 0xed, 0x41, 0xd7, 0xcc, 0x64, 0xda, 0xe4, 0xda, 0x71, 0xff, 0x81, 0x12, 0x55, 0xa1, + 0xb2, 0x1d, 0x68, 0x49, 0xe2, 0xb9, 0x8c, 0x03, 0x18, 0x78, 0xa3, 0x5e, 0xb8, 0x29, 0xe9, 0x54, + 0xc6, 0x35, 0x9e, 0xca, 0x38, 0xe8, 0x36, 0xf8, 0xa5, 0x8c, 0x87, 0xbf, 0x3c, 0xb8, 0x39, 0x99, + 0xa1, 0x98, 0x6b, 0x92, 0xca, 0x36, 0xcf, 0xc0, 0x60, 0x03, 0x0b, 0xd9, 0xa4, 0xef, 0xf4, 0xff, + 0x1a, 0xfb, 0xf0, 0x19, 0x6c, 0x9f, 0x64, 0x24, 0xd0, 0x98, 0x43, 0xb4, 0x91, 0x4c, 0x0c, 0x7b, + 0x00, 0x6d, 0x2c, 0x50, 0x70, 0x19, 0x57, 0x77, 0x71, 0x00, 0xcb, 0x1f, 0x7b, 0xad, 0x17, 0x05, + 0x8a, 0xa3, 0xc3, 0xb0, 0x55, 0x5a, 0x47, 0xf1, 0xc1, 0xa7, 0xcb, 0xab, 0xfe, 0xda, 0xf7, 0xab, + 0xfe, 0xda, 0xc5, 0xb2, 0xef, 0x5d, 0x2e, 0xfb, 0xde, 0xb7, 0x65, 0xdf, 0xfb, 0xb9, 0xec, 0x7b, + 0x1f, 0x0f, 0xfe, 0xf5, 0xb0, 0x9f, 0xaf, 0xd4, 0x87, 0xb5, 0xb3, 0x96, 0xbb, 0xd9, 0xa7, 0xbf, + 0x03, 0x00, 0x00, 0xff, 0xff, 0x18, 0xa1, 0x4b, 0x5b, 0x27, 0x04, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.proto b/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.proto new file mode 100644 index 000000000..ddd3f8d11 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +package containerd.linux.runc; + +import weak "gogoproto/gogo.proto"; + +option go_package = "github.com/containerd/containerd/runtime/linux/runctypes;runctypes"; + +message RuncOptions { + string runtime = 1; + string runtime_root = 2; + string criu_path = 3; + bool systemd_cgroup = 4; +} + +message CreateOptions { + bool no_pivot_root = 1; + bool open_tcp = 2; + bool external_unix_sockets = 3; + bool terminal = 4; + bool file_locks = 5; + repeated string empty_namespaces = 6; + string cgroups_mode = 7; + bool no_new_keyring = 8; + string shim_cgroup = 9; + uint32 io_uid = 10; + uint32 io_gid = 11; +} + +message CheckpointOptions { + bool exit = 1; + bool open_tcp = 2; + bool external_unix_sockets = 3; + bool terminal = 4; + bool file_locks = 5; + repeated string empty_namespaces = 6; + string cgroups_mode = 7; +} + +message ProcessDetails { + string exec_id = 1; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/monitor.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/monitor.go new file mode 100644 index 000000000..eb07ebdb4 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/monitor.go @@ -0,0 +1,70 @@ +/* + Copyright The containerd Authors. + + 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 runtime + +// TaskMonitor provides an interface for monitoring of containers within containerd +type TaskMonitor interface { + // Monitor adds the provided container to the monitor + Monitor(Task) error + // Stop stops and removes the provided container from the monitor + Stop(Task) error +} + +// NewMultiTaskMonitor returns a new TaskMonitor broadcasting to the provided monitors +func NewMultiTaskMonitor(monitors ...TaskMonitor) TaskMonitor { + return &multiTaskMonitor{ + monitors: monitors, + } +} + +// NewNoopMonitor is a task monitor that does nothing +func NewNoopMonitor() TaskMonitor { + return &noopTaskMonitor{} +} + +type noopTaskMonitor struct { +} + +func (mm *noopTaskMonitor) Monitor(c Task) error { + return nil +} + +func (mm *noopTaskMonitor) Stop(c Task) error { + return nil +} + +type multiTaskMonitor struct { + monitors []TaskMonitor +} + +func (mm *multiTaskMonitor) Monitor(c Task) error { + for _, m := range mm.monitors { + if err := m.Monitor(c); err != nil { + return err + } + } + return nil +} + +func (mm *multiTaskMonitor) Stop(c Task) error { + for _, m := range mm.monitors { + if err := m.Stop(c); err != nil { + return err + } + } + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/runtime.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/runtime.go new file mode 100644 index 000000000..1b3f87c15 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/runtime.go @@ -0,0 +1,72 @@ +/* + Copyright The containerd Authors. + + 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 runtime + +import ( + "context" + "time" + + "github.com/containerd/containerd/mount" + "github.com/gogo/protobuf/types" +) + +// IO holds process IO information +type IO struct { + Stdin string + Stdout string + Stderr string + Terminal bool +} + +// CreateOpts contains task creation data +type CreateOpts struct { + // Spec is the OCI runtime spec + Spec *types.Any + // Rootfs mounts to perform to gain access to the container's filesystem + Rootfs []mount.Mount + // IO for the container's main process + IO IO + // Checkpoint digest to restore container state + Checkpoint string + // RuntimeOptions for the runtime + RuntimeOptions *types.Any + // TaskOptions received for the task + TaskOptions *types.Any + // Runtime to use + Runtime string +} + +// Exit information for a process +type Exit struct { + Pid uint32 + Status uint32 + Timestamp time.Time +} + +// PlatformRuntime is responsible for the creation and management of +// tasks and processes for a platform. +type PlatformRuntime interface { + // ID of the runtime + ID() string + // Create creates a task with the provided id and options. + Create(ctx context.Context, id string, opts CreateOpts) (Task, error) + // Get returns a task. + Get(context.Context, string) (Task, error) + // Tasks returns all the current tasks for the runtime. + // Any container runs at most one task at a time. + Tasks(context.Context, bool) ([]Task, error) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/task.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/task.go new file mode 100644 index 000000000..981e290c6 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/task.go @@ -0,0 +1,132 @@ +/* + Copyright The containerd Authors. + + 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 runtime + +import ( + "context" + "time" + + "github.com/gogo/protobuf/types" +) + +// TaskInfo provides task specific information +type TaskInfo struct { + ID string + Runtime string + Spec []byte + Namespace string +} + +// Process is a runtime object for an executing process inside a container +type Process interface { + ID() string + // State returns the process state + State(context.Context) (State, error) + // Kill signals a container + Kill(context.Context, uint32, bool) error + // Pty resizes the processes pty/console + ResizePty(context.Context, ConsoleSize) error + // CloseStdin closes the processes stdin + CloseIO(context.Context) error + // Start the container's user defined process + Start(context.Context) error + // Wait for the process to exit + Wait(context.Context) (*Exit, error) + // Delete deletes the process + Delete(context.Context) (*Exit, error) +} + +// Task is the runtime object for an executing container +type Task interface { + Process + + // Namespace that the task exists in + Namespace() string + // Pause pauses the container process + Pause(context.Context) error + // Resume unpauses the container process + Resume(context.Context) error + // Exec adds a process into the container + Exec(context.Context, string, ExecOpts) (Process, error) + // Pids returns all pids + Pids(context.Context) ([]ProcessInfo, error) + // Checkpoint checkpoints a container to an image with live system data + Checkpoint(context.Context, string, *types.Any) error + // Update sets the provided resources to a running task + Update(context.Context, *types.Any) error + // Process returns a process within the task for the provided id + Process(context.Context, string) (Process, error) + // Stats returns runtime specific metrics for a task + Stats(context.Context) (*types.Any, error) +} + +// ExecOpts provides additional options for additional processes running in a task +type ExecOpts struct { + Spec *types.Any + IO IO +} + +// ConsoleSize of a pty or windows terminal +type ConsoleSize struct { + Width uint32 + Height uint32 +} + +// Status is the runtime status of a task and/or process +type Status int + +const ( + // CreatedStatus when a process has been created + CreatedStatus Status = iota + 1 + // RunningStatus when a process is running + RunningStatus + // StoppedStatus when a process has stopped + StoppedStatus + // DeletedStatus when a process has been deleted + DeletedStatus + // PausedStatus when a process is paused + PausedStatus + // PausingStatus when a process is currently pausing + PausingStatus +) + +// State information for a process +type State struct { + // Status is the current status of the container + Status Status + // Pid is the main process id for the container + Pid uint32 + // ExitStatus of the process + // Only valid if the Status is Stopped + ExitStatus uint32 + // ExitedAt is the time at which the process exited + // Only valid if the Status is Stopped + ExitedAt time.Time + Stdin string + Stdout string + Stderr string + Terminal bool +} + +// ProcessInfo holds platform specific process information +type ProcessInfo struct { + // Pid is the process ID + Pid uint32 + // Info includes additional process information + // Info varies by platform + Info interface{} +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/task_list.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/task_list.go new file mode 100644 index 000000000..b92c6e01c --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/task_list.go @@ -0,0 +1,130 @@ +/* + Copyright The containerd Authors. + + 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 runtime + +import ( + "context" + "sync" + + "github.com/containerd/containerd/namespaces" + "github.com/pkg/errors" +) + +var ( + // ErrTaskNotExists is returned when a task does not exist + ErrTaskNotExists = errors.New("task does not exist") + // ErrTaskAlreadyExists is returned when a task already exists + ErrTaskAlreadyExists = errors.New("task already exists") +) + +// NewTaskList returns a new TaskList +func NewTaskList() *TaskList { + return &TaskList{ + tasks: make(map[string]map[string]Task), + } +} + +// TaskList holds and provides locking around tasks +type TaskList struct { + mu sync.Mutex + tasks map[string]map[string]Task +} + +// Get a task +func (l *TaskList) Get(ctx context.Context, id string) (Task, error) { + l.mu.Lock() + defer l.mu.Unlock() + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return nil, err + } + tasks, ok := l.tasks[namespace] + if !ok { + return nil, ErrTaskNotExists + } + t, ok := tasks[id] + if !ok { + return nil, ErrTaskNotExists + } + return t, nil +} + +// GetAll tasks under a namespace +func (l *TaskList) GetAll(ctx context.Context, noNS bool) ([]Task, error) { + l.mu.Lock() + defer l.mu.Unlock() + var o []Task + if noNS { + for ns := range l.tasks { + for _, t := range l.tasks[ns] { + o = append(o, t) + } + } + return o, nil + } + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return nil, err + } + tasks, ok := l.tasks[namespace] + if !ok { + return o, nil + } + for _, t := range tasks { + o = append(o, t) + } + return o, nil +} + +// Add a task +func (l *TaskList) Add(ctx context.Context, t Task) error { + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return err + } + return l.AddWithNamespace(namespace, t) +} + +// AddWithNamespace adds a task with the provided namespace +func (l *TaskList) AddWithNamespace(namespace string, t Task) error { + l.mu.Lock() + defer l.mu.Unlock() + + id := t.ID() + if _, ok := l.tasks[namespace]; !ok { + l.tasks[namespace] = make(map[string]Task) + } + if _, ok := l.tasks[namespace][id]; ok { + return errors.Wrap(ErrTaskAlreadyExists, id) + } + l.tasks[namespace][id] = t + return nil +} + +// Delete a task +func (l *TaskList) Delete(ctx context.Context, id string) { + l.mu.Lock() + defer l.mu.Unlock() + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return + } + tasks, ok := l.tasks[namespace] + if ok { + delete(tasks, id) + } +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/typeurl.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/typeurl.go new file mode 100644 index 000000000..eb54e250f --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/typeurl.go @@ -0,0 +1,34 @@ +/* + Copyright The containerd Authors. + + 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 runtime + +import ( + "strconv" + + "github.com/containerd/typeurl" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func init() { + const prefix = "types.containerd.io" + // register TypeUrls for commonly marshaled external types + major := strconv.Itoa(specs.VersionMajor) + typeurl.Register(&specs.Spec{}, prefix, "opencontainers/runtime-spec", major, "Spec") + typeurl.Register(&specs.Process{}, prefix, "opencontainers/runtime-spec", major, "Process") + typeurl.Register(&specs.LinuxResources{}, prefix, "opencontainers/runtime-spec", major, "LinuxResources") + typeurl.Register(&specs.WindowsResources{}, prefix, "opencontainers/runtime-spec", major, "WindowsResources") +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/reaper_unix.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/reaper_unix.go new file mode 100644 index 000000000..45a88db12 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/reaper_unix.go @@ -0,0 +1,109 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 shim + +import ( + "os/exec" + "sync" + "time" + + "github.com/containerd/containerd/sys" + runc "github.com/containerd/go-runc" + "github.com/pkg/errors" +) + +// ErrNoSuchProcess is returned when the process no longer exists +var ErrNoSuchProcess = errors.New("no such process") + +const bufferSize = 2048 + +// Reap should be called when the process receives an SIGCHLD. Reap will reap +// all exited processes and close their wait channels +func Reap() error { + now := time.Now() + exits, err := sys.Reap(false) + Default.Lock() + for c := range Default.subscribers { + for _, e := range exits { + c <- runc.Exit{ + Timestamp: now, + Pid: e.Pid, + Status: e.Status, + } + } + } + Default.Unlock() + return err +} + +// Default is the default monitor initialized for the package +var Default = &Monitor{ + subscribers: make(map[chan runc.Exit]struct{}), +} + +// Monitor monitors the underlying system for process status changes +type Monitor struct { + sync.Mutex + + subscribers map[chan runc.Exit]struct{} +} + +// Start starts the command a registers the process with the reaper +func (m *Monitor) Start(c *exec.Cmd) (chan runc.Exit, error) { + ec := m.Subscribe() + if err := c.Start(); err != nil { + m.Unsubscribe(ec) + return nil, err + } + return ec, nil +} + +// Wait blocks until a process is signal as dead. +// User should rely on the value of the exit status to determine if the +// command was successful or not. +func (m *Monitor) Wait(c *exec.Cmd, ec chan runc.Exit) (int, error) { + for e := range ec { + if e.Pid == c.Process.Pid { + // make sure we flush all IO + c.Wait() + m.Unsubscribe(ec) + return e.Status, nil + } + } + // return no such process if the ec channel is closed and no more exit + // events will be sent + return -1, ErrNoSuchProcess +} + +// Subscribe to process exit changes +func (m *Monitor) Subscribe() chan runc.Exit { + c := make(chan runc.Exit, bufferSize) + m.Lock() + m.subscribers[c] = struct{}{} + m.Unlock() + return c +} + +// Unsubscribe to process exit changes +func (m *Monitor) Unsubscribe(c chan runc.Exit) { + m.Lock() + delete(m.subscribers, c) + close(c) + m.Unlock() +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim.go new file mode 100644 index 000000000..d60d49663 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim.go @@ -0,0 +1,288 @@ +/* + Copyright The containerd Authors. + + 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 shim + +import ( + "context" + "flag" + "fmt" + "os" + "runtime" + "runtime/debug" + "strings" + "time" + + "github.com/containerd/containerd/events" + "github.com/containerd/containerd/log" + "github.com/containerd/containerd/namespaces" + shimapi "github.com/containerd/containerd/runtime/v2/task" + "github.com/containerd/ttrpc" + "github.com/gogo/protobuf/proto" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// Client for a shim server +type Client struct { + service shimapi.TaskService + context context.Context + signals chan os.Signal +} + +// Init func for the creation of a shim server +type Init func(context.Context, string, events.Publisher) (Shim, error) + +// Shim server interface +type Shim interface { + shimapi.TaskService + Cleanup(ctx context.Context) (*shimapi.DeleteResponse, error) + StartShim(ctx context.Context, id, containerdBinary, containerdAddress string) (string, error) +} + +// OptsKey is the context key for the Opts value. +type OptsKey struct{} + +// Opts are context options associated with the shim invocation. +type Opts struct { + BundlePath string + Debug bool +} + +// BinaryOpts allows the configuration of a shims binary setup +type BinaryOpts func(*Config) + +// Config of shim binary options provided by shim implementations +type Config struct { + // NoSubreaper disables setting the shim as a child subreaper + NoSubreaper bool + // NoReaper disables the shim binary from reaping any child process implicitly + NoReaper bool +} + +var ( + debugFlag bool + idFlag string + namespaceFlag string + socketFlag string + bundlePath string + addressFlag string + containerdBinaryFlag string + action string +) + +func parseFlags() { + flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs") + flag.StringVar(&namespaceFlag, "namespace", "", "namespace that owns the shim") + flag.StringVar(&idFlag, "id", "", "id of the task") + flag.StringVar(&socketFlag, "socket", "", "abstract socket path to serve") + flag.StringVar(&bundlePath, "bundle", "", "path to the bundle if not workdir") + + flag.StringVar(&addressFlag, "address", "", "grpc address back to main containerd") + flag.StringVar(&containerdBinaryFlag, "publish-binary", "containerd", "path to publish binary (used for publishing events)") + + flag.Parse() + action = flag.Arg(0) +} + +func setRuntime() { + debug.SetGCPercent(40) + go func() { + for range time.Tick(30 * time.Second) { + debug.FreeOSMemory() + } + }() + if os.Getenv("GOMAXPROCS") == "" { + // If GOMAXPROCS hasn't been set, we default to a value of 2 to reduce + // the number of Go stacks present in the shim. + runtime.GOMAXPROCS(2) + } +} + +func setLogger(ctx context.Context, id string) error { + logrus.SetFormatter(&logrus.TextFormatter{ + TimestampFormat: log.RFC3339NanoFixed, + FullTimestamp: true, + }) + if debugFlag { + logrus.SetLevel(logrus.DebugLevel) + } + f, err := openLog(ctx, id) + if err != nil { + return err + } + logrus.SetOutput(f) + return nil +} + +// Run initializes and runs a shim server +func Run(id string, initFunc Init, opts ...BinaryOpts) { + var config Config + for _, o := range opts { + o(&config) + } + if err := run(id, initFunc, config); err != nil { + fmt.Fprintf(os.Stderr, "%s: %s\n", id, err) + os.Exit(1) + } +} + +func run(id string, initFunc Init, config Config) error { + parseFlags() + setRuntime() + + signals, err := setupSignals(config) + if err != nil { + return err + } + if !config.NoSubreaper { + if err := subreaper(); err != nil { + return err + } + } + publisher := &remoteEventsPublisher{ + address: addressFlag, + containerdBinaryPath: containerdBinaryFlag, + noReaper: config.NoReaper, + } + if namespaceFlag == "" { + return fmt.Errorf("shim namespace cannot be empty") + } + ctx := namespaces.WithNamespace(context.Background(), namespaceFlag) + ctx = context.WithValue(ctx, OptsKey{}, Opts{BundlePath: bundlePath, Debug: debugFlag}) + ctx = log.WithLogger(ctx, log.G(ctx).WithField("runtime", id)) + + service, err := initFunc(ctx, idFlag, publisher) + if err != nil { + return err + } + switch action { + case "delete": + logger := logrus.WithFields(logrus.Fields{ + "pid": os.Getpid(), + "namespace": namespaceFlag, + }) + go handleSignals(logger, signals) + response, err := service.Cleanup(ctx) + if err != nil { + return err + } + data, err := proto.Marshal(response) + if err != nil { + return err + } + if _, err := os.Stdout.Write(data); err != nil { + return err + } + return nil + case "start": + address, err := service.StartShim(ctx, idFlag, containerdBinaryFlag, addressFlag) + if err != nil { + return err + } + if _, err := os.Stdout.WriteString(address); err != nil { + return err + } + return nil + default: + if err := setLogger(ctx, idFlag); err != nil { + return err + } + client := NewShimClient(ctx, service, signals) + return client.Serve() + } +} + +// NewShimClient creates a new shim server client +func NewShimClient(ctx context.Context, svc shimapi.TaskService, signals chan os.Signal) *Client { + s := &Client{ + service: svc, + context: ctx, + signals: signals, + } + return s +} + +// Serve the shim server +func (s *Client) Serve() error { + dump := make(chan os.Signal, 32) + setupDumpStacks(dump) + + path, err := os.Getwd() + if err != nil { + return err + } + server, err := newServer() + if err != nil { + return errors.Wrap(err, "failed creating server") + } + + logrus.Debug("registering ttrpc server") + shimapi.RegisterTaskService(server, s.service) + + if err := serve(s.context, server, socketFlag); err != nil { + return err + } + logger := logrus.WithFields(logrus.Fields{ + "pid": os.Getpid(), + "path": path, + "namespace": namespaceFlag, + }) + go func() { + for range dump { + dumpStacks(logger) + } + }() + return handleSignals(logger, s.signals) +} + +// serve serves the ttrpc API over a unix socket at the provided path +// this function does not block +func serve(ctx context.Context, server *ttrpc.Server, path string) error { + l, err := serveListener(path) + if err != nil { + return err + } + go func() { + defer l.Close() + if err := server.Serve(ctx, l); err != nil && + !strings.Contains(err.Error(), "use of closed network connection") { + logrus.WithError(err).Fatal("containerd-shim: ttrpc server failure") + } + }() + return nil +} + +func dumpStacks(logger *logrus.Entry) { + var ( + buf []byte + stackSize int + ) + bufferLen := 16384 + for stackSize == len(buf) { + buf = make([]byte, bufferLen) + stackSize = runtime.Stack(buf, true) + bufferLen *= 2 + } + buf = buf[:stackSize] + logger.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) +} + +type remoteEventsPublisher struct { + address string + containerdBinaryPath string + noReaper bool +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_darwin.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_darwin.go new file mode 100644 index 000000000..314b45cb5 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_darwin.go @@ -0,0 +1,29 @@ +// +build darwin + +/* + Copyright The containerd Authors. + + 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 shim + +import "github.com/containerd/ttrpc" + +func newServer() (*ttrpc.Server, error) { + return ttrpc.NewServer() +} + +func subreaper() error { + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_linux.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_linux.go new file mode 100644 index 000000000..7ad2a7262 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_linux.go @@ -0,0 +1,30 @@ +/* + Copyright The containerd Authors. + + 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 shim + +import ( + "github.com/containerd/containerd/sys" + "github.com/containerd/ttrpc" +) + +func newServer() (*ttrpc.Server, error) { + return ttrpc.NewServer(ttrpc.WithServerHandshaker(ttrpc.UnixSocketRequireSameUser())) +} + +func subreaper() error { + return sys.SetSubreaper(1) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_unix.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_unix.go new file mode 100644 index 000000000..08668dc1c --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_unix.go @@ -0,0 +1,130 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 shim + +import ( + "bytes" + "context" + "io" + "net" + "os" + "os/exec" + "os/signal" + "syscall" + + "github.com/containerd/containerd/events" + "github.com/containerd/containerd/namespaces" + "github.com/containerd/fifo" + "github.com/containerd/typeurl" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" +) + +// setupSignals creates a new signal handler for all signals and sets the shim as a +// sub-reaper so that the container processes are reparented +func setupSignals(config Config) (chan os.Signal, error) { + signals := make(chan os.Signal, 32) + smp := []os.Signal{unix.SIGTERM, unix.SIGINT, unix.SIGPIPE} + if !config.NoReaper { + smp = append(smp, unix.SIGCHLD) + } + signal.Notify(signals, smp...) + return signals, nil +} + +func setupDumpStacks(dump chan<- os.Signal) { + signal.Notify(dump, syscall.SIGUSR1) +} + +func serveListener(path string) (net.Listener, error) { + var ( + l net.Listener + err error + ) + if path == "" { + l, err = net.FileListener(os.NewFile(3, "socket")) + path = "[inherited from parent]" + } else { + if len(path) > 106 { + return nil, errors.Errorf("%q: unix socket path too long (> 106)", path) + } + l, err = net.Listen("unix", "\x00"+path) + } + if err != nil { + return nil, err + } + logrus.WithField("socket", path).Debug("serving api on abstract socket") + return l, nil +} + +func handleSignals(logger *logrus.Entry, signals chan os.Signal) error { + logger.Info("starting signal loop") + for { + select { + case s := <-signals: + switch s { + case unix.SIGCHLD: + if err := Reap(); err != nil { + logger.WithError(err).Error("reap exit status") + } + case unix.SIGPIPE: + } + } + } +} + +func openLog(ctx context.Context, _ string) (io.Writer, error) { + return fifo.OpenFifo(ctx, "log", unix.O_WRONLY, 0700) +} + +func (l *remoteEventsPublisher) Publish(ctx context.Context, topic string, event events.Event) error { + ns, _ := namespaces.Namespace(ctx) + encoded, err := typeurl.MarshalAny(event) + if err != nil { + return err + } + data, err := encoded.Marshal() + if err != nil { + return err + } + cmd := exec.CommandContext(ctx, l.containerdBinaryPath, "--address", l.address, "publish", "--topic", topic, "--namespace", ns) + cmd.Stdin = bytes.NewReader(data) + if l.noReaper { + if err := cmd.Start(); err != nil { + return err + } + if err := cmd.Wait(); err != nil { + return errors.Wrap(err, "failed to publish event") + } + return nil + } + c, err := Default.Start(cmd) + if err != nil { + return err + } + status, err := Default.Wait(cmd, c) + if err != nil { + return err + } + if status != 0 { + return errors.New("failed to publish event") + } + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_windows.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_windows.go new file mode 100644 index 000000000..3bac959db --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_windows.go @@ -0,0 +1,302 @@ +// +build windows + +/* + Copyright The containerd Authors. + + 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 shim + +import ( + "bytes" + "context" + "fmt" + "io" + "net" + "os" + "os/exec" + "sync" + "unsafe" + + winio "github.com/Microsoft/go-winio" + "github.com/containerd/containerd/events" + "github.com/containerd/containerd/namespaces" + "github.com/containerd/ttrpc" + "github.com/containerd/typeurl" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/windows" +) + +// setupSignals creates a new signal handler for all signals +func setupSignals(config Config) (chan os.Signal, error) { + signals := make(chan os.Signal, 32) + return signals, nil +} + +func newServer() (*ttrpc.Server, error) { + return ttrpc.NewServer() +} + +func subreaper() error { + return nil +} + +type fakeSignal struct { +} + +func (fs *fakeSignal) String() string { + return "" +} + +func (fs *fakeSignal) Signal() { +} + +func setupDumpStacks(dump chan<- os.Signal) { + // Windows does not support signals like *nix systems. So instead of + // trapping on SIGUSR1 to dump stacks, we wait on a Win32 event to be + // signaled. ACL'd to builtin administrators and local system + event := "Global\\containerd-shim-runhcs-v1-" + fmt.Sprint(os.Getpid()) + ev, _ := windows.UTF16PtrFromString(event) + sd, err := winio.SddlToSecurityDescriptor("D:P(A;;GA;;;BA)(A;;GA;;;SY)") + if err != nil { + logrus.Errorf("failed to get security descriptor for debug stackdump event %s: %s", event, err.Error()) + return + } + var sa windows.SecurityAttributes + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0])) + h, err := windows.CreateEvent(&sa, 0, 0, ev) + if h == 0 || err != nil { + logrus.Errorf("failed to create debug stackdump event %s: %s", event, err.Error()) + return + } + go func() { + logrus.Debugf("Stackdump - waiting signal at %s", event) + for { + windows.WaitForSingleObject(h, windows.INFINITE) + dump <- new(fakeSignal) + } + }() +} + +// serve serves the ttrpc API over a unix socket at the provided path +// this function does not block +func serveListener(path string) (net.Listener, error) { + if path == "" { + return nil, errors.New("'socket' must be npipe path") + } + l, err := winio.ListenPipe(path, nil) + if err != nil { + return nil, err + } + logrus.WithField("socket", path).Debug("serving api on npipe socket") + return l, nil +} + +func handleSignals(logger *logrus.Entry, signals chan os.Signal) error { + logger.Info("starting signal loop") + for { + select { + case s := <-signals: + switch s { + case os.Interrupt: + break + } + } + } +} + +var _ = (io.WriterTo)(&blockingBuffer{}) +var _ = (io.Writer)(&blockingBuffer{}) + +// blockingBuffer implements the `io.Writer` and `io.WriterTo` interfaces. Once +// `capacity` is reached the calls to `Write` will block until a successful call +// to `WriterTo` frees up the buffer space. +// +// Note: This has the same threadding semantics as bytes.Buffer with no +// additional locking so multithreading is not supported. +type blockingBuffer struct { + c *sync.Cond + + capacity int + + buffer bytes.Buffer +} + +func newBlockingBuffer(capacity int) *blockingBuffer { + return &blockingBuffer{ + c: sync.NewCond(&sync.Mutex{}), + capacity: capacity, + } +} + +func (bb *blockingBuffer) Len() int { + bb.c.L.Lock() + defer bb.c.L.Unlock() + return bb.buffer.Len() +} + +func (bb *blockingBuffer) Write(p []byte) (int, error) { + if len(p) > bb.capacity { + return 0, errors.Errorf("len(p) (%d) too large for capacity (%d)", len(p), bb.capacity) + } + + bb.c.L.Lock() + for bb.buffer.Len()+len(p) > bb.capacity { + bb.c.Wait() + } + defer bb.c.L.Unlock() + return bb.buffer.Write(p) +} + +func (bb *blockingBuffer) WriteTo(w io.Writer) (int64, error) { + bb.c.L.Lock() + defer bb.c.L.Unlock() + defer bb.c.Signal() + return bb.buffer.WriteTo(w) +} + +// deferredShimWriteLogger exists to solve the upstream loggin issue presented +// by using Windows Named Pipes for logging. When containerd restarts it tries +// to reconnect to any shims. This means that the connection to the logger will +// be severed but when containerd starts up it should reconnect and start +// logging again. We abstract all of this logic behind what looks like a simple +// `io.Writer` that can reconnect in the lifetime and buffers logs while +// disconnected. +type deferredShimWriteLogger struct { + mu sync.Mutex + + ctx context.Context + + connected bool + aborted bool + + buffer *blockingBuffer + + l net.Listener + c net.Conn + conerr error +} + +// beginAccept issues an accept to wait for a connection. Once a connection +// occurs drains any outstanding buffer. While draining the buffer any writes +// are blocked. If the buffer fails to fully drain due to a connection drop a +// call to `beginAccept` is re-issued waiting for another connection from +// containerd. +func (dswl *deferredShimWriteLogger) beginAccept() { + dswl.mu.Lock() + if dswl.connected { + return + } + dswl.mu.Unlock() + + c, err := dswl.l.Accept() + if err == winio.ErrPipeListenerClosed { + dswl.mu.Lock() + dswl.aborted = true + dswl.l.Close() + dswl.conerr = errors.New("connection closed") + dswl.mu.Unlock() + return + } + dswl.mu.Lock() + dswl.connected = true + dswl.c = c + + // Drain the buffer + if dswl.buffer.Len() > 0 { + _, err := dswl.buffer.WriteTo(dswl.c) + if err != nil { + // We lost our connection draining the buffer. + dswl.connected = false + dswl.c.Close() + go dswl.beginAccept() + } + } + dswl.mu.Unlock() +} + +func (dswl *deferredShimWriteLogger) Write(p []byte) (int, error) { + dswl.mu.Lock() + defer dswl.mu.Unlock() + + if dswl.aborted { + return 0, dswl.conerr + } + + if dswl.connected { + // We have a connection. beginAccept would have drained the buffer so we just write our data to + // the connection directly. + written, err := dswl.c.Write(p) + if err != nil { + // We lost the connection. + dswl.connected = false + dswl.c.Close() + go dswl.beginAccept() + + // We weren't able to write the full `p` bytes. Buffer the rest + if written != len(p) { + w, err := dswl.buffer.Write(p[written:]) + if err != nil { + // We failed to buffer. Return this error + return written + w, err + } + written += w + } + } + + return written, nil + } + + // We are disconnected. Buffer the contents. + return dswl.buffer.Write(p) +} + +// openLog on Windows acts as the server of the log pipe. This allows the +// containerd daemon to independently restart and reconnect to the logs. +func openLog(ctx context.Context, id string) (io.Writer, error) { + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return nil, err + } + + dswl := &deferredShimWriteLogger{ + ctx: ctx, + buffer: newBlockingBuffer(64 * 1024), // 64KB, + } + l, err := winio.ListenPipe(fmt.Sprintf("\\\\.\\pipe\\containerd-shim-%s-%s-log", ns, id), nil) + if err != nil { + return nil, err + } + dswl.l = l + go dswl.beginAccept() + return dswl, nil +} + +func (l *remoteEventsPublisher) Publish(ctx context.Context, topic string, event events.Event) error { + ns, _ := namespaces.Namespace(ctx) + encoded, err := typeurl.MarshalAny(event) + if err != nil { + return err + } + data, err := encoded.Marshal() + if err != nil { + return err + } + cmd := exec.CommandContext(ctx, l.containerdBinaryPath, "--address", l.address, "publish", "--topic", topic, "--namespace", ns) + cmd.Stdin = bytes.NewReader(data) + return cmd.Run() +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/util.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/util.go new file mode 100644 index 000000000..77afaa1f3 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/util.go @@ -0,0 +1,142 @@ +/* + Copyright The containerd Authors. + + 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 shim + +import ( + "context" + "fmt" + "net" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/containerd/containerd/namespaces" + "github.com/pkg/errors" +) + +const shimBinaryFormat = "containerd-shim-%s-%s" + +var runtimePaths sync.Map + +// Command returns the shim command with the provided args and configuration +func Command(ctx context.Context, runtime, containerdAddress, path string, cmdArgs ...string) (*exec.Cmd, error) { + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return nil, err + } + self, err := os.Executable() + if err != nil { + return nil, err + } + args := []string{ + "-namespace", ns, + "-address", containerdAddress, + "-publish-binary", self, + } + args = append(args, cmdArgs...) + name := BinaryName(runtime) + if name == "" { + return nil, fmt.Errorf("invalid runtime name %s, correct runtime name should format like io.containerd.runc.v1", runtime) + } + + var cmdPath string + cmdPathI, cmdPathFound := runtimePaths.Load(name) + if cmdPathFound { + cmdPath = cmdPathI.(string) + } else { + var lerr error + if cmdPath, lerr = exec.LookPath(name); lerr != nil { + if eerr, ok := lerr.(*exec.Error); ok { + if eerr.Err == exec.ErrNotFound { + return nil, errors.Wrapf(os.ErrNotExist, "runtime %q binary not installed %q", runtime, name) + } + } + } + cmdPath, err = filepath.Abs(cmdPath) + if err != nil { + return nil, err + } + if cmdPathI, cmdPathFound = runtimePaths.LoadOrStore(name, cmdPath); cmdPathFound { + // We didn't store cmdPath we loaded an already cached value. Use it. + cmdPath = cmdPathI.(string) + } + } + + cmd := exec.Command(cmdPath, args...) + cmd.Dir = path + cmd.Env = append(os.Environ(), "GOMAXPROCS=2") + cmd.SysProcAttr = getSysProcAttr() + return cmd, nil +} + +// BinaryName returns the shim binary name from the runtime name, +// empty string returns means runtime name is invalid +func BinaryName(runtime string) string { + // runtime name should format like $prefix.name.version + parts := strings.Split(runtime, ".") + if len(parts) < 2 { + return "" + } + + return fmt.Sprintf(shimBinaryFormat, parts[len(parts)-2], parts[len(parts)-1]) +} + +// Connect to the provided address +func Connect(address string, d func(string, time.Duration) (net.Conn, error)) (net.Conn, error) { + return d(address, 100*time.Second) +} + +// WritePidFile writes a pid file atomically +func WritePidFile(path string, pid int) error { + path, err := filepath.Abs(path) + if err != nil { + return err + } + tempPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path))) + f, err := os.OpenFile(tempPath, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666) + if err != nil { + return err + } + _, err = fmt.Fprintf(f, "%d", pid) + f.Close() + if err != nil { + return err + } + return os.Rename(tempPath, path) +} + +// WriteAddress writes a address file atomically +func WriteAddress(path, address string) error { + path, err := filepath.Abs(path) + if err != nil { + return err + } + tempPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path))) + f, err := os.OpenFile(tempPath, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666) + if err != nil { + return err + } + _, err = f.WriteString(address) + f.Close() + if err != nil { + return err + } + return os.Rename(tempPath, path) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/util_unix.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/util_unix.go new file mode 100644 index 000000000..262fe2b36 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/util_unix.go @@ -0,0 +1,70 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 shim + +import ( + "context" + "net" + "path/filepath" + "strings" + "syscall" + "time" + + "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/sys" + "github.com/pkg/errors" +) + +func getSysProcAttr() *syscall.SysProcAttr { + return &syscall.SysProcAttr{ + Setpgid: true, + } +} + +// SetScore sets the oom score for a process +func SetScore(pid int) error { + return sys.SetOOMScore(pid, sys.OOMScoreMaxKillable) +} + +// SocketAddress returns an abstract socket address +func SocketAddress(ctx context.Context, id string) (string, error) { + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return "", err + } + return filepath.Join(string(filepath.Separator), "containerd-shim", ns, id, "shim.sock"), nil +} + +// AnonDialer returns a dialer for an abstract socket +func AnonDialer(address string, timeout time.Duration) (net.Conn, error) { + address = strings.TrimPrefix(address, "unix://") + return net.DialTimeout("unix", "\x00"+address, timeout) +} + +// NewSocket returns a new socket +func NewSocket(address string) (*net.UnixListener, error) { + if len(address) > 106 { + return nil, errors.Errorf("%q: unix socket path too long (> 106)", address) + } + l, err := net.Listen("unix", "\x00"+address) + if err != nil { + return nil, errors.Wrapf(err, "failed to listen to abstract unix socket %q", address) + } + return l.(*net.UnixListener), nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/util_windows.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/util_windows.go new file mode 100644 index 000000000..986fc754b --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/shim/util_windows.go @@ -0,0 +1,90 @@ +/* + Copyright The containerd Authors. + + 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 shim + +import ( + "context" + "fmt" + "net" + "os" + "syscall" + "time" + + winio "github.com/Microsoft/go-winio" + "github.com/containerd/containerd/namespaces" + "github.com/pkg/errors" +) + +func getSysProcAttr() *syscall.SysProcAttr { + return nil +} + +// SetScore sets the oom score for a process +func SetScore(pid int) error { + return nil +} + +// SocketAddress returns a npipe address +func SocketAddress(ctx context.Context, id string) (string, error) { + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return "", err + } + return fmt.Sprintf("\\\\.\\pipe\\containerd-shim-%s-%s-pipe", ns, id), nil +} + +// AnonDialer returns a dialer for a npipe +func AnonDialer(address string, timeout time.Duration) (net.Conn, error) { + var c net.Conn + var lastError error + timedOutError := errors.Errorf("timed out waiting for npipe %s", address) + start := time.Now() + for { + remaining := timeout - time.Now().Sub(start) + if remaining <= 0 { + lastError = timedOutError + break + } + c, lastError = winio.DialPipe(address, &remaining) + if lastError == nil { + break + } + if !os.IsNotExist(lastError) { + break + } + // There is nobody serving the pipe. We limit the timeout for this case + // to 5 seconds because any shim that would serve this endpoint should + // serve it within 5 seconds. We use the passed in timeout for the + // `DialPipe` timeout if the pipe exists however to give the pipe time + // to `Accept` the connection. + if time.Now().Sub(start) >= 5*time.Second { + lastError = timedOutError + break + } + time.Sleep(10 * time.Millisecond) + } + return c, lastError +} + +// NewSocket returns a new npipe listener +func NewSocket(address string) (net.Listener, error) { + l, err := winio.ListenPipe(address, nil) + if err != nil { + return nil, errors.Wrapf(err, "failed to listen to npipe %s", address) + } + return l, nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/task/doc.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/task/doc.go new file mode 100644 index 000000000..f933dd8d4 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/task/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + 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 task diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/task/shim.pb.go b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/task/shim.pb.go new file mode 100644 index 000000000..9caefd758 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/task/shim.pb.go @@ -0,0 +1,5693 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/runtime/v2/task/shim.proto + +/* + Package task is a generated protocol buffer package. + + It is generated from these files: + github.com/containerd/containerd/runtime/v2/task/shim.proto + + It has these top-level messages: + CreateTaskRequest + CreateTaskResponse + DeleteRequest + DeleteResponse + ExecProcessRequest + ExecProcessResponse + ResizePtyRequest + StateRequest + StateResponse + KillRequest + CloseIORequest + PidsRequest + PidsResponse + CheckpointTaskRequest + UpdateTaskRequest + StartRequest + StartResponse + WaitRequest + WaitResponse + StatsRequest + StatsResponse + ConnectRequest + ConnectResponse + ShutdownRequest + PauseRequest + ResumeRequest +*/ +package task + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import google_protobuf "github.com/gogo/protobuf/types" +import google_protobuf1 "github.com/gogo/protobuf/types" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" +import _ "github.com/gogo/protobuf/types" +import containerd_types "github.com/containerd/containerd/api/types" +import containerd_v1_types "github.com/containerd/containerd/api/types/task" + +import time "time" + +import types "github.com/gogo/protobuf/types" + +import strings "strings" +import reflect "reflect" + +import context "context" +import ttrpc "github.com/containerd/ttrpc" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type CreateTaskRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Bundle string `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` + Rootfs []*containerd_types.Mount `protobuf:"bytes,3,rep,name=rootfs" json:"rootfs,omitempty"` + Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` + Stdin string `protobuf:"bytes,5,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,6,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,7,opt,name=stderr,proto3" json:"stderr,omitempty"` + Checkpoint string `protobuf:"bytes,8,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` + ParentCheckpoint string `protobuf:"bytes,9,opt,name=parent_checkpoint,json=parentCheckpoint,proto3" json:"parent_checkpoint,omitempty"` + Options *google_protobuf.Any `protobuf:"bytes,10,opt,name=options" json:"options,omitempty"` +} + +func (m *CreateTaskRequest) Reset() { *m = CreateTaskRequest{} } +func (*CreateTaskRequest) ProtoMessage() {} +func (*CreateTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{0} } + +type CreateTaskResponse struct { + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (m *CreateTaskResponse) Reset() { *m = CreateTaskResponse{} } +func (*CreateTaskResponse) ProtoMessage() {} +func (*CreateTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{1} } + +type DeleteRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` +} + +func (m *DeleteRequest) Reset() { *m = DeleteRequest{} } +func (*DeleteRequest) ProtoMessage() {} +func (*DeleteRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{2} } + +type DeleteResponse struct { + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + ExitStatus uint32 `protobuf:"varint,2,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,3,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` +} + +func (m *DeleteResponse) Reset() { *m = DeleteResponse{} } +func (*DeleteResponse) ProtoMessage() {} +func (*DeleteResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{3} } + +type ExecProcessRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Terminal bool `protobuf:"varint,3,opt,name=terminal,proto3" json:"terminal,omitempty"` + Stdin string `protobuf:"bytes,4,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,5,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,6,opt,name=stderr,proto3" json:"stderr,omitempty"` + Spec *google_protobuf.Any `protobuf:"bytes,7,opt,name=spec" json:"spec,omitempty"` +} + +func (m *ExecProcessRequest) Reset() { *m = ExecProcessRequest{} } +func (*ExecProcessRequest) ProtoMessage() {} +func (*ExecProcessRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{4} } + +type ExecProcessResponse struct { +} + +func (m *ExecProcessResponse) Reset() { *m = ExecProcessResponse{} } +func (*ExecProcessResponse) ProtoMessage() {} +func (*ExecProcessResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{5} } + +type ResizePtyRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Width uint32 `protobuf:"varint,3,opt,name=width,proto3" json:"width,omitempty"` + Height uint32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *ResizePtyRequest) Reset() { *m = ResizePtyRequest{} } +func (*ResizePtyRequest) ProtoMessage() {} +func (*ResizePtyRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{6} } + +type StateRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` +} + +func (m *StateRequest) Reset() { *m = StateRequest{} } +func (*StateRequest) ProtoMessage() {} +func (*StateRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{7} } + +type StateResponse struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Bundle string `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` + Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` + Status containerd_v1_types.Status `protobuf:"varint,4,opt,name=status,proto3,enum=containerd.v1.types.Status" json:"status,omitempty"` + Stdin string `protobuf:"bytes,5,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,6,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,7,opt,name=stderr,proto3" json:"stderr,omitempty"` + Terminal bool `protobuf:"varint,8,opt,name=terminal,proto3" json:"terminal,omitempty"` + ExitStatus uint32 `protobuf:"varint,9,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,10,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` +} + +func (m *StateResponse) Reset() { *m = StateResponse{} } +func (*StateResponse) ProtoMessage() {} +func (*StateResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{8} } + +type KillRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Signal uint32 `protobuf:"varint,3,opt,name=signal,proto3" json:"signal,omitempty"` + All bool `protobuf:"varint,4,opt,name=all,proto3" json:"all,omitempty"` +} + +func (m *KillRequest) Reset() { *m = KillRequest{} } +func (*KillRequest) ProtoMessage() {} +func (*KillRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{9} } + +type CloseIORequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Stdin bool `protobuf:"varint,3,opt,name=stdin,proto3" json:"stdin,omitempty"` +} + +func (m *CloseIORequest) Reset() { *m = CloseIORequest{} } +func (*CloseIORequest) ProtoMessage() {} +func (*CloseIORequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{10} } + +type PidsRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (m *PidsRequest) Reset() { *m = PidsRequest{} } +func (*PidsRequest) ProtoMessage() {} +func (*PidsRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{11} } + +type PidsResponse struct { + Processes []*containerd_v1_types.ProcessInfo `protobuf:"bytes,1,rep,name=processes" json:"processes,omitempty"` +} + +func (m *PidsResponse) Reset() { *m = PidsResponse{} } +func (*PidsResponse) ProtoMessage() {} +func (*PidsResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{12} } + +type CheckpointTaskRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Options *google_protobuf.Any `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` +} + +func (m *CheckpointTaskRequest) Reset() { *m = CheckpointTaskRequest{} } +func (*CheckpointTaskRequest) ProtoMessage() {} +func (*CheckpointTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{13} } + +type UpdateTaskRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Resources *google_protobuf.Any `protobuf:"bytes,2,opt,name=resources" json:"resources,omitempty"` +} + +func (m *UpdateTaskRequest) Reset() { *m = UpdateTaskRequest{} } +func (*UpdateTaskRequest) ProtoMessage() {} +func (*UpdateTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{14} } + +type StartRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` +} + +func (m *StartRequest) Reset() { *m = StartRequest{} } +func (*StartRequest) ProtoMessage() {} +func (*StartRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{15} } + +type StartResponse struct { + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` +} + +func (m *StartResponse) Reset() { *m = StartResponse{} } +func (*StartResponse) ProtoMessage() {} +func (*StartResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{16} } + +type WaitRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` +} + +func (m *WaitRequest) Reset() { *m = WaitRequest{} } +func (*WaitRequest) ProtoMessage() {} +func (*WaitRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{17} } + +type WaitResponse struct { + ExitStatus uint32 `protobuf:"varint,1,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,2,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` +} + +func (m *WaitResponse) Reset() { *m = WaitResponse{} } +func (*WaitResponse) ProtoMessage() {} +func (*WaitResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{18} } + +type StatsRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (m *StatsRequest) Reset() { *m = StatsRequest{} } +func (*StatsRequest) ProtoMessage() {} +func (*StatsRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{19} } + +type StatsResponse struct { + Stats *google_protobuf.Any `protobuf:"bytes,1,opt,name=stats" json:"stats,omitempty"` +} + +func (m *StatsResponse) Reset() { *m = StatsResponse{} } +func (*StatsResponse) ProtoMessage() {} +func (*StatsResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{20} } + +type ConnectRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (m *ConnectRequest) Reset() { *m = ConnectRequest{} } +func (*ConnectRequest) ProtoMessage() {} +func (*ConnectRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{21} } + +type ConnectResponse struct { + ShimPid uint32 `protobuf:"varint,1,opt,name=shim_pid,json=shimPid,proto3" json:"shim_pid,omitempty"` + TaskPid uint32 `protobuf:"varint,2,opt,name=task_pid,json=taskPid,proto3" json:"task_pid,omitempty"` + Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` +} + +func (m *ConnectResponse) Reset() { *m = ConnectResponse{} } +func (*ConnectResponse) ProtoMessage() {} +func (*ConnectResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{22} } + +type ShutdownRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Now bool `protobuf:"varint,2,opt,name=now,proto3" json:"now,omitempty"` +} + +func (m *ShutdownRequest) Reset() { *m = ShutdownRequest{} } +func (*ShutdownRequest) ProtoMessage() {} +func (*ShutdownRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{23} } + +type PauseRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (m *PauseRequest) Reset() { *m = PauseRequest{} } +func (*PauseRequest) ProtoMessage() {} +func (*PauseRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{24} } + +type ResumeRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (m *ResumeRequest) Reset() { *m = ResumeRequest{} } +func (*ResumeRequest) ProtoMessage() {} +func (*ResumeRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{25} } + +func init() { + proto.RegisterType((*CreateTaskRequest)(nil), "containerd.task.v2.CreateTaskRequest") + proto.RegisterType((*CreateTaskResponse)(nil), "containerd.task.v2.CreateTaskResponse") + proto.RegisterType((*DeleteRequest)(nil), "containerd.task.v2.DeleteRequest") + proto.RegisterType((*DeleteResponse)(nil), "containerd.task.v2.DeleteResponse") + proto.RegisterType((*ExecProcessRequest)(nil), "containerd.task.v2.ExecProcessRequest") + proto.RegisterType((*ExecProcessResponse)(nil), "containerd.task.v2.ExecProcessResponse") + proto.RegisterType((*ResizePtyRequest)(nil), "containerd.task.v2.ResizePtyRequest") + proto.RegisterType((*StateRequest)(nil), "containerd.task.v2.StateRequest") + proto.RegisterType((*StateResponse)(nil), "containerd.task.v2.StateResponse") + proto.RegisterType((*KillRequest)(nil), "containerd.task.v2.KillRequest") + proto.RegisterType((*CloseIORequest)(nil), "containerd.task.v2.CloseIORequest") + proto.RegisterType((*PidsRequest)(nil), "containerd.task.v2.PidsRequest") + proto.RegisterType((*PidsResponse)(nil), "containerd.task.v2.PidsResponse") + proto.RegisterType((*CheckpointTaskRequest)(nil), "containerd.task.v2.CheckpointTaskRequest") + proto.RegisterType((*UpdateTaskRequest)(nil), "containerd.task.v2.UpdateTaskRequest") + proto.RegisterType((*StartRequest)(nil), "containerd.task.v2.StartRequest") + proto.RegisterType((*StartResponse)(nil), "containerd.task.v2.StartResponse") + proto.RegisterType((*WaitRequest)(nil), "containerd.task.v2.WaitRequest") + proto.RegisterType((*WaitResponse)(nil), "containerd.task.v2.WaitResponse") + proto.RegisterType((*StatsRequest)(nil), "containerd.task.v2.StatsRequest") + proto.RegisterType((*StatsResponse)(nil), "containerd.task.v2.StatsResponse") + proto.RegisterType((*ConnectRequest)(nil), "containerd.task.v2.ConnectRequest") + proto.RegisterType((*ConnectResponse)(nil), "containerd.task.v2.ConnectResponse") + proto.RegisterType((*ShutdownRequest)(nil), "containerd.task.v2.ShutdownRequest") + proto.RegisterType((*PauseRequest)(nil), "containerd.task.v2.PauseRequest") + proto.RegisterType((*ResumeRequest)(nil), "containerd.task.v2.ResumeRequest") +} +func (m *CreateTaskRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CreateTaskRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.Bundle) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Bundle))) + i += copy(dAtA[i:], m.Bundle) + } + if len(m.Rootfs) > 0 { + for _, msg := range m.Rootfs { + dAtA[i] = 0x1a + i++ + i = encodeVarintShim(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.Terminal { + dAtA[i] = 0x20 + i++ + if m.Terminal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if len(m.Stdin) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Stdin))) + i += copy(dAtA[i:], m.Stdin) + } + if len(m.Stdout) > 0 { + dAtA[i] = 0x32 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Stdout))) + i += copy(dAtA[i:], m.Stdout) + } + if len(m.Stderr) > 0 { + dAtA[i] = 0x3a + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Stderr))) + i += copy(dAtA[i:], m.Stderr) + } + if len(m.Checkpoint) > 0 { + dAtA[i] = 0x42 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Checkpoint))) + i += copy(dAtA[i:], m.Checkpoint) + } + if len(m.ParentCheckpoint) > 0 { + dAtA[i] = 0x4a + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ParentCheckpoint))) + i += copy(dAtA[i:], m.ParentCheckpoint) + } + if m.Options != nil { + dAtA[i] = 0x52 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Options.Size())) + n1, err := m.Options.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + } + return i, nil +} + +func (m *CreateTaskResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CreateTaskResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Pid != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Pid)) + } + return i, nil +} + +func (m *DeleteRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DeleteRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.ExecID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + return i, nil +} + +func (m *DeleteResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DeleteResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Pid != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Pid)) + } + if m.ExitStatus != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.ExitStatus)) + } + dAtA[i] = 0x1a + i++ + i = encodeVarintShim(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) + n2, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + return i, nil +} + +func (m *ExecProcessRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecProcessRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.ExecID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + if m.Terminal { + dAtA[i] = 0x18 + i++ + if m.Terminal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if len(m.Stdin) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Stdin))) + i += copy(dAtA[i:], m.Stdin) + } + if len(m.Stdout) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Stdout))) + i += copy(dAtA[i:], m.Stdout) + } + if len(m.Stderr) > 0 { + dAtA[i] = 0x32 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Stderr))) + i += copy(dAtA[i:], m.Stderr) + } + if m.Spec != nil { + dAtA[i] = 0x3a + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Spec.Size())) + n3, err := m.Spec.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n3 + } + return i, nil +} + +func (m *ExecProcessResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExecProcessResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + return i, nil +} + +func (m *ResizePtyRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResizePtyRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.ExecID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + if m.Width != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Width)) + } + if m.Height != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Height)) + } + return i, nil +} + +func (m *StateRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StateRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.ExecID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + return i, nil +} + +func (m *StateResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StateResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.Bundle) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Bundle))) + i += copy(dAtA[i:], m.Bundle) + } + if m.Pid != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Pid)) + } + if m.Status != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Status)) + } + if len(m.Stdin) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Stdin))) + i += copy(dAtA[i:], m.Stdin) + } + if len(m.Stdout) > 0 { + dAtA[i] = 0x32 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Stdout))) + i += copy(dAtA[i:], m.Stdout) + } + if len(m.Stderr) > 0 { + dAtA[i] = 0x3a + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Stderr))) + i += copy(dAtA[i:], m.Stderr) + } + if m.Terminal { + dAtA[i] = 0x40 + i++ + if m.Terminal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.ExitStatus != 0 { + dAtA[i] = 0x48 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.ExitStatus)) + } + dAtA[i] = 0x52 + i++ + i = encodeVarintShim(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) + n4, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n4 + return i, nil +} + +func (m *KillRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KillRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.ExecID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + if m.Signal != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Signal)) + } + if m.All { + dAtA[i] = 0x20 + i++ + if m.All { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + return i, nil +} + +func (m *CloseIORequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CloseIORequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.ExecID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + if m.Stdin { + dAtA[i] = 0x18 + i++ + if m.Stdin { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + return i, nil +} + +func (m *PidsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PidsRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + return i, nil +} + +func (m *PidsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PidsResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Processes) > 0 { + for _, msg := range m.Processes { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *CheckpointTaskRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CheckpointTaskRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.Path) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Path))) + i += copy(dAtA[i:], m.Path) + } + if m.Options != nil { + dAtA[i] = 0x1a + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Options.Size())) + n5, err := m.Options.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n5 + } + return i, nil +} + +func (m *UpdateTaskRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UpdateTaskRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if m.Resources != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Resources.Size())) + n6, err := m.Resources.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n6 + } + return i, nil +} + +func (m *StartRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StartRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.ExecID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + return i, nil +} + +func (m *StartResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StartResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Pid != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Pid)) + } + return i, nil +} + +func (m *WaitRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WaitRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if len(m.ExecID) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + return i, nil +} + +func (m *WaitResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *WaitResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.ExitStatus != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.ExitStatus)) + } + dAtA[i] = 0x12 + i++ + i = encodeVarintShim(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) + n7, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n7 + return i, nil +} + +func (m *StatsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StatsRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + return i, nil +} + +func (m *StatsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StatsResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Stats != nil { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(m.Stats.Size())) + n8, err := m.Stats.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n8 + } + return i, nil +} + +func (m *ConnectRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConnectRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + return i, nil +} + +func (m *ConnectResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConnectResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.ShimPid != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.ShimPid)) + } + if m.TaskPid != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintShim(dAtA, i, uint64(m.TaskPid)) + } + if len(m.Version) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.Version))) + i += copy(dAtA[i:], m.Version) + } + return i, nil +} + +func (m *ShutdownRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ShutdownRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + if m.Now { + dAtA[i] = 0x10 + i++ + if m.Now { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + return i, nil +} + +func (m *PauseRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PauseRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + return i, nil +} + +func (m *ResumeRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResumeRequest) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ID) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i += copy(dAtA[i:], m.ID) + } + return i, nil +} + +func encodeVarintShim(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *CreateTaskRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.Bundle) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if len(m.Rootfs) > 0 { + for _, e := range m.Rootfs { + l = e.Size() + n += 1 + l + sovShim(uint64(l)) + } + } + if m.Terminal { + n += 2 + } + l = len(m.Stdin) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.Stdout) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.Stderr) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.Checkpoint) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.ParentCheckpoint) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if m.Options != nil { + l = m.Options.Size() + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *CreateTaskResponse) Size() (n int) { + var l int + _ = l + if m.Pid != 0 { + n += 1 + sovShim(uint64(m.Pid)) + } + return n +} + +func (m *DeleteRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *DeleteResponse) Size() (n int) { + var l int + _ = l + if m.Pid != 0 { + n += 1 + sovShim(uint64(m.Pid)) + } + if m.ExitStatus != 0 { + n += 1 + sovShim(uint64(m.ExitStatus)) + } + l = types.SizeOfStdTime(m.ExitedAt) + n += 1 + l + sovShim(uint64(l)) + return n +} + +func (m *ExecProcessRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if m.Terminal { + n += 2 + } + l = len(m.Stdin) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.Stdout) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.Stderr) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if m.Spec != nil { + l = m.Spec.Size() + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *ExecProcessResponse) Size() (n int) { + var l int + _ = l + return n +} + +func (m *ResizePtyRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if m.Width != 0 { + n += 1 + sovShim(uint64(m.Width)) + } + if m.Height != 0 { + n += 1 + sovShim(uint64(m.Height)) + } + return n +} + +func (m *StateRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *StateResponse) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.Bundle) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if m.Pid != 0 { + n += 1 + sovShim(uint64(m.Pid)) + } + if m.Status != 0 { + n += 1 + sovShim(uint64(m.Status)) + } + l = len(m.Stdin) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.Stdout) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.Stderr) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if m.Terminal { + n += 2 + } + if m.ExitStatus != 0 { + n += 1 + sovShim(uint64(m.ExitStatus)) + } + l = types.SizeOfStdTime(m.ExitedAt) + n += 1 + l + sovShim(uint64(l)) + return n +} + +func (m *KillRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if m.Signal != 0 { + n += 1 + sovShim(uint64(m.Signal)) + } + if m.All { + n += 2 + } + return n +} + +func (m *CloseIORequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if m.Stdin { + n += 2 + } + return n +} + +func (m *PidsRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *PidsResponse) Size() (n int) { + var l int + _ = l + if len(m.Processes) > 0 { + for _, e := range m.Processes { + l = e.Size() + n += 1 + l + sovShim(uint64(l)) + } + } + return n +} + +func (m *CheckpointTaskRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.Path) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if m.Options != nil { + l = m.Options.Size() + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *UpdateTaskRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if m.Resources != nil { + l = m.Resources.Size() + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *StartRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *StartResponse) Size() (n int) { + var l int + _ = l + if m.Pid != 0 { + n += 1 + sovShim(uint64(m.Pid)) + } + return n +} + +func (m *WaitRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *WaitResponse) Size() (n int) { + var l int + _ = l + if m.ExitStatus != 0 { + n += 1 + sovShim(uint64(m.ExitStatus)) + } + l = types.SizeOfStdTime(m.ExitedAt) + n += 1 + l + sovShim(uint64(l)) + return n +} + +func (m *StatsRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *StatsResponse) Size() (n int) { + var l int + _ = l + if m.Stats != nil { + l = m.Stats.Size() + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *ConnectRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *ConnectResponse) Size() (n int) { + var l int + _ = l + if m.ShimPid != 0 { + n += 1 + sovShim(uint64(m.ShimPid)) + } + if m.TaskPid != 0 { + n += 1 + sovShim(uint64(m.TaskPid)) + } + l = len(m.Version) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *ShutdownRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + if m.Now { + n += 2 + } + return n +} + +func (m *PauseRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func (m *ResumeRequest) Size() (n int) { + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovShim(uint64(l)) + } + return n +} + +func sovShim(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozShim(x uint64) (n int) { + return sovShim(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *CreateTaskRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateTaskRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Bundle:` + fmt.Sprintf("%v", this.Bundle) + `,`, + `Rootfs:` + strings.Replace(fmt.Sprintf("%v", this.Rootfs), "Mount", "containerd_types.Mount", 1) + `,`, + `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, + `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, + `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, + `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, + `Checkpoint:` + fmt.Sprintf("%v", this.Checkpoint) + `,`, + `ParentCheckpoint:` + fmt.Sprintf("%v", this.ParentCheckpoint) + `,`, + `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "google_protobuf.Any", 1) + `,`, + `}`, + }, "") + return s +} +func (this *CreateTaskResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateTaskResponse{`, + `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `}`, + }, "") + return s +} +func (this *DeleteRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&DeleteRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `}`, + }, "") + return s +} +func (this *DeleteResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&DeleteResponse{`, + `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *ExecProcessRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ExecProcessRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, + `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, + `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, + `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "Any", "google_protobuf.Any", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ExecProcessResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ExecProcessResponse{`, + `}`, + }, "") + return s +} +func (this *ResizePtyRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ResizePtyRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `Width:` + fmt.Sprintf("%v", this.Width) + `,`, + `Height:` + fmt.Sprintf("%v", this.Height) + `,`, + `}`, + }, "") + return s +} +func (this *StateRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&StateRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `}`, + }, "") + return s +} +func (this *StateResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&StateResponse{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Bundle:` + fmt.Sprintf("%v", this.Bundle) + `,`, + `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `Status:` + fmt.Sprintf("%v", this.Status) + `,`, + `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, + `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, + `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, + `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, + `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *KillRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&KillRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `Signal:` + fmt.Sprintf("%v", this.Signal) + `,`, + `All:` + fmt.Sprintf("%v", this.All) + `,`, + `}`, + }, "") + return s +} +func (this *CloseIORequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CloseIORequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, + `}`, + }, "") + return s +} +func (this *PidsRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PidsRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `}`, + }, "") + return s +} +func (this *PidsResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PidsResponse{`, + `Processes:` + strings.Replace(fmt.Sprintf("%v", this.Processes), "ProcessInfo", "containerd_v1_types.ProcessInfo", 1) + `,`, + `}`, + }, "") + return s +} +func (this *CheckpointTaskRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CheckpointTaskRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Path:` + fmt.Sprintf("%v", this.Path) + `,`, + `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "google_protobuf.Any", 1) + `,`, + `}`, + }, "") + return s +} +func (this *UpdateTaskRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateTaskRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Resources:` + strings.Replace(fmt.Sprintf("%v", this.Resources), "Any", "google_protobuf.Any", 1) + `,`, + `}`, + }, "") + return s +} +func (this *StartRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&StartRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `}`, + }, "") + return s +} +func (this *StartResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&StartResponse{`, + `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `}`, + }, "") + return s +} +func (this *WaitRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&WaitRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `}`, + }, "") + return s +} +func (this *WaitResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&WaitResponse{`, + `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *StatsRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&StatsRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `}`, + }, "") + return s +} +func (this *StatsResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&StatsResponse{`, + `Stats:` + strings.Replace(fmt.Sprintf("%v", this.Stats), "Any", "google_protobuf.Any", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ConnectRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ConnectRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `}`, + }, "") + return s +} +func (this *ConnectResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ConnectResponse{`, + `ShimPid:` + fmt.Sprintf("%v", this.ShimPid) + `,`, + `TaskPid:` + fmt.Sprintf("%v", this.TaskPid) + `,`, + `Version:` + fmt.Sprintf("%v", this.Version) + `,`, + `}`, + }, "") + return s +} +func (this *ShutdownRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ShutdownRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Now:` + fmt.Sprintf("%v", this.Now) + `,`, + `}`, + }, "") + return s +} +func (this *PauseRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PauseRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `}`, + }, "") + return s +} +func (this *ResumeRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ResumeRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `}`, + }, "") + return s +} +func valueToStringShim(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} + +type TaskService interface { + State(ctx context.Context, req *StateRequest) (*StateResponse, error) + Create(ctx context.Context, req *CreateTaskRequest) (*CreateTaskResponse, error) + Start(ctx context.Context, req *StartRequest) (*StartResponse, error) + Delete(ctx context.Context, req *DeleteRequest) (*DeleteResponse, error) + Pids(ctx context.Context, req *PidsRequest) (*PidsResponse, error) + Pause(ctx context.Context, req *PauseRequest) (*google_protobuf1.Empty, error) + Resume(ctx context.Context, req *ResumeRequest) (*google_protobuf1.Empty, error) + Checkpoint(ctx context.Context, req *CheckpointTaskRequest) (*google_protobuf1.Empty, error) + Kill(ctx context.Context, req *KillRequest) (*google_protobuf1.Empty, error) + Exec(ctx context.Context, req *ExecProcessRequest) (*google_protobuf1.Empty, error) + ResizePty(ctx context.Context, req *ResizePtyRequest) (*google_protobuf1.Empty, error) + CloseIO(ctx context.Context, req *CloseIORequest) (*google_protobuf1.Empty, error) + Update(ctx context.Context, req *UpdateTaskRequest) (*google_protobuf1.Empty, error) + Wait(ctx context.Context, req *WaitRequest) (*WaitResponse, error) + Stats(ctx context.Context, req *StatsRequest) (*StatsResponse, error) + Connect(ctx context.Context, req *ConnectRequest) (*ConnectResponse, error) + Shutdown(ctx context.Context, req *ShutdownRequest) (*google_protobuf1.Empty, error) +} + +func RegisterTaskService(srv *ttrpc.Server, svc TaskService) { + srv.Register("containerd.task.v2.Task", map[string]ttrpc.Method{ + "State": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req StateRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.State(ctx, &req) + }, + "Create": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req CreateTaskRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Create(ctx, &req) + }, + "Start": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req StartRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Start(ctx, &req) + }, + "Delete": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req DeleteRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Delete(ctx, &req) + }, + "Pids": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req PidsRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Pids(ctx, &req) + }, + "Pause": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req PauseRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Pause(ctx, &req) + }, + "Resume": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req ResumeRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Resume(ctx, &req) + }, + "Checkpoint": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req CheckpointTaskRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Checkpoint(ctx, &req) + }, + "Kill": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req KillRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Kill(ctx, &req) + }, + "Exec": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req ExecProcessRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Exec(ctx, &req) + }, + "ResizePty": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req ResizePtyRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.ResizePty(ctx, &req) + }, + "CloseIO": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req CloseIORequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.CloseIO(ctx, &req) + }, + "Update": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req UpdateTaskRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Update(ctx, &req) + }, + "Wait": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req WaitRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Wait(ctx, &req) + }, + "Stats": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req StatsRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Stats(ctx, &req) + }, + "Connect": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req ConnectRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Connect(ctx, &req) + }, + "Shutdown": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { + var req ShutdownRequest + if err := unmarshal(&req); err != nil { + return nil, err + } + return svc.Shutdown(ctx, &req) + }, + }) +} + +type taskClient struct { + client *ttrpc.Client +} + +func NewTaskClient(client *ttrpc.Client) TaskService { + return &taskClient{ + client: client, + } +} + +func (c *taskClient) State(ctx context.Context, req *StateRequest) (*StateResponse, error) { + var resp StateResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "State", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Create(ctx context.Context, req *CreateTaskRequest) (*CreateTaskResponse, error) { + var resp CreateTaskResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Create", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Start(ctx context.Context, req *StartRequest) (*StartResponse, error) { + var resp StartResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Start", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Delete(ctx context.Context, req *DeleteRequest) (*DeleteResponse, error) { + var resp DeleteResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Delete", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Pids(ctx context.Context, req *PidsRequest) (*PidsResponse, error) { + var resp PidsResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Pids", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Pause(ctx context.Context, req *PauseRequest) (*google_protobuf1.Empty, error) { + var resp google_protobuf1.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Pause", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Resume(ctx context.Context, req *ResumeRequest) (*google_protobuf1.Empty, error) { + var resp google_protobuf1.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Resume", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Checkpoint(ctx context.Context, req *CheckpointTaskRequest) (*google_protobuf1.Empty, error) { + var resp google_protobuf1.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Checkpoint", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Kill(ctx context.Context, req *KillRequest) (*google_protobuf1.Empty, error) { + var resp google_protobuf1.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Kill", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Exec(ctx context.Context, req *ExecProcessRequest) (*google_protobuf1.Empty, error) { + var resp google_protobuf1.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Exec", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) ResizePty(ctx context.Context, req *ResizePtyRequest) (*google_protobuf1.Empty, error) { + var resp google_protobuf1.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "ResizePty", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) CloseIO(ctx context.Context, req *CloseIORequest) (*google_protobuf1.Empty, error) { + var resp google_protobuf1.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "CloseIO", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Update(ctx context.Context, req *UpdateTaskRequest) (*google_protobuf1.Empty, error) { + var resp google_protobuf1.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Update", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Wait(ctx context.Context, req *WaitRequest) (*WaitResponse, error) { + var resp WaitResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Wait", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Stats(ctx context.Context, req *StatsRequest) (*StatsResponse, error) { + var resp StatsResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Stats", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Connect(ctx context.Context, req *ConnectRequest) (*ConnectResponse, error) { + var resp ConnectResponse + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Connect", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} + +func (c *taskClient) Shutdown(ctx context.Context, req *ShutdownRequest) (*google_protobuf1.Empty, error) { + var resp google_protobuf1.Empty + if err := c.client.Call(ctx, "containerd.task.v2.Task", "Shutdown", req, &resp); err != nil { + return nil, err + } + return &resp, nil +} +func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CreateTaskRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CreateTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bundle", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bundle = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rootfs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Rootfs = append(m.Rootfs, &containerd_types.Mount{}) + if err := m.Rootfs[len(m.Rootfs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Terminal", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Terminal = bool(v != 0) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdout = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stderr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Checkpoint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Checkpoint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ParentCheckpoint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ParentCheckpoint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Options == nil { + m.Options = &google_protobuf.Any{} + } + if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CreateTaskResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CreateTaskResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CreateTaskResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DeleteRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeleteRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DeleteResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeleteResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeleteResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitStatus", wireType) + } + m.ExitStatus = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExitStatus |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecProcessRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecProcessRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Terminal", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Terminal = bool(v != 0) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdout = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stderr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Spec == nil { + m.Spec = &google_protobuf.Any{} + } + if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ExecProcessResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ExecProcessResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ExecProcessResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResizePtyRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResizePtyRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Width", wireType) + } + m.Width = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Width |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StateRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StateResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Bundle", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Bundle = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= (containerd_v1_types.Status(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdin", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdin = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stdout = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Stderr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Terminal", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Terminal = bool(v != 0) + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitStatus", wireType) + } + m.ExitStatus = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExitStatus |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KillRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KillRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KillRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Signal", wireType) + } + m.Signal = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Signal |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field All", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.All = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CloseIORequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CloseIORequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CloseIORequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Stdin", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Stdin = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PidsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PidsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PidsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PidsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PidsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PidsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Processes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Processes = append(m.Processes, &containerd_v1_types.ProcessInfo{}) + if err := m.Processes[len(m.Processes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CheckpointTaskRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CheckpointTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Options == nil { + m.Options = &google_protobuf.Any{} + } + if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateTaskRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateTaskRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resources", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Resources == nil { + m.Resources = &google_protobuf.Any{} + } + if err := m.Resources.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StartRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StartRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StartRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StartResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StartResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StartResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) + } + m.Pid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WaitRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WaitRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WaitRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *WaitResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: WaitResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: WaitResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitStatus", wireType) + } + m.ExitStatus = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExitStatus |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExitedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StatsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StatsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StatsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StatsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StatsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StatsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stats", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Stats == nil { + m.Stats = &google_protobuf.Any{} + } + if err := m.Stats.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConnectRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConnectRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConnectRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConnectResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConnectResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConnectResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ShimPid", wireType) + } + m.ShimPid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ShimPid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskPid", wireType) + } + m.TaskPid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TaskPid |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ShutdownRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ShutdownRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ShutdownRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Now", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Now = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PauseRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PauseRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PauseRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResumeRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResumeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResumeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowShim + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthShim + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipShim(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthShim + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipShim(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowShim + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowShim + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowShim + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthShim + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowShim + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipShim(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthShim = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowShim = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/runtime/v2/task/shim.proto", fileDescriptorShim) +} + +var fileDescriptorShim = []byte{ + // 1240 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xdb, 0x6f, 0x1b, 0xc5, + 0x17, 0xee, 0xfa, 0xb2, 0xb6, 0x8f, 0xeb, 0x34, 0x9d, 0x5f, 0x9a, 0xdf, 0xd6, 0x95, 0x6c, 0x77, + 0x4b, 0x83, 0x01, 0xc9, 0x16, 0xae, 0xe0, 0x81, 0x48, 0xa0, 0xdc, 0xa8, 0x4c, 0x0b, 0x89, 0xb6, + 0x45, 0x45, 0xbc, 0x58, 0x1b, 0xef, 0xc4, 0x5e, 0xc5, 0xde, 0x59, 0x76, 0x66, 0x73, 0x41, 0x42, + 0xe2, 0x89, 0x07, 0x9e, 0xf8, 0xb3, 0xf2, 0xc0, 0x03, 0x12, 0x2f, 0xbc, 0x10, 0xa8, 0xff, 0x12, + 0x34, 0x17, 0xc7, 0x6b, 0x67, 0xd7, 0x4e, 0x2a, 0xbf, 0x44, 0x73, 0x76, 0xbe, 0x39, 0x33, 0xe7, + 0xcc, 0x77, 0xbe, 0x33, 0x31, 0x6c, 0xf6, 0x5c, 0xd6, 0x0f, 0x0f, 0x1b, 0x5d, 0x32, 0x6c, 0x76, + 0x89, 0xc7, 0x6c, 0xd7, 0xc3, 0x81, 0x13, 0x1d, 0x06, 0xa1, 0xc7, 0xdc, 0x21, 0x6e, 0x9e, 0xb4, + 0x9a, 0xcc, 0xa6, 0xc7, 0x4d, 0xda, 0x77, 0x87, 0x0d, 0x3f, 0x20, 0x8c, 0x20, 0x34, 0x81, 0x35, + 0xf8, 0x5c, 0xe3, 0xa4, 0x55, 0x7e, 0xd8, 0x23, 0xa4, 0x37, 0xc0, 0x4d, 0x81, 0x38, 0x0c, 0x8f, + 0x9a, 0xb6, 0x77, 0x2e, 0xe1, 0xe5, 0x47, 0xb3, 0x53, 0x78, 0xe8, 0xb3, 0xf1, 0xe4, 0x5a, 0x8f, + 0xf4, 0x88, 0x18, 0x36, 0xf9, 0x48, 0x7d, 0xad, 0xce, 0x2e, 0xe1, 0x47, 0xa1, 0xcc, 0x1e, 0xfa, + 0x0a, 0xf0, 0xe9, 0xc2, 0xf3, 0xdb, 0xbe, 0xdb, 0x64, 0xe7, 0x3e, 0xa6, 0xcd, 0x21, 0x09, 0x3d, + 0xa6, 0xd6, 0x7d, 0x76, 0x8b, 0x75, 0x22, 0x6c, 0x11, 0x9f, 0x58, 0x6b, 0xfe, 0x99, 0x82, 0xfb, + 0x3b, 0x01, 0xb6, 0x19, 0x7e, 0x6d, 0xd3, 0x63, 0x0b, 0xff, 0x10, 0x62, 0xca, 0xd0, 0x3a, 0xa4, + 0x5c, 0xc7, 0xd0, 0x6a, 0x5a, 0xbd, 0xb0, 0xad, 0x8f, 0x2e, 0xab, 0xa9, 0xf6, 0xae, 0x95, 0x72, + 0x1d, 0xb4, 0x0e, 0xfa, 0x61, 0xe8, 0x39, 0x03, 0x6c, 0xa4, 0xf8, 0x9c, 0xa5, 0x2c, 0xd4, 0x04, + 0x3d, 0x20, 0x84, 0x1d, 0x51, 0x23, 0x5d, 0x4b, 0xd7, 0x8b, 0xad, 0xff, 0x37, 0xa2, 0xd9, 0xe4, + 0x1b, 0x37, 0xbe, 0xe6, 0x07, 0xb6, 0x14, 0x0c, 0x95, 0x21, 0xcf, 0x70, 0x30, 0x74, 0x3d, 0x7b, + 0x60, 0x64, 0x6a, 0x5a, 0x3d, 0x6f, 0x5d, 0xd9, 0x68, 0x0d, 0xb2, 0x94, 0x39, 0xae, 0x67, 0x64, + 0xc5, 0x1e, 0xd2, 0xe0, 0x5b, 0x53, 0xe6, 0x90, 0x90, 0x19, 0xba, 0xdc, 0x5a, 0x5a, 0xea, 0x3b, + 0x0e, 0x02, 0x23, 0x77, 0xf5, 0x1d, 0x07, 0x01, 0xaa, 0x00, 0x74, 0xfb, 0xb8, 0x7b, 0xec, 0x13, + 0xd7, 0x63, 0x46, 0x5e, 0xcc, 0x45, 0xbe, 0xa0, 0x8f, 0xe0, 0xbe, 0x6f, 0x07, 0xd8, 0x63, 0x9d, + 0x08, 0xac, 0x20, 0x60, 0xab, 0x72, 0x62, 0x67, 0x02, 0x6e, 0x40, 0x8e, 0xf8, 0xcc, 0x25, 0x1e, + 0x35, 0xa0, 0xa6, 0xd5, 0x8b, 0xad, 0xb5, 0x86, 0xbc, 0xcc, 0xc6, 0xf8, 0x32, 0x1b, 0x5b, 0xde, + 0xb9, 0x35, 0x06, 0x99, 0x1b, 0x80, 0xa2, 0x49, 0xa5, 0x3e, 0xf1, 0x28, 0x46, 0xab, 0x90, 0xf6, + 0x55, 0x5a, 0x4b, 0x16, 0x1f, 0x9a, 0x2f, 0xa1, 0xb4, 0x8b, 0x07, 0x98, 0xe1, 0x45, 0x89, 0x7f, + 0x02, 0x39, 0x7c, 0x86, 0xbb, 0x1d, 0xd7, 0x91, 0x99, 0xdf, 0x86, 0xd1, 0x65, 0x55, 0xdf, 0x3b, + 0xc3, 0xdd, 0xf6, 0xae, 0xa5, 0xf3, 0xa9, 0xb6, 0x63, 0xfe, 0xa2, 0xc1, 0xca, 0xd8, 0x5d, 0xd2, + 0x96, 0xa8, 0x0a, 0x45, 0x7c, 0xe6, 0xb2, 0x0e, 0x65, 0x36, 0x0b, 0xa9, 0xf0, 0x56, 0xb2, 0x80, + 0x7f, 0x7a, 0x25, 0xbe, 0xa0, 0x2d, 0x28, 0x70, 0x0b, 0x3b, 0x1d, 0x9b, 0x19, 0x69, 0x11, 0x6d, + 0xf9, 0x5a, 0xb4, 0xaf, 0xc7, 0xd4, 0xdd, 0xce, 0x5f, 0x5c, 0x56, 0xef, 0xfc, 0xf6, 0x4f, 0x55, + 0xb3, 0xf2, 0x72, 0xd9, 0x16, 0x33, 0xff, 0xd6, 0x00, 0xf1, 0xb3, 0x1d, 0x04, 0xa4, 0x8b, 0x29, + 0x5d, 0x46, 0x70, 0x53, 0x8c, 0x49, 0x27, 0x31, 0x26, 0x13, 0xcf, 0x98, 0x6c, 0x02, 0x63, 0xf4, + 0x29, 0xc6, 0xd4, 0x21, 0x43, 0x7d, 0xdc, 0x15, 0x3c, 0x4a, 0xba, 0x61, 0x81, 0x30, 0x1f, 0xc0, + 0xff, 0xa6, 0xc2, 0x93, 0xc9, 0x36, 0x7f, 0x82, 0x55, 0x0b, 0x53, 0xf7, 0x47, 0x7c, 0xc0, 0xce, + 0x97, 0x12, 0xf3, 0x1a, 0x64, 0x4f, 0x5d, 0x87, 0xf5, 0x45, 0xc0, 0x25, 0x4b, 0x1a, 0xfc, 0xfc, + 0x7d, 0xec, 0xf6, 0xfa, 0x4c, 0x84, 0x5b, 0xb2, 0x94, 0x65, 0xbe, 0x80, 0xbb, 0xfc, 0x0a, 0x97, + 0xc3, 0xa5, 0xdf, 0x53, 0x50, 0x52, 0xde, 0x14, 0x95, 0x6e, 0xab, 0x09, 0x8a, 0x7a, 0xe9, 0x09, + 0xf5, 0x9e, 0xf1, 0xc4, 0x0b, 0xd6, 0xf1, 0x83, 0xaf, 0xb4, 0x1e, 0x45, 0x55, 0xe2, 0xe4, 0x63, + 0x25, 0x14, 0x92, 0x86, 0x96, 0x82, 0x2e, 0x49, 0x0d, 0xa2, 0xec, 0xc9, 0xcf, 0xb0, 0x67, 0xa6, + 0x22, 0x0a, 0xf3, 0x2b, 0x02, 0xde, 0xa9, 0x22, 0x18, 0x14, 0x5f, 0xb8, 0x83, 0xc1, 0x52, 0x58, + 0xc1, 0x63, 0x74, 0x7b, 0xe3, 0x3a, 0x28, 0x59, 0xca, 0xe2, 0x09, 0xb7, 0x07, 0x63, 0x39, 0xe5, + 0x43, 0xb3, 0x0b, 0x2b, 0x3b, 0x03, 0x42, 0x71, 0x7b, 0x7f, 0x59, 0x74, 0x94, 0x57, 0x21, 0xeb, + 0x4f, 0x1a, 0xe6, 0x53, 0x28, 0x1e, 0xb8, 0xce, 0xa2, 0x22, 0x37, 0xbf, 0x81, 0xbb, 0x12, 0xa6, + 0xe8, 0xf4, 0x39, 0x14, 0x7c, 0x59, 0x3f, 0x98, 0x1a, 0x9a, 0xe8, 0x1a, 0xb5, 0x58, 0x3e, 0xa8, + 0x2a, 0x6b, 0x7b, 0x47, 0xc4, 0x9a, 0x2c, 0x31, 0x29, 0x3c, 0x98, 0x08, 0xf4, 0x4d, 0x7a, 0x17, + 0x82, 0x8c, 0x6f, 0xb3, 0xbe, 0x62, 0xa9, 0x18, 0x47, 0x75, 0x3d, 0x7d, 0x13, 0x5d, 0xef, 0xc0, + 0xfd, 0x6f, 0x7d, 0xe7, 0x86, 0xcd, 0xb2, 0x05, 0x85, 0x00, 0x53, 0x12, 0x06, 0x5d, 0x2c, 0x75, + 0x36, 0xc9, 0xfd, 0x04, 0xa6, 0x6a, 0x38, 0x60, 0x4b, 0xa9, 0xe1, 0xc7, 0xa2, 0x84, 0xb9, 0xb3, + 0xc4, 0x06, 0xf4, 0x15, 0x14, 0xdf, 0xd8, 0xee, 0x72, 0xb6, 0x0b, 0xe0, 0xae, 0xf4, 0xa5, 0x76, + 0x9b, 0xa9, 0x2b, 0x6d, 0x7e, 0x5d, 0xa5, 0xde, 0xa9, 0xae, 0x36, 0xa4, 0xe6, 0x2d, 0x64, 0xdf, + 0xa6, 0x54, 0xb3, 0x09, 0xfd, 0x3e, 0xe4, 0x5c, 0xb6, 0x99, 0x3c, 0x56, 0xd2, 0xc5, 0x48, 0x88, + 0x59, 0x87, 0x95, 0x1d, 0xe2, 0x79, 0xb8, 0xbb, 0x28, 0x4f, 0xa6, 0x0d, 0xf7, 0xae, 0x90, 0x6a, + 0xa3, 0x87, 0x90, 0xe7, 0xaf, 0xcc, 0xce, 0x24, 0xf1, 0x39, 0x6e, 0x1f, 0xb8, 0x0e, 0x9f, 0xe2, + 0x2f, 0x31, 0x31, 0x25, 0xfb, 0x70, 0x8e, 0xdb, 0x7c, 0xca, 0x80, 0xdc, 0x09, 0x0e, 0xa8, 0x4b, + 0x64, 0xb1, 0x15, 0xac, 0xb1, 0x69, 0x6e, 0xc2, 0xbd, 0x57, 0xfd, 0x90, 0x39, 0xe4, 0xd4, 0x5b, + 0x74, 0x6b, 0xab, 0x90, 0xf6, 0xc8, 0xa9, 0x70, 0x9d, 0xb7, 0xf8, 0x90, 0xa7, 0xeb, 0xc0, 0x0e, + 0xe9, 0xa2, 0x16, 0x61, 0xbe, 0x0f, 0x25, 0x0b, 0xd3, 0x70, 0xb8, 0x08, 0xd8, 0xfa, 0x15, 0x20, + 0xc3, 0x6b, 0x01, 0xbd, 0x84, 0xac, 0x68, 0x17, 0x68, 0xaa, 0x88, 0xd5, 0x43, 0xba, 0x11, 0xed, + 0x4b, 0xe5, 0xc7, 0x73, 0x10, 0x2a, 0x69, 0x6f, 0x40, 0x97, 0xef, 0x27, 0xf4, 0x34, 0x0e, 0x7c, + 0xed, 0xc1, 0x5a, 0xde, 0x58, 0x04, 0x53, 0x8e, 0xe5, 0x31, 0x03, 0x96, 0x78, 0xcc, 0xab, 0xd2, + 0x4b, 0x3c, 0x66, 0xa4, 0x9e, 0xf6, 0x41, 0x97, 0xef, 0x2d, 0x14, 0x0b, 0x9e, 0x7a, 0xda, 0x95, + 0xcd, 0x79, 0x10, 0xe5, 0xb0, 0x0d, 0x19, 0x2e, 0x92, 0xa8, 0x1a, 0x87, 0x8d, 0xa8, 0x6c, 0xb9, + 0x96, 0x0c, 0x50, 0xae, 0xb6, 0x20, 0x2b, 0xae, 0x3a, 0x3e, 0xd2, 0x28, 0x0b, 0xca, 0xeb, 0xd7, + 0xc8, 0xbf, 0xc7, 0xff, 0x99, 0x41, 0x3b, 0xa0, 0x4b, 0x16, 0xc4, 0x87, 0x37, 0xc5, 0x90, 0x44, + 0x27, 0xfb, 0x00, 0x91, 0x87, 0xf4, 0x07, 0xb1, 0xf7, 0x14, 0xa7, 0xe3, 0x89, 0x0e, 0xbf, 0x80, + 0x0c, 0x6f, 0xa5, 0xf1, 0x39, 0x8a, 0x34, 0xd9, 0x44, 0x07, 0x5f, 0x42, 0x86, 0x2b, 0x17, 0x8a, + 0xe5, 0xcc, 0xf5, 0x67, 0x6b, 0xa2, 0x9f, 0x36, 0x14, 0xae, 0x9e, 0x7b, 0xe8, 0xbd, 0x84, 0x0c, + 0x4d, 0xbd, 0x06, 0x13, 0x5d, 0xed, 0x41, 0x4e, 0x35, 0x6a, 0x14, 0x4b, 0x93, 0xe9, 0x2e, 0x9e, + 0xe8, 0xe6, 0x39, 0xe8, 0xb2, 0x3d, 0xc5, 0x97, 0xcd, 0xb5, 0xd6, 0x35, 0x27, 0xb4, 0x0c, 0x97, + 0xf2, 0xf8, 0x1c, 0x47, 0x1a, 0x46, 0x3c, 0x0f, 0xa7, 0xba, 0x80, 0x12, 0x06, 0x9a, 0x2c, 0x0c, + 0x74, 0xa1, 0x30, 0x4c, 0x58, 0x6d, 0x41, 0x4e, 0x09, 0x6c, 0x42, 0xa2, 0xa6, 0x74, 0xba, 0xfc, + 0x64, 0x2e, 0x46, 0xf9, 0x7c, 0x0e, 0xf9, 0xb1, 0xa2, 0xa2, 0xd8, 0x05, 0x33, 0x7a, 0x9b, 0x94, + 0xb5, 0xed, 0xfd, 0x8b, 0xb7, 0x95, 0x3b, 0x7f, 0xbd, 0xad, 0xdc, 0xf9, 0x79, 0x54, 0xd1, 0x2e, + 0x46, 0x15, 0xed, 0x8f, 0x51, 0x45, 0xfb, 0x77, 0x54, 0xd1, 0xbe, 0xff, 0xe4, 0xb6, 0xbf, 0x4c, + 0x6c, 0xf2, 0x3f, 0xdf, 0xa5, 0x0e, 0x75, 0xb1, 0xc5, 0xb3, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, + 0x11, 0x4c, 0x17, 0x02, 0xdb, 0x10, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/task/shim.proto b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/task/shim.proto new file mode 100644 index 000000000..5521ff799 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/runtime/v2/task/shim.proto @@ -0,0 +1,184 @@ +syntax = "proto3"; + +package containerd.task.v2; + +import "google/protobuf/any.proto"; +import "google/protobuf/empty.proto"; +import weak "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; +import "github.com/containerd/containerd/api/types/mount.proto"; +import "github.com/containerd/containerd/api/types/task/task.proto"; + +option go_package = "github.com/containerd/containerd/runtime/v2/task;task"; + +// Shim service is launched for each container and is responsible for owning the IO +// for the container and its additional processes. The shim is also the parent of +// each container and allows reattaching to the IO and receiving the exit status +// for the container processes. +service Task { + rpc State(StateRequest) returns (StateResponse); + rpc Create(CreateTaskRequest) returns (CreateTaskResponse); + rpc Start(StartRequest) returns (StartResponse); + rpc Delete(DeleteRequest) returns (DeleteResponse); + rpc Pids(PidsRequest) returns (PidsResponse); + rpc Pause(PauseRequest) returns (google.protobuf.Empty); + rpc Resume(ResumeRequest) returns (google.protobuf.Empty); + rpc Checkpoint(CheckpointTaskRequest) returns (google.protobuf.Empty); + rpc Kill(KillRequest) returns (google.protobuf.Empty); + rpc Exec(ExecProcessRequest) returns (google.protobuf.Empty); + rpc ResizePty(ResizePtyRequest) returns (google.protobuf.Empty); + rpc CloseIO(CloseIORequest) returns (google.protobuf.Empty); + rpc Update(UpdateTaskRequest) returns (google.protobuf.Empty); + rpc Wait(WaitRequest) returns (WaitResponse); + rpc Stats(StatsRequest) returns (StatsResponse); + rpc Connect(ConnectRequest) returns (ConnectResponse); + rpc Shutdown(ShutdownRequest) returns (google.protobuf.Empty); +} + +message CreateTaskRequest { + string id = 1; + string bundle = 2; + repeated containerd.types.Mount rootfs = 3; + bool terminal = 4; + string stdin = 5; + string stdout = 6; + string stderr = 7; + string checkpoint = 8; + string parent_checkpoint = 9; + google.protobuf.Any options = 10; +} + +message CreateTaskResponse { + uint32 pid = 1; +} + +message DeleteRequest { + string id = 1; + string exec_id = 2; +} + +message DeleteResponse { + uint32 pid = 1; + uint32 exit_status = 2; + google.protobuf.Timestamp exited_at = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; +} + +message ExecProcessRequest { + string id = 1; + string exec_id = 2; + bool terminal = 3; + string stdin = 4; + string stdout = 5; + string stderr = 6; + google.protobuf.Any spec = 7; +} + +message ExecProcessResponse { +} + +message ResizePtyRequest { + string id = 1; + string exec_id = 2; + uint32 width = 3; + uint32 height = 4; +} + +message StateRequest { + string id = 1; + string exec_id = 2; +} + +message StateResponse { + string id = 1; + string bundle = 2; + uint32 pid = 3; + containerd.v1.types.Status status = 4; + string stdin = 5; + string stdout = 6; + string stderr = 7; + bool terminal = 8; + uint32 exit_status = 9; + google.protobuf.Timestamp exited_at = 10 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; +} + +message KillRequest { + string id = 1; + string exec_id = 2; + uint32 signal = 3; + bool all = 4; +} + +message CloseIORequest { + string id = 1; + string exec_id = 2; + bool stdin = 3; +} + +message PidsRequest { + string id = 1; +} + +message PidsResponse { + repeated containerd.v1.types.ProcessInfo processes = 1; +} + +message CheckpointTaskRequest { + string id = 1; + string path = 2; + google.protobuf.Any options = 3; +} + +message UpdateTaskRequest { + string id = 1; + google.protobuf.Any resources = 2; +} + +message StartRequest { + string id = 1; + string exec_id = 2; +} + +message StartResponse { + uint32 pid = 1; +} + +message WaitRequest { + string id = 1; + string exec_id = 2; +} + +message WaitResponse { + uint32 exit_status = 1; + google.protobuf.Timestamp exited_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; +} + +message StatsRequest { + string id = 1; +} + +message StatsResponse { + google.protobuf.Any stats = 1; +} + +message ConnectRequest { + string id = 1; +} + +message ConnectResponse { + uint32 shim_pid = 1; + uint32 task_pid = 2; + string version = 3; +} + +message ShutdownRequest { + string id = 1; + bool now = 2; +} + +message PauseRequest { + string id = 1; +} + +message ResumeRequest { + string id = 1; +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/env.go b/src/runtime/vendor/github.com/containerd/containerd/sys/env.go new file mode 100644 index 000000000..8450d6275 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/env.go @@ -0,0 +1,33 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 sys + +import "golang.org/x/sys/unix" + +// RunningPrivileged returns true if the effective user ID of the +// calling process is 0 +func RunningPrivileged() bool { + return unix.Geteuid() == 0 +} + +// RunningUnprivileged returns true if the effective user ID of the +// calling process is not 0 +func RunningUnprivileged() bool { + return !RunningPrivileged() +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/epoll.go b/src/runtime/vendor/github.com/containerd/containerd/sys/epoll.go new file mode 100644 index 000000000..683f38eea --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/epoll.go @@ -0,0 +1,36 @@ +// +build linux + +/* + Copyright The containerd Authors. + + 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 sys + +import "golang.org/x/sys/unix" + +// EpollCreate1 directly calls unix.EpollCreate1 +func EpollCreate1(flag int) (int, error) { + return unix.EpollCreate1(flag) +} + +// EpollCtl directly calls unix.EpollCtl +func EpollCtl(epfd int, op int, fd int, event *unix.EpollEvent) error { + return unix.EpollCtl(epfd, op, fd, event) +} + +// EpollWait directly calls unix.EpollWait +func EpollWait(epfd int, events []unix.EpollEvent, msec int) (int, error) { + return unix.EpollWait(epfd, events, msec) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/fds.go b/src/runtime/vendor/github.com/containerd/containerd/sys/fds.go new file mode 100644 index 000000000..db3cf702f --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/fds.go @@ -0,0 +1,34 @@ +// +build !windows,!darwin + +/* + Copyright The containerd Authors. + + 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 sys + +import ( + "io/ioutil" + "path/filepath" + "strconv" +) + +// GetOpenFds returns the number of open fds for the process provided by pid +func GetOpenFds(pid int) (int, error) { + dirs, err := ioutil.ReadDir(filepath.Join("/proc", strconv.Itoa(pid), "fd")) + if err != nil { + return -1, err + } + return len(dirs), nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/filesys_unix.go b/src/runtime/vendor/github.com/containerd/containerd/sys/filesys_unix.go new file mode 100644 index 000000000..700f44efa --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/filesys_unix.go @@ -0,0 +1,26 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 sys + +import "os" + +// ForceRemoveAll on unix is just a wrapper for os.RemoveAll +func ForceRemoveAll(path string) error { + return os.RemoveAll(path) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/filesys_windows.go b/src/runtime/vendor/github.com/containerd/containerd/sys/filesys_windows.go new file mode 100644 index 000000000..dc880c342 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/filesys_windows.go @@ -0,0 +1,263 @@ +// +build windows + +/* + Copyright The containerd Authors. + + 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 sys + +import ( + "os" + "path/filepath" + "regexp" + "strings" + "syscall" + "unsafe" + + winio "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim" +) + +// MkdirAllWithACL is a wrapper for MkdirAll that creates a directory +// ACL'd for Builtin Administrators and Local System. +func MkdirAllWithACL(path string, perm os.FileMode) error { + return mkdirall(path, true) +} + +// MkdirAll implementation that is volume path aware for Windows. +func MkdirAll(path string, _ os.FileMode) error { + return mkdirall(path, false) +} + +// mkdirall is a custom version of os.MkdirAll modified for use on Windows +// so that it is both volume path aware, and can create a directory with +// a DACL. +func mkdirall(path string, adminAndLocalSystem bool) error { + if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) { + return nil + } + + // The rest of this method is largely copied from os.MkdirAll and should be kept + // as-is to ensure compatibility. + + // Fast path: if we can tell whether path is a directory or file, stop with success or error. + dir, err := os.Stat(path) + if err == nil { + if dir.IsDir() { + return nil + } + return &os.PathError{ + Op: "mkdir", + Path: path, + Err: syscall.ENOTDIR, + } + } + + // Slow path: make sure parent exists and then call Mkdir for path. + i := len(path) + for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator. + i-- + } + + j := i + for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element. + j-- + } + + if j > 1 { + // Create parent + err = mkdirall(path[0:j-1], false) + if err != nil { + return err + } + } + + // Parent now exists; invoke os.Mkdir or mkdirWithACL and use its result. + if adminAndLocalSystem { + err = mkdirWithACL(path) + } else { + err = os.Mkdir(path, 0) + } + + if err != nil { + // Handle arguments like "foo/." by + // double-checking that directory doesn't exist. + dir, err1 := os.Lstat(path) + if err1 == nil && dir.IsDir() { + return nil + } + return err + } + return nil +} + +// mkdirWithACL creates a new directory. If there is an error, it will be of +// type *PathError. . +// +// This is a modified and combined version of os.Mkdir and syscall.Mkdir +// in golang to cater for creating a directory am ACL permitting full +// access, with inheritance, to any subfolder/file for Built-in Administrators +// and Local System. +func mkdirWithACL(name string) error { + sa := syscall.SecurityAttributes{Length: 0} + sddl := "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)" + sd, err := winio.SddlToSecurityDescriptor(sddl) + if err != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: err} + } + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0])) + + namep, err := syscall.UTF16PtrFromString(name) + if err != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: err} + } + + e := syscall.CreateDirectory(namep, &sa) + if e != nil { + return &os.PathError{Op: "mkdir", Path: name, Err: e} + } + return nil +} + +// IsAbs is a platform-specific wrapper for filepath.IsAbs. On Windows, +// golang filepath.IsAbs does not consider a path \windows\system32 as absolute +// as it doesn't start with a drive-letter/colon combination. However, in +// docker we need to verify things such as WORKDIR /windows/system32 in +// a Dockerfile (which gets translated to \windows\system32 when being processed +// by the daemon. This SHOULD be treated as absolute from a docker processing +// perspective. +func IsAbs(path string) bool { + if !filepath.IsAbs(path) { + if !strings.HasPrefix(path, string(os.PathSeparator)) { + return false + } + } + return true +} + +// The origin of the functions below here are the golang OS and syscall packages, +// slightly modified to only cope with files, not directories due to the +// specific use case. +// +// The alteration is to allow a file on Windows to be opened with +// FILE_FLAG_SEQUENTIAL_SCAN (particular for docker load), to avoid eating +// the standby list, particularly when accessing large files such as layer.tar. + +// CreateSequential creates the named file with mode 0666 (before umask), truncating +// it if it already exists. If successful, methods on the returned +// File can be used for I/O; the associated file descriptor has mode +// O_RDWR. +// If there is an error, it will be of type *PathError. +func CreateSequential(name string) (*os.File, error) { + return OpenFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0) +} + +// OpenSequential opens the named file for reading. If successful, methods on +// the returned file can be used for reading; the associated file +// descriptor has mode O_RDONLY. +// If there is an error, it will be of type *PathError. +func OpenSequential(name string) (*os.File, error) { + return OpenFileSequential(name, os.O_RDONLY, 0) +} + +// OpenFileSequential is the generalized open call; most users will use Open +// or Create instead. +// If there is an error, it will be of type *PathError. +func OpenFileSequential(name string, flag int, _ os.FileMode) (*os.File, error) { + if name == "" { + return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} + } + r, errf := syscallOpenFileSequential(name, flag, 0) + if errf == nil { + return r, nil + } + return nil, &os.PathError{Op: "open", Path: name, Err: errf} +} + +func syscallOpenFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) { + r, e := syscallOpenSequential(name, flag|syscall.O_CLOEXEC, 0) + if e != nil { + return nil, e + } + return os.NewFile(uintptr(r), name), nil +} + +func makeInheritSa() *syscall.SecurityAttributes { + var sa syscall.SecurityAttributes + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + return &sa +} + +func syscallOpenSequential(path string, mode int, _ uint32) (fd syscall.Handle, err error) { + if len(path) == 0 { + return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND + } + pathp, err := syscall.UTF16PtrFromString(path) + if err != nil { + return syscall.InvalidHandle, err + } + var access uint32 + switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) { + case syscall.O_RDONLY: + access = syscall.GENERIC_READ + case syscall.O_WRONLY: + access = syscall.GENERIC_WRITE + case syscall.O_RDWR: + access = syscall.GENERIC_READ | syscall.GENERIC_WRITE + } + if mode&syscall.O_CREAT != 0 { + access |= syscall.GENERIC_WRITE + } + if mode&syscall.O_APPEND != 0 { + access &^= syscall.GENERIC_WRITE + access |= syscall.FILE_APPEND_DATA + } + sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE) + var sa *syscall.SecurityAttributes + if mode&syscall.O_CLOEXEC == 0 { + sa = makeInheritSa() + } + var createmode uint32 + switch { + case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL): + createmode = syscall.CREATE_NEW + case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC): + createmode = syscall.CREATE_ALWAYS + case mode&syscall.O_CREAT == syscall.O_CREAT: + createmode = syscall.OPEN_ALWAYS + case mode&syscall.O_TRUNC == syscall.O_TRUNC: + createmode = syscall.TRUNCATE_EXISTING + default: + createmode = syscall.OPEN_EXISTING + } + // Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang. + //https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx + const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN + h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0) + return h, e +} + +// ForceRemoveAll is the same as os.RemoveAll, but uses hcsshim.DestroyLayer in order +// to delete container layers. +func ForceRemoveAll(path string) error { + info := hcsshim.DriverInfo{ + HomeDir: filepath.Dir(path), + } + + return hcsshim.DestroyLayer(info, filepath.Base(path)) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/mount_linux.go b/src/runtime/vendor/github.com/containerd/containerd/sys/mount_linux.go new file mode 100644 index 000000000..a9eee9b73 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/mount_linux.go @@ -0,0 +1,119 @@ +/* + Copyright The containerd Authors. + + 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 sys + +import ( + "runtime" + "syscall" + "unsafe" + + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +// FMountat performs mount from the provided directory. +func FMountat(dirfd uintptr, source, target, fstype string, flags uintptr, data string) error { + var ( + sourceP, targetP, fstypeP, dataP *byte + pid uintptr + ws unix.WaitStatus + err error + errno syscall.Errno + ) + + sourceP, err = syscall.BytePtrFromString(source) + if err != nil { + return err + } + + targetP, err = syscall.BytePtrFromString(target) + if err != nil { + return err + } + + fstypeP, err = syscall.BytePtrFromString(fstype) + if err != nil { + return err + } + + if data != "" { + dataP, err = syscall.BytePtrFromString(data) + if err != nil { + return err + } + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + pid, errno = forkAndMountat(dirfd, + uintptr(unsafe.Pointer(sourceP)), + uintptr(unsafe.Pointer(targetP)), + uintptr(unsafe.Pointer(fstypeP)), + flags, + uintptr(unsafe.Pointer(dataP))) + + if errno != 0 { + return errors.Wrap(errno, "failed to fork thread") + } + + _, err = unix.Wait4(int(pid), &ws, 0, nil) + for err == syscall.EINTR { + _, err = unix.Wait4(int(pid), &ws, 0, nil) + } + + if err != nil { + return errors.Wrapf(err, "failed to find pid=%d process", pid) + } + + errno = syscall.Errno(ws.ExitStatus()) + if errno != 0 { + return errors.Wrap(errno, "failed to mount") + } + return nil +} + +// forkAndMountat will fork thread, change working dir and mount. +// +// precondition: the runtime OS thread must be locked. +func forkAndMountat(dirfd uintptr, source, target, fstype, flags, data uintptr) (pid uintptr, errno syscall.Errno) { + // block signal during clone + beforeFork() + + // the cloned thread shares the open file descriptor, but the thread + // never be reused by runtime. + pid, _, errno = syscall.RawSyscall6(syscall.SYS_CLONE, uintptr(syscall.SIGCHLD)|syscall.CLONE_FILES, 0, 0, 0, 0, 0) + if errno != 0 || pid != 0 { + // restore all signals + afterFork() + return + } + + // restore all signals + afterForkInChild() + + // change working dir + _, _, errno = syscall.RawSyscall(syscall.SYS_FCHDIR, dirfd, 0, 0) + if errno != 0 { + goto childerr + } + _, _, errno = syscall.RawSyscall6(syscall.SYS_MOUNT, source, target, fstype, flags, data, 0) + +childerr: + syscall.RawSyscall(syscall.SYS_EXIT, uintptr(errno), 0, 0) + panic("unreachable") +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/oom_unix.go b/src/runtime/vendor/github.com/containerd/containerd/sys/oom_unix.go new file mode 100644 index 000000000..7192efec1 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/oom_unix.go @@ -0,0 +1,47 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 sys + +import ( + "fmt" + "os" + "strconv" + + "github.com/opencontainers/runc/libcontainer/system" +) + +// OOMScoreMaxKillable is the maximum score keeping the process killable by the oom killer +const OOMScoreMaxKillable = -999 + +// SetOOMScore sets the oom score for the provided pid +func SetOOMScore(pid, score int) error { + path := fmt.Sprintf("/proc/%d/oom_score_adj", pid) + f, err := os.OpenFile(path, os.O_WRONLY, 0) + if err != nil { + return err + } + defer f.Close() + if _, err = f.WriteString(strconv.Itoa(score)); err != nil { + if os.IsPermission(err) && (system.RunningInUserNS() || RunningUnprivileged()) { + return nil + } + return err + } + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/oom_windows.go b/src/runtime/vendor/github.com/containerd/containerd/sys/oom_windows.go new file mode 100644 index 000000000..f44bcebd1 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/oom_windows.go @@ -0,0 +1,24 @@ +/* + Copyright The containerd Authors. + + 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 sys + +// SetOOMScore sets the oom score for the process +// +// Not implemented on Windows +func SetOOMScore(pid, score int) error { + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/proc.go b/src/runtime/vendor/github.com/containerd/containerd/sys/proc.go new file mode 100644 index 000000000..496eb1fea --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/proc.go @@ -0,0 +1,80 @@ +// +build linux + +/* + Copyright The containerd Authors. + + 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 sys + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" + + "github.com/opencontainers/runc/libcontainer/system" +) + +const nanoSecondsPerSecond = 1e9 + +var clockTicksPerSecond = uint64(system.GetClockTicks()) + +// GetSystemCPUUsage returns the host system's cpu usage in +// nanoseconds. An error is returned if the format of the underlying +// file does not match. +// +// Uses /proc/stat defined by POSIX. Looks for the cpu +// statistics line and then sums up the first seven fields +// provided. See `man 5 proc` for details on specific field +// information. +func GetSystemCPUUsage() (uint64, error) { + var line string + f, err := os.Open("/proc/stat") + if err != nil { + return 0, err + } + bufReader := bufio.NewReaderSize(nil, 128) + defer func() { + bufReader.Reset(nil) + f.Close() + }() + bufReader.Reset(f) + err = nil + for err == nil { + line, err = bufReader.ReadString('\n') + if err != nil { + break + } + parts := strings.Fields(line) + switch parts[0] { + case "cpu": + if len(parts) < 8 { + return 0, fmt.Errorf("bad format of cpu stats") + } + var totalClockTicks uint64 + for _, i := range parts[1:8] { + v, err := strconv.ParseUint(i, 10, 64) + if err != nil { + return 0, fmt.Errorf("error parsing cpu stats") + } + totalClockTicks += v + } + return (totalClockTicks * nanoSecondsPerSecond) / + clockTicksPerSecond, nil + } + } + return 0, fmt.Errorf("bad stats format") +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/reaper.go b/src/runtime/vendor/github.com/containerd/containerd/sys/reaper.go new file mode 100644 index 000000000..d08ccccfb --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/reaper.go @@ -0,0 +1,69 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 sys + +import ( + "golang.org/x/sys/unix" +) + +// Exit is the wait4 information from an exited process +type Exit struct { + Pid int + Status int +} + +// Reap reaps all child processes for the calling process and returns their +// exit information +func Reap(wait bool) (exits []Exit, err error) { + var ( + ws unix.WaitStatus + rus unix.Rusage + ) + flag := unix.WNOHANG + if wait { + flag = 0 + } + for { + pid, err := unix.Wait4(-1, &ws, flag, &rus) + if err != nil { + if err == unix.ECHILD { + return exits, nil + } + return exits, err + } + if pid <= 0 { + return exits, nil + } + exits = append(exits, Exit{ + Pid: pid, + Status: exitStatus(ws), + }) + } +} + +const exitSignalOffset = 128 + +// exitStatus returns the correct exit status for a process based on if it +// was signaled or exited cleanly +func exitStatus(status unix.WaitStatus) int { + if status.Signaled() { + return exitSignalOffset + int(status.Signal()) + } + return status.ExitStatus() +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/reaper_linux.go b/src/runtime/vendor/github.com/containerd/containerd/sys/reaper_linux.go new file mode 100644 index 000000000..ecb0bd031 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/reaper_linux.go @@ -0,0 +1,52 @@ +/* + Copyright The containerd Authors. + + 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 sys + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +// If arg2 is nonzero, set the "child subreaper" attribute of the +// calling process; if arg2 is zero, unset the attribute. When a +// process is marked as a child subreaper, all of the children +// that it creates, and their descendants, will be marked as +// having a subreaper. In effect, a subreaper fulfills the role +// of init(1) for its descendant processes. Upon termination of +// a process that is orphaned (i.e., its immediate parent has +// already terminated) and marked as having a subreaper, the +// nearest still living ancestor subreaper will receive a SIGCHLD +// signal and be able to wait(2) on the process to discover its +// termination status. +const setChildSubreaper = 36 + +// SetSubreaper sets the value i as the subreaper setting for the calling process +func SetSubreaper(i int) error { + return unix.Prctl(setChildSubreaper, uintptr(i), 0, 0, 0) +} + +// GetSubreaper returns the subreaper setting for the calling process +func GetSubreaper() (int, error) { + var i uintptr + + if err := unix.Prctl(unix.PR_GET_CHILD_SUBREAPER, uintptr(unsafe.Pointer(&i)), 0, 0, 0); err != nil { + return -1, err + } + + return int(i), nil +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/socket_unix.go b/src/runtime/vendor/github.com/containerd/containerd/sys/socket_unix.go new file mode 100644 index 000000000..90fa55c48 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/socket_unix.go @@ -0,0 +1,80 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 sys + +import ( + "net" + "os" + "path/filepath" + + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +// CreateUnixSocket creates a unix socket and returns the listener +func CreateUnixSocket(path string) (net.Listener, error) { + // BSDs have a 104 limit + if len(path) > 104 { + return nil, errors.Errorf("%q: unix socket path too long (> 104)", path) + } + if err := os.MkdirAll(filepath.Dir(path), 0660); err != nil { + return nil, err + } + if err := unix.Unlink(path); err != nil && !os.IsNotExist(err) { + return nil, err + } + return net.Listen("unix", path) +} + +// GetLocalListener returns a listener out of a unix socket. +func GetLocalListener(path string, uid, gid int) (net.Listener, error) { + // Ensure parent directory is created + if err := mkdirAs(filepath.Dir(path), uid, gid); err != nil { + return nil, err + } + + l, err := CreateUnixSocket(path) + if err != nil { + return l, err + } + + if err := os.Chmod(path, 0660); err != nil { + l.Close() + return nil, err + } + + if err := os.Chown(path, uid, gid); err != nil { + l.Close() + return nil, err + } + + return l, nil +} + +func mkdirAs(path string, uid, gid int) error { + if _, err := os.Stat(path); err == nil || !os.IsNotExist(err) { + return err + } + + if err := os.Mkdir(path, 0770); err != nil { + return err + } + + return os.Chown(path, uid, gid) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/socket_windows.go b/src/runtime/vendor/github.com/containerd/containerd/sys/socket_windows.go new file mode 100644 index 000000000..3ee7679b4 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/socket_windows.go @@ -0,0 +1,32 @@ +// +build windows + +/* + Copyright The containerd Authors. + + 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 sys + +import ( + "net" + + "github.com/Microsoft/go-winio" +) + +// GetLocalListener returns a Listernet out of a named pipe. +// `path` must be of the form of `\\.\pipe\` +// (see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365150) +func GetLocalListener(path string, uid, gid int) (net.Listener, error) { + return winio.ListenPipe(path, nil) +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/stat_bsd.go b/src/runtime/vendor/github.com/containerd/containerd/sys/stat_bsd.go new file mode 100644 index 000000000..b9c95d90d --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/stat_bsd.go @@ -0,0 +1,44 @@ +// +build darwin freebsd + +/* + Copyright The containerd Authors. + + 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 sys + +import ( + "syscall" + "time" +) + +// StatAtime returns the access time from a stat struct +func StatAtime(st *syscall.Stat_t) syscall.Timespec { + return st.Atimespec +} + +// StatCtime returns the created time from a stat struct +func StatCtime(st *syscall.Stat_t) syscall.Timespec { + return st.Ctimespec +} + +// StatMtime returns the modified time from a stat struct +func StatMtime(st *syscall.Stat_t) syscall.Timespec { + return st.Mtimespec +} + +// StatATimeAsTime returns the access time as a time.Time +func StatATimeAsTime(st *syscall.Stat_t) time.Time { + return time.Unix(int64(st.Atimespec.Sec), int64(st.Atimespec.Nsec)) // nolint: unconvert +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/stat_unix.go b/src/runtime/vendor/github.com/containerd/containerd/sys/stat_unix.go new file mode 100644 index 000000000..21a666dff --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/stat_unix.go @@ -0,0 +1,44 @@ +// +build linux solaris + +/* + Copyright The containerd Authors. + + 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 sys + +import ( + "syscall" + "time" +) + +// StatAtime returns the Atim +func StatAtime(st *syscall.Stat_t) syscall.Timespec { + return st.Atim +} + +// StatCtime returns the Ctim +func StatCtime(st *syscall.Stat_t) syscall.Timespec { + return st.Ctim +} + +// StatMtime returns the Mtim +func StatMtime(st *syscall.Stat_t) syscall.Timespec { + return st.Mtim +} + +// StatATimeAsTime returns st.Atim as a time.Time +func StatATimeAsTime(st *syscall.Stat_t) time.Time { + return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) // nolint: unconvert +} diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.go b/src/runtime/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.go new file mode 100644 index 000000000..6e40a9c7d --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.go @@ -0,0 +1,30 @@ +/* + Copyright The containerd Authors. + + 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 sys + +import ( + _ "unsafe" // required for go:linkname. +) + +//go:linkname beforeFork syscall.runtime_BeforeFork +func beforeFork() + +//go:linkname afterFork syscall.runtime_AfterFork +func afterFork() + +//go:linkname afterForkInChild syscall.runtime_AfterForkInChild +func afterForkInChild() diff --git a/src/runtime/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.s b/src/runtime/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.s new file mode 100644 index 000000000..c073fa4ad --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.s @@ -0,0 +1,15 @@ +/* + Copyright The containerd Authors. + + 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. +*/ diff --git a/src/runtime/vendor/github.com/containerd/cri-containerd/LICENSE b/src/runtime/vendor/github.com/containerd/cri-containerd/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cri-containerd/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/src/runtime/vendor/github.com/containerd/cri-containerd/pkg/annotations/annotations.go b/src/runtime/vendor/github.com/containerd/cri-containerd/pkg/annotations/annotations.go new file mode 100644 index 000000000..be63ba27a --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cri-containerd/pkg/annotations/annotations.go @@ -0,0 +1,38 @@ +/* +Copyright 2018 The Containerd Authors. + +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 annotations + +// ContainerType values +// Following OCI annotations are used by katacontainers now. +// We'll switch to standard secure pod API after it is defined in CRI. +const ( + // ContainerTypeSandbox represents a pod sandbox container + ContainerTypeSandbox = "sandbox" + + // ContainerTypeContainer represents a container running within a pod + ContainerTypeContainer = "container" + + // ContainerType is the container type (sandbox or container) annotation + ContainerType = "io.kubernetes.cri.container-type" + + // SandboxID is the sandbox ID annotation + SandboxID = "io.kubernetes.cri.sandbox-id" + + // UntrustedWorkload is the sandbox annotation for untrusted workload. Untrusted + // workload can only run on dedicated runtime for untrusted workload. + UntrustedWorkload = "io.kubernetes.cri.untrusted-workload" +) diff --git a/src/runtime/vendor/github.com/containerd/cri-containerd/pkg/api/runtimeoptions/v1/api.pb.go b/src/runtime/vendor/github.com/containerd/cri-containerd/pkg/api/runtimeoptions/v1/api.pb.go new file mode 100644 index 000000000..71341ec78 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cri-containerd/pkg/api/runtimeoptions/v1/api.pb.go @@ -0,0 +1,394 @@ +/* +Copyright 2019 The containerd Authors. + +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. +*/ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: api.proto + +/* + Package cri_runtimeoptions_v1 is a generated protocol buffer package. + + It is generated from these files: + api.proto + + It has these top-level messages: + Options +*/ +package cri_runtimeoptions_v1 + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" +import _ "github.com/gogo/protobuf/gogoproto" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type Options struct { + // TypeUrl specifies the type of the content inside the config file. + TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"` + // ConfigPath specifies the filesystem location of the config file + // used by the runtime. + ConfigPath string `protobuf:"bytes,2,opt,name=config_path,json=configPath,proto3" json:"config_path,omitempty"` +} + +func (m *Options) Reset() { *m = Options{} } +func (*Options) ProtoMessage() {} +func (*Options) Descriptor() ([]byte, []int) { return fileDescriptorApi, []int{0} } + +func (m *Options) GetTypeUrl() string { + if m != nil { + return m.TypeUrl + } + return "" +} + +func (m *Options) GetConfigPath() string { + if m != nil { + return m.ConfigPath + } + return "" +} + +func init() { + proto.RegisterType((*Options)(nil), "cri.runtimeoptions.v1.Options") +} +func (m *Options) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Options) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.TypeUrl) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.TypeUrl))) + i += copy(dAtA[i:], m.TypeUrl) + } + if len(m.ConfigPath) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintApi(dAtA, i, uint64(len(m.ConfigPath))) + i += copy(dAtA[i:], m.ConfigPath) + } + return i, nil +} + +func encodeVarintApi(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Options) Size() (n int) { + var l int + _ = l + l = len(m.TypeUrl) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + l = len(m.ConfigPath) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + return n +} + +func sovApi(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozApi(x uint64) (n int) { + return sovApi(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Options) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Options{`, + `TypeUrl:` + fmt.Sprintf("%v", this.TypeUrl) + `,`, + `ConfigPath:` + fmt.Sprintf("%v", this.ConfigPath) + `,`, + `}`, + }, "") + return s +} +func valueToStringApi(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Options) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Options: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Options: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeUrl = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConfigPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConfigPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipApi(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthApi + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipApi(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthApi = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowApi = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("api.proto", fileDescriptorApi) } + +var fileDescriptorApi = []byte{ + // 183 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0x2c, 0xc8, 0xd4, + 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x4d, 0x2e, 0xca, 0xd4, 0x2b, 0x2a, 0xcd, 0x2b, 0xc9, + 0xcc, 0x4d, 0xcd, 0x2f, 0x28, 0xc9, 0xcc, 0xcf, 0x2b, 0xd6, 0x2b, 0x33, 0x94, 0xd2, 0x4d, 0xcf, + 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0x4f, 0xcf, 0xd7, 0x07, 0xab, + 0x4e, 0x2a, 0x4d, 0x03, 0xf3, 0xc0, 0x1c, 0x30, 0x0b, 0x62, 0x8a, 0x92, 0x2b, 0x17, 0xbb, 0x3f, + 0x44, 0xb3, 0x90, 0x24, 0x17, 0x47, 0x49, 0x65, 0x41, 0x6a, 0x7c, 0x69, 0x51, 0x8e, 0x04, 0xa3, + 0x02, 0xa3, 0x06, 0x67, 0x10, 0x3b, 0x88, 0x1f, 0x5a, 0x94, 0x23, 0x24, 0xcf, 0xc5, 0x9d, 0x9c, + 0x9f, 0x97, 0x96, 0x99, 0x1e, 0x5f, 0x90, 0x58, 0x92, 0x21, 0xc1, 0x04, 0x96, 0xe5, 0x82, 0x08, + 0x05, 0x24, 0x96, 0x64, 0x38, 0xc9, 0x9c, 0x78, 0x28, 0xc7, 0x78, 0xe3, 0xa1, 0x1c, 0x43, 0xc3, + 0x23, 0x39, 0xc6, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, + 0xc2, 0x63, 0x39, 0x86, 0x24, 0x36, 0xb0, 0x5d, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x07, + 0x00, 0xf2, 0x18, 0xbe, 0x00, 0x00, 0x00, +} diff --git a/src/runtime/vendor/github.com/containerd/cri-containerd/pkg/api/runtimeoptions/v1/api.proto b/src/runtime/vendor/github.com/containerd/cri-containerd/pkg/api/runtimeoptions/v1/api.proto new file mode 100644 index 000000000..f907d609c --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/cri-containerd/pkg/api/runtimeoptions/v1/api.proto @@ -0,0 +1,22 @@ +// To regenerate api.pb.go run `make proto` +syntax = "proto3"; + +package cri.runtimeoptions.v1; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.stringer_all) = true; +option (gogoproto.goproto_getters_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.sizer_all) = true; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.goproto_unrecognized_all) = false; + +message Options { + // TypeUrl specifies the type of the content inside the config file. + string type_url = 1; + // ConfigPath specifies the filesystem location of the config file + // used by the runtime. + string config_path = 2; +} diff --git a/src/runtime/vendor/github.com/containerd/fifo/.gitignore b/src/runtime/vendor/github.com/containerd/fifo/.gitignore new file mode 100644 index 000000000..c57100a59 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/fifo/.gitignore @@ -0,0 +1 @@ +coverage.txt diff --git a/src/runtime/vendor/github.com/containerd/fifo/.travis.yml b/src/runtime/vendor/github.com/containerd/fifo/.travis.yml new file mode 100644 index 000000000..c6eaba059 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/fifo/.travis.yml @@ -0,0 +1,22 @@ +language: go +go: + - 1.11.x + - tip + +install: + - go get -u github.com/vbatts/git-validation + - go get -u github.com/kunalkushwaha/ltag + +before_script: + - pushd ..; git clone https://github.com/containerd/project; popd + +script: + - DCO_VERBOSITY=-q ../project/script/validate/dco + - ../project/script/validate/fileheader ../project/ + - make deps + - make fmt + - make vet + - make test + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/src/runtime/vendor/github.com/containerd/fifo/LICENSE b/src/runtime/vendor/github.com/containerd/fifo/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/fifo/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/src/runtime/vendor/github.com/containerd/fifo/Makefile b/src/runtime/vendor/github.com/containerd/fifo/Makefile new file mode 100644 index 000000000..96be48d48 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/fifo/Makefile @@ -0,0 +1,27 @@ +# Copyright The containerd Authors. + +# 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. + +.PHONY: fmt vet test deps + +test: deps + go test -v -race -covermode=atomic -coverprofile=coverage.txt ./... + +deps: + go get -d -t ./... + +fmt: + gofmt -s -l . + +vet: + go vet ./... diff --git a/src/runtime/vendor/github.com/containerd/fifo/fifo.go b/src/runtime/vendor/github.com/containerd/fifo/fifo.go new file mode 100644 index 000000000..e79813da7 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/fifo/fifo.go @@ -0,0 +1,236 @@ +/* + Copyright The containerd Authors. + + 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 fifo + +import ( + "io" + "os" + "runtime" + "sync" + "syscall" + + "github.com/pkg/errors" + "golang.org/x/net/context" +) + +type fifo struct { + flag int + opened chan struct{} + closed chan struct{} + closing chan struct{} + err error + file *os.File + closingOnce sync.Once // close has been called + closedOnce sync.Once // fifo is closed + handle *handle +} + +var leakCheckWg *sync.WaitGroup + +// OpenFifo opens a fifo. Returns io.ReadWriteCloser. +// Context can be used to cancel this function until open(2) has not returned. +// Accepted flags: +// - syscall.O_CREAT - create new fifo if one doesn't exist +// - syscall.O_RDONLY - open fifo only from reader side +// - syscall.O_WRONLY - open fifo only from writer side +// - syscall.O_RDWR - open fifo from both sides, never block on syscall level +// - syscall.O_NONBLOCK - return io.ReadWriteCloser even if other side of the +// fifo isn't open. read/write will be connected after the actual fifo is +// open or after fifo is closed. +func OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { + if _, err := os.Stat(fn); err != nil { + if os.IsNotExist(err) && flag&syscall.O_CREAT != 0 { + if err := mkfifo(fn, uint32(perm&os.ModePerm)); err != nil && !os.IsExist(err) { + return nil, errors.Wrapf(err, "error creating fifo %v", fn) + } + } else { + return nil, err + } + } + + block := flag&syscall.O_NONBLOCK == 0 || flag&syscall.O_RDWR != 0 + + flag &= ^syscall.O_CREAT + flag &= ^syscall.O_NONBLOCK + + h, err := getHandle(fn) + if err != nil { + return nil, err + } + + f := &fifo{ + handle: h, + flag: flag, + opened: make(chan struct{}), + closed: make(chan struct{}), + closing: make(chan struct{}), + } + + wg := leakCheckWg + if wg != nil { + wg.Add(2) + } + + go func() { + if wg != nil { + defer wg.Done() + } + select { + case <-ctx.Done(): + select { + case <-f.opened: + default: + f.Close() + } + case <-f.opened: + case <-f.closed: + } + }() + go func() { + if wg != nil { + defer wg.Done() + } + var file *os.File + fn, err := h.Path() + if err == nil { + file, err = os.OpenFile(fn, flag, 0) + } + select { + case <-f.closing: + if err == nil { + select { + case <-ctx.Done(): + err = ctx.Err() + default: + err = errors.Errorf("fifo %v was closed before opening", h.Name()) + } + if file != nil { + file.Close() + } + } + default: + } + if err != nil { + f.closedOnce.Do(func() { + f.err = err + close(f.closed) + }) + return + } + f.file = file + close(f.opened) + }() + if block { + select { + case <-f.opened: + case <-f.closed: + return nil, f.err + } + } + return f, nil +} + +// Read from a fifo to a byte array. +func (f *fifo) Read(b []byte) (int, error) { + if f.flag&syscall.O_WRONLY > 0 { + return 0, errors.New("reading from write-only fifo") + } + select { + case <-f.opened: + return f.file.Read(b) + default: + } + select { + case <-f.opened: + return f.file.Read(b) + case <-f.closed: + return 0, errors.New("reading from a closed fifo") + } +} + +// Write from byte array to a fifo. +func (f *fifo) Write(b []byte) (int, error) { + if f.flag&(syscall.O_WRONLY|syscall.O_RDWR) == 0 { + return 0, errors.New("writing to read-only fifo") + } + select { + case <-f.opened: + return f.file.Write(b) + default: + } + select { + case <-f.opened: + return f.file.Write(b) + case <-f.closed: + return 0, errors.New("writing to a closed fifo") + } +} + +// Close the fifo. Next reads/writes will error. This method can also be used +// before open(2) has returned and fifo was never opened. +func (f *fifo) Close() (retErr error) { + for { + select { + case <-f.closed: + f.handle.Close() + return + default: + select { + case <-f.opened: + f.closedOnce.Do(func() { + retErr = f.file.Close() + f.err = retErr + close(f.closed) + }) + default: + if f.flag&syscall.O_RDWR != 0 { + runtime.Gosched() + break + } + f.closingOnce.Do(func() { + close(f.closing) + }) + reverseMode := syscall.O_WRONLY + if f.flag&syscall.O_WRONLY > 0 { + reverseMode = syscall.O_RDONLY + } + fn, err := f.handle.Path() + // if Close() is called concurrently(shouldn't) it may cause error + // because handle is closed + select { + case <-f.closed: + default: + if err != nil { + // Path has become invalid. We will leak a goroutine. + // This case should not happen in linux. + f.closedOnce.Do(func() { + f.err = err + close(f.closed) + }) + <-f.closed + break + } + f, err := os.OpenFile(fn, reverseMode|syscall.O_NONBLOCK, 0) + if err == nil { + f.Close() + } + runtime.Gosched() + } + } + } + } +} diff --git a/src/runtime/vendor/github.com/containerd/fifo/handle_linux.go b/src/runtime/vendor/github.com/containerd/fifo/handle_linux.go new file mode 100644 index 000000000..6ac89b6a4 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/fifo/handle_linux.go @@ -0,0 +1,97 @@ +// +build linux + +/* + Copyright The containerd Authors. + + 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 fifo + +import ( + "fmt" + "os" + "sync" + "syscall" + + "github.com/pkg/errors" +) + +const O_PATH = 010000000 + +type handle struct { + f *os.File + fd uintptr + dev uint64 + ino uint64 + closeOnce sync.Once + name string +} + +func getHandle(fn string) (*handle, error) { + f, err := os.OpenFile(fn, O_PATH, 0) + if err != nil { + return nil, errors.Wrapf(err, "failed to open %v with O_PATH", fn) + } + + var ( + stat syscall.Stat_t + fd = f.Fd() + ) + if err := syscall.Fstat(int(fd), &stat); err != nil { + f.Close() + return nil, errors.Wrapf(err, "failed to stat handle %v", fd) + } + + h := &handle{ + f: f, + name: fn, + dev: uint64(stat.Dev), + ino: stat.Ino, + fd: fd, + } + + // check /proc just in case + if _, err := os.Stat(h.procPath()); err != nil { + f.Close() + return nil, errors.Wrapf(err, "couldn't stat %v", h.procPath()) + } + + return h, nil +} + +func (h *handle) procPath() string { + return fmt.Sprintf("/proc/self/fd/%d", h.fd) +} + +func (h *handle) Name() string { + return h.name +} + +func (h *handle) Path() (string, error) { + var stat syscall.Stat_t + if err := syscall.Stat(h.procPath(), &stat); err != nil { + return "", errors.Wrapf(err, "path %v could not be statted", h.procPath()) + } + if uint64(stat.Dev) != h.dev || stat.Ino != h.ino { + return "", errors.Errorf("failed to verify handle %v/%v %v/%v", stat.Dev, h.dev, stat.Ino, h.ino) + } + return h.procPath(), nil +} + +func (h *handle) Close() error { + h.closeOnce.Do(func() { + h.f.Close() + }) + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/fifo/handle_nolinux.go b/src/runtime/vendor/github.com/containerd/fifo/handle_nolinux.go new file mode 100644 index 000000000..4f2a282b2 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/fifo/handle_nolinux.go @@ -0,0 +1,65 @@ +// +build !linux + +/* + Copyright The containerd Authors. + + 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 fifo + +import ( + "syscall" + + "github.com/pkg/errors" +) + +type handle struct { + fn string + dev uint64 + ino uint64 +} + +func getHandle(fn string) (*handle, error) { + var stat syscall.Stat_t + if err := syscall.Stat(fn, &stat); err != nil { + return nil, errors.Wrapf(err, "failed to stat %v", fn) + } + + h := &handle{ + fn: fn, + dev: uint64(stat.Dev), + ino: uint64(stat.Ino), + } + + return h, nil +} + +func (h *handle) Path() (string, error) { + var stat syscall.Stat_t + if err := syscall.Stat(h.fn, &stat); err != nil { + return "", errors.Wrapf(err, "path %v could not be statted", h.fn) + } + if uint64(stat.Dev) != h.dev || uint64(stat.Ino) != h.ino { + return "", errors.Errorf("failed to verify handle %v/%v %v/%v for %v", stat.Dev, h.dev, stat.Ino, h.ino, h.fn) + } + return h.fn, nil +} + +func (h *handle) Name() string { + return h.fn +} + +func (h *handle) Close() error { + return nil +} diff --git a/src/runtime/vendor/github.com/containerd/fifo/mkfifo_nosolaris.go b/src/runtime/vendor/github.com/containerd/fifo/mkfifo_nosolaris.go new file mode 100644 index 000000000..2799a06d1 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/fifo/mkfifo_nosolaris.go @@ -0,0 +1,25 @@ +// +build !solaris + +/* + Copyright The containerd Authors. + + 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 fifo + +import "syscall" + +func mkfifo(path string, mode uint32) (err error) { + return syscall.Mkfifo(path, mode) +} diff --git a/src/runtime/vendor/github.com/containerd/fifo/mkfifo_solaris.go b/src/runtime/vendor/github.com/containerd/fifo/mkfifo_solaris.go new file mode 100644 index 000000000..1ecd722ae --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/fifo/mkfifo_solaris.go @@ -0,0 +1,27 @@ +// +build solaris + +/* + Copyright The containerd Authors. + + 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 fifo + +import ( + "golang.org/x/sys/unix" +) + +func mkfifo(path string, mode uint32) (err error) { + return unix.Mkfifo(path, mode) +} diff --git a/src/runtime/vendor/github.com/containerd/fifo/raw.go b/src/runtime/vendor/github.com/containerd/fifo/raw.go new file mode 100644 index 000000000..acc303e43 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/fifo/raw.go @@ -0,0 +1,116 @@ +// +build go1.12 + +/* + Copyright The containerd Authors. + + 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 fifo + +import ( + "syscall" + + "github.com/pkg/errors" +) + +// SyscallConn provides raw access to the fifo's underlying filedescrptor. +// See syscall.Conn for guarentees provided by this interface. +func (f *fifo) SyscallConn() (syscall.RawConn, error) { + // deterministic check for closed + select { + case <-f.closed: + return nil, errors.New("fifo closed") + default: + } + + select { + case <-f.closed: + return nil, errors.New("fifo closed") + case <-f.opened: + return f.file.SyscallConn() + default: + } + + // Not opened and not closed, this means open is non-blocking AND it's not open yet + // Use rawConn to deal with non-blocking open. + rc := &rawConn{f: f, ready: make(chan struct{})} + go func() { + select { + case <-f.closed: + return + case <-f.opened: + rc.raw, rc.err = f.file.SyscallConn() + close(rc.ready) + } + }() + + return rc, nil +} + +type rawConn struct { + f *fifo + ready chan struct{} + raw syscall.RawConn + err error +} + +func (r *rawConn) Control(f func(fd uintptr)) error { + select { + case <-r.f.closed: + return errors.New("control of closed fifo") + case <-r.ready: + } + + if r.err != nil { + return r.err + } + + return r.raw.Control(f) +} + +func (r *rawConn) Read(f func(fd uintptr) (done bool)) error { + if r.f.flag&syscall.O_WRONLY > 0 { + return errors.New("reading from write-only fifo") + } + + select { + case <-r.f.closed: + return errors.New("reading of a closed fifo") + case <-r.ready: + } + + if r.err != nil { + return r.err + } + + return r.raw.Read(f) +} + +func (r *rawConn) Write(f func(fd uintptr) (done bool)) error { + if r.f.flag&(syscall.O_WRONLY|syscall.O_RDWR) == 0 { + return errors.New("writing to read-only fifo") + } + + select { + case <-r.f.closed: + return errors.New("writing to a closed fifo") + case <-r.ready: + } + + if r.err != nil { + return r.err + } + + return r.raw.Write(f) +} diff --git a/src/runtime/vendor/github.com/containerd/fifo/readme.md b/src/runtime/vendor/github.com/containerd/fifo/readme.md new file mode 100644 index 000000000..30e233cc6 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/fifo/readme.md @@ -0,0 +1,44 @@ +### fifo + +[![Build Status](https://travis-ci.org/containerd/fifo.svg?branch=master)](https://travis-ci.org/containerd/fifo) +[![codecov](https://codecov.io/gh/containerd/fifo/branch/master/graph/badge.svg)](https://codecov.io/gh/containerd/fifo) + +Go package for handling fifos in a sane way. + +``` +// OpenFifo opens a fifo. Returns io.ReadWriteCloser. +// Context can be used to cancel this function until open(2) has not returned. +// Accepted flags: +// - syscall.O_CREAT - create new fifo if one doesn't exist +// - syscall.O_RDONLY - open fifo only from reader side +// - syscall.O_WRONLY - open fifo only from writer side +// - syscall.O_RDWR - open fifo from both sides, never block on syscall level +// - syscall.O_NONBLOCK - return io.ReadWriteCloser even if other side of the +// fifo isn't open. read/write will be connected after the actual fifo is +// open or after fifo is closed. +func OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) + + +// Read from a fifo to a byte array. +func (f *fifo) Read(b []byte) (int, error) + + +// Write from byte array to a fifo. +func (f *fifo) Write(b []byte) (int, error) + + +// Close the fifo. Next reads/writes will error. This method can also be used +// before open(2) has returned and fifo was never opened. +func (f *fifo) Close() error +``` + +## Project details + +The fifo is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/src/runtime/vendor/github.com/containerd/go-runc/.travis.yml b/src/runtime/vendor/github.com/containerd/go-runc/.travis.yml new file mode 100644 index 000000000..dd60e9ba0 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/.travis.yml @@ -0,0 +1,20 @@ +language: go +go: + - 1.12.x + - 1.13.x + +install: + - go get -t ./... + - go get -u github.com/vbatts/git-validation + - go get -u github.com/kunalkushwaha/ltag + +before_script: + - pushd ..; git clone https://github.com/containerd/project; popd + +script: + - DCO_VERBOSITY=-q ../project/script/validate/dco + - ../project/script/validate/fileheader ../project/ + - go test -v -race -covermode=atomic -coverprofile=coverage.txt ./... + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/src/runtime/vendor/github.com/containerd/go-runc/LICENSE b/src/runtime/vendor/github.com/containerd/go-runc/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/src/runtime/vendor/github.com/containerd/go-runc/README.md b/src/runtime/vendor/github.com/containerd/go-runc/README.md new file mode 100644 index 000000000..c899bdd7e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/README.md @@ -0,0 +1,25 @@ +# go-runc + +[![Build Status](https://travis-ci.org/containerd/go-runc.svg?branch=master)](https://travis-ci.org/containerd/go-runc) +[![codecov](https://codecov.io/gh/containerd/go-runc/branch/master/graph/badge.svg)](https://codecov.io/gh/containerd/go-runc) + +This is a package for consuming the [runc](https://github.com/opencontainers/runc) binary in your Go applications. +It tries to expose all the settings and features of the runc CLI. If there is something missing then add it, its opensource! + +This needs runc @ [a9610f2c0](https://github.com/opencontainers/runc/commit/a9610f2c0237d2636d05a031ec8659a70e75ffeb) +or greater. + +## Docs + +Docs can be found at [godoc.org](https://godoc.org/github.com/containerd/go-runc). + +## Project details + +The go-runc is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/src/runtime/vendor/github.com/containerd/go-runc/command_linux.go b/src/runtime/vendor/github.com/containerd/go-runc/command_linux.go new file mode 100644 index 000000000..8a30f679d --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/command_linux.go @@ -0,0 +1,56 @@ +/* + Copyright The containerd Authors. + + 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 runc + +import ( + "context" + "os" + "os/exec" + "strings" + "syscall" +) + +func (r *Runc) command(context context.Context, args ...string) *exec.Cmd { + command := r.Command + if command == "" { + command = DefaultCommand + } + cmd := exec.CommandContext(context, command, append(r.args(), args...)...) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: r.Setpgid, + } + cmd.Env = filterEnv(os.Environ(), "NOTIFY_SOCKET") // NOTIFY_SOCKET introduces a special behavior in runc but should only be set if invoked from systemd + if r.PdeathSignal != 0 { + cmd.SysProcAttr.Pdeathsig = r.PdeathSignal + } + + return cmd +} + +func filterEnv(in []string, names ...string) []string { + out := make([]string, 0, len(in)) +loop0: + for _, v := range in { + for _, k := range names { + if strings.HasPrefix(v, k+"=") { + continue loop0 + } + } + out = append(out, v) + } + return out +} diff --git a/src/runtime/vendor/github.com/containerd/go-runc/command_other.go b/src/runtime/vendor/github.com/containerd/go-runc/command_other.go new file mode 100644 index 000000000..b8fd4b866 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/command_other.go @@ -0,0 +1,35 @@ +// +build !linux + +/* + Copyright The containerd Authors. + + 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 runc + +import ( + "context" + "os" + "os/exec" +) + +func (r *Runc) command(context context.Context, args ...string) *exec.Cmd { + command := r.Command + if command == "" { + command = DefaultCommand + } + cmd := exec.CommandContext(context, command, append(r.args(), args...)...) + cmd.Env = os.Environ() + return cmd +} diff --git a/src/runtime/vendor/github.com/containerd/go-runc/console.go b/src/runtime/vendor/github.com/containerd/go-runc/console.go new file mode 100644 index 000000000..ff223e427 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/console.go @@ -0,0 +1,165 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 runc + +import ( + "fmt" + "io/ioutil" + "net" + "os" + "path/filepath" + + "github.com/containerd/console" + "golang.org/x/sys/unix" +) + +// NewConsoleSocket creates a new unix socket at the provided path to accept a +// pty master created by runc for use by the container +func NewConsoleSocket(path string) (*Socket, error) { + abs, err := filepath.Abs(path) + if err != nil { + return nil, err + } + addr, err := net.ResolveUnixAddr("unix", abs) + if err != nil { + return nil, err + } + l, err := net.ListenUnix("unix", addr) + if err != nil { + return nil, err + } + return &Socket{ + l: l, + }, nil +} + +// NewTempConsoleSocket returns a temp console socket for use with a container +// On Close(), the socket is deleted +func NewTempConsoleSocket() (*Socket, error) { + runtimeDir := os.Getenv("XDG_RUNTIME_DIR") + dir, err := ioutil.TempDir(runtimeDir, "pty") + if err != nil { + return nil, err + } + abs, err := filepath.Abs(filepath.Join(dir, "pty.sock")) + if err != nil { + return nil, err + } + addr, err := net.ResolveUnixAddr("unix", abs) + if err != nil { + return nil, err + } + l, err := net.ListenUnix("unix", addr) + if err != nil { + return nil, err + } + if runtimeDir != "" { + if err := os.Chmod(abs, 0755|os.ModeSticky); err != nil { + return nil, err + } + } + return &Socket{ + l: l, + rmdir: true, + }, nil +} + +// Socket is a unix socket that accepts the pty master created by runc +type Socket struct { + rmdir bool + l *net.UnixListener +} + +// Path returns the path to the unix socket on disk +func (c *Socket) Path() string { + return c.l.Addr().String() +} + +// recvFd waits for a file descriptor to be sent over the given AF_UNIX +// socket. The file name of the remote file descriptor will be recreated +// locally (it is sent as non-auxiliary data in the same payload). +func recvFd(socket *net.UnixConn) (*os.File, error) { + const MaxNameLen = 4096 + var oobSpace = unix.CmsgSpace(4) + + name := make([]byte, MaxNameLen) + oob := make([]byte, oobSpace) + + n, oobn, _, _, err := socket.ReadMsgUnix(name, oob) + if err != nil { + return nil, err + } + + if n >= MaxNameLen || oobn != oobSpace { + return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn) + } + + // Truncate. + name = name[:n] + oob = oob[:oobn] + + scms, err := unix.ParseSocketControlMessage(oob) + if err != nil { + return nil, err + } + if len(scms) != 1 { + return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms)) + } + scm := scms[0] + + fds, err := unix.ParseUnixRights(&scm) + if err != nil { + return nil, err + } + if len(fds) != 1 { + return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds)) + } + fd := uintptr(fds[0]) + + return os.NewFile(fd, string(name)), nil +} + +// ReceiveMaster blocks until the socket receives the pty master +func (c *Socket) ReceiveMaster() (console.Console, error) { + conn, err := c.l.Accept() + if err != nil { + return nil, err + } + defer conn.Close() + uc, ok := conn.(*net.UnixConn) + if !ok { + return nil, fmt.Errorf("received connection which was not a unix socket") + } + f, err := recvFd(uc) + if err != nil { + return nil, err + } + return console.ConsoleFromFile(f) +} + +// Close closes the unix socket +func (c *Socket) Close() error { + err := c.l.Close() + if c.rmdir { + if rerr := os.RemoveAll(filepath.Dir(c.Path())); err == nil { + err = rerr + } + } + return err +} diff --git a/src/runtime/vendor/github.com/containerd/go-runc/container.go b/src/runtime/vendor/github.com/containerd/go-runc/container.go new file mode 100644 index 000000000..107381a55 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/container.go @@ -0,0 +1,30 @@ +/* + Copyright The containerd Authors. + + 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 runc + +import "time" + +// Container hold information for a runc container +type Container struct { + ID string `json:"id"` + Pid int `json:"pid"` + Status string `json:"status"` + Bundle string `json:"bundle"` + Rootfs string `json:"rootfs"` + Created time.Time `json:"created"` + Annotations map[string]string `json:"annotations"` +} diff --git a/src/runtime/vendor/github.com/containerd/go-runc/events.go b/src/runtime/vendor/github.com/containerd/go-runc/events.go new file mode 100644 index 000000000..d610aeb34 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/events.go @@ -0,0 +1,100 @@ +/* + Copyright The containerd Authors. + + 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 runc + +type Event struct { + // Type are the event type generated by runc + // If the type is "error" then check the Err field on the event for + // the actual error + Type string `json:"type"` + ID string `json:"id"` + Stats *Stats `json:"data,omitempty"` + // Err has a read error if we were unable to decode the event from runc + Err error `json:"-"` +} + +type Stats struct { + Cpu Cpu `json:"cpu"` + Memory Memory `json:"memory"` + Pids Pids `json:"pids"` + Blkio Blkio `json:"blkio"` + Hugetlb map[string]Hugetlb `json:"hugetlb"` +} + +type Hugetlb struct { + Usage uint64 `json:"usage,omitempty"` + Max uint64 `json:"max,omitempty"` + Failcnt uint64 `json:"failcnt"` +} + +type BlkioEntry struct { + Major uint64 `json:"major,omitempty"` + Minor uint64 `json:"minor,omitempty"` + Op string `json:"op,omitempty"` + Value uint64 `json:"value,omitempty"` +} + +type Blkio struct { + IoServiceBytesRecursive []BlkioEntry `json:"ioServiceBytesRecursive,omitempty"` + IoServicedRecursive []BlkioEntry `json:"ioServicedRecursive,omitempty"` + IoQueuedRecursive []BlkioEntry `json:"ioQueueRecursive,omitempty"` + IoServiceTimeRecursive []BlkioEntry `json:"ioServiceTimeRecursive,omitempty"` + IoWaitTimeRecursive []BlkioEntry `json:"ioWaitTimeRecursive,omitempty"` + IoMergedRecursive []BlkioEntry `json:"ioMergedRecursive,omitempty"` + IoTimeRecursive []BlkioEntry `json:"ioTimeRecursive,omitempty"` + SectorsRecursive []BlkioEntry `json:"sectorsRecursive,omitempty"` +} + +type Pids struct { + Current uint64 `json:"current,omitempty"` + Limit uint64 `json:"limit,omitempty"` +} + +type Throttling struct { + Periods uint64 `json:"periods,omitempty"` + ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"` + ThrottledTime uint64 `json:"throttledTime,omitempty"` +} + +type CpuUsage struct { + // Units: nanoseconds. + Total uint64 `json:"total,omitempty"` + Percpu []uint64 `json:"percpu,omitempty"` + Kernel uint64 `json:"kernel"` + User uint64 `json:"user"` +} + +type Cpu struct { + Usage CpuUsage `json:"usage,omitempty"` + Throttling Throttling `json:"throttling,omitempty"` +} + +type MemoryEntry struct { + Limit uint64 `json:"limit"` + Usage uint64 `json:"usage,omitempty"` + Max uint64 `json:"max,omitempty"` + Failcnt uint64 `json:"failcnt"` +} + +type Memory struct { + Cache uint64 `json:"cache,omitempty"` + Usage MemoryEntry `json:"usage,omitempty"` + Swap MemoryEntry `json:"swap,omitempty"` + Kernel MemoryEntry `json:"kernel,omitempty"` + KernelTCP MemoryEntry `json:"kernelTCP,omitempty"` + Raw map[string]uint64 `json:"raw,omitempty"` +} diff --git a/src/runtime/vendor/github.com/containerd/go-runc/go.mod b/src/runtime/vendor/github.com/containerd/go-runc/go.mod new file mode 100644 index 000000000..d833ee160 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/go.mod @@ -0,0 +1,10 @@ +module github.com/containerd/go-runc + +go 1.13 + +require ( + github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e + github.com/opencontainers/runtime-spec v1.0.1 + github.com/pkg/errors v0.8.1 + golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 +) diff --git a/src/runtime/vendor/github.com/containerd/go-runc/go.sum b/src/runtime/vendor/github.com/containerd/go-runc/go.sum new file mode 100644 index 000000000..f7d00e37f --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/go.sum @@ -0,0 +1,9 @@ +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e h1:GdiIYd8ZDOrT++e1NjhSD4rGt9zaJukHm4rt5F4mRQc= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/opencontainers/runtime-spec v1.0.1 h1:wY4pOY8fBdSIvs9+IDHC55thBuEulhzfSgKeC1yFvzQ= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/src/runtime/vendor/github.com/containerd/go-runc/io.go b/src/runtime/vendor/github.com/containerd/go-runc/io.go new file mode 100644 index 000000000..6cf0410c9 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/io.go @@ -0,0 +1,218 @@ +/* + Copyright The containerd Authors. + + 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 runc + +import ( + "io" + "os" + "os/exec" +) + +type IO interface { + io.Closer + Stdin() io.WriteCloser + Stdout() io.ReadCloser + Stderr() io.ReadCloser + Set(*exec.Cmd) +} + +type StartCloser interface { + CloseAfterStart() error +} + +// IOOpt sets I/O creation options +type IOOpt func(*IOOption) + +// IOOption holds I/O creation options +type IOOption struct { + OpenStdin bool + OpenStdout bool + OpenStderr bool +} + +func defaultIOOption() *IOOption { + return &IOOption{ + OpenStdin: true, + OpenStdout: true, + OpenStderr: true, + } +} + +func newPipe() (*pipe, error) { + r, w, err := os.Pipe() + if err != nil { + return nil, err + } + return &pipe{ + r: r, + w: w, + }, nil +} + +type pipe struct { + r *os.File + w *os.File +} + +func (p *pipe) Close() error { + err := p.w.Close() + if rerr := p.r.Close(); err == nil { + err = rerr + } + return err +} + +type pipeIO struct { + in *pipe + out *pipe + err *pipe +} + +func (i *pipeIO) Stdin() io.WriteCloser { + if i.in == nil { + return nil + } + return i.in.w +} + +func (i *pipeIO) Stdout() io.ReadCloser { + if i.out == nil { + return nil + } + return i.out.r +} + +func (i *pipeIO) Stderr() io.ReadCloser { + if i.err == nil { + return nil + } + return i.err.r +} + +func (i *pipeIO) Close() error { + var err error + for _, v := range []*pipe{ + i.in, + i.out, + i.err, + } { + if v != nil { + if cerr := v.Close(); err == nil { + err = cerr + } + } + } + return err +} + +func (i *pipeIO) CloseAfterStart() error { + for _, f := range []*pipe{ + i.out, + i.err, + } { + if f != nil { + f.w.Close() + } + } + return nil +} + +// Set sets the io to the exec.Cmd +func (i *pipeIO) Set(cmd *exec.Cmd) { + if i.in != nil { + cmd.Stdin = i.in.r + } + if i.out != nil { + cmd.Stdout = i.out.w + } + if i.err != nil { + cmd.Stderr = i.err.w + } +} + +func NewSTDIO() (IO, error) { + return &stdio{}, nil +} + +type stdio struct { +} + +func (s *stdio) Close() error { + return nil +} + +func (s *stdio) Set(cmd *exec.Cmd) { + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr +} + +func (s *stdio) Stdin() io.WriteCloser { + return os.Stdin +} + +func (s *stdio) Stdout() io.ReadCloser { + return os.Stdout +} + +func (s *stdio) Stderr() io.ReadCloser { + return os.Stderr +} + +// NewNullIO returns IO setup for /dev/null use with runc +func NewNullIO() (IO, error) { + f, err := os.Open(os.DevNull) + if err != nil { + return nil, err + } + return &nullIO{ + devNull: f, + }, nil +} + +type nullIO struct { + devNull *os.File +} + +func (n *nullIO) Close() error { + // this should be closed after start but if not + // make sure we close the file but don't return the error + n.devNull.Close() + return nil +} + +func (n *nullIO) Stdin() io.WriteCloser { + return nil +} + +func (n *nullIO) Stdout() io.ReadCloser { + return nil +} + +func (n *nullIO) Stderr() io.ReadCloser { + return nil +} + +func (n *nullIO) Set(c *exec.Cmd) { + // don't set STDIN here + c.Stdout = n.devNull + c.Stderr = n.devNull +} + +func (n *nullIO) CloseAfterStart() error { + return n.devNull.Close() +} diff --git a/src/runtime/vendor/github.com/containerd/go-runc/io_unix.go b/src/runtime/vendor/github.com/containerd/go-runc/io_unix.go new file mode 100644 index 000000000..567cd072e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/io_unix.go @@ -0,0 +1,76 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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 runc + +import ( + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +// NewPipeIO creates pipe pairs to be used with runc +func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) { + option := defaultIOOption() + for _, o := range opts { + o(option) + } + var ( + pipes []*pipe + stdin, stdout, stderr *pipe + ) + // cleanup in case of an error + defer func() { + if err != nil { + for _, p := range pipes { + p.Close() + } + } + }() + if option.OpenStdin { + if stdin, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stdin) + if err = unix.Fchown(int(stdin.r.Fd()), uid, gid); err != nil { + return nil, errors.Wrap(err, "failed to chown stdin") + } + } + if option.OpenStdout { + if stdout, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stdout) + if err = unix.Fchown(int(stdout.w.Fd()), uid, gid); err != nil { + return nil, errors.Wrap(err, "failed to chown stdout") + } + } + if option.OpenStderr { + if stderr, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stderr) + if err = unix.Fchown(int(stderr.w.Fd()), uid, gid); err != nil { + return nil, errors.Wrap(err, "failed to chown stderr") + } + } + return &pipeIO{ + in: stdin, + out: stdout, + err: stderr, + }, nil +} diff --git a/src/runtime/vendor/github.com/containerd/go-runc/io_windows.go b/src/runtime/vendor/github.com/containerd/go-runc/io_windows.go new file mode 100644 index 000000000..fc56ac4f3 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/io_windows.go @@ -0,0 +1,62 @@ +// +build windows + +/* + Copyright The containerd Authors. + + 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 runc + +// NewPipeIO creates pipe pairs to be used with runc +func NewPipeIO(opts ...IOOpt) (i IO, err error) { + option := defaultIOOption() + for _, o := range opts { + o(option) + } + var ( + pipes []*pipe + stdin, stdout, stderr *pipe + ) + // cleanup in case of an error + defer func() { + if err != nil { + for _, p := range pipes { + p.Close() + } + } + }() + if option.OpenStdin { + if stdin, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stdin) + } + if option.OpenStdout { + if stdout, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stdout) + } + if option.OpenStderr { + if stderr, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stderr) + } + return &pipeIO{ + in: stdin, + out: stdout, + err: stderr, + }, nil +} diff --git a/src/runtime/vendor/github.com/containerd/go-runc/monitor.go b/src/runtime/vendor/github.com/containerd/go-runc/monitor.go new file mode 100644 index 000000000..ff06a3fca --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/monitor.go @@ -0,0 +1,76 @@ +/* + Copyright The containerd Authors. + + 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 runc + +import ( + "os/exec" + "syscall" + "time" +) + +var Monitor ProcessMonitor = &defaultMonitor{} + +type Exit struct { + Timestamp time.Time + Pid int + Status int +} + +// ProcessMonitor is an interface for process monitoring +// +// It allows daemons using go-runc to have a SIGCHLD handler +// to handle exits without introducing races between the handler +// and go's exec.Cmd +// These methods should match the methods exposed by exec.Cmd to provide +// a consistent experience for the caller +type ProcessMonitor interface { + Start(*exec.Cmd) (chan Exit, error) + Wait(*exec.Cmd, chan Exit) (int, error) +} + +type defaultMonitor struct { +} + +func (m *defaultMonitor) Start(c *exec.Cmd) (chan Exit, error) { + if err := c.Start(); err != nil { + return nil, err + } + ec := make(chan Exit, 1) + go func() { + var status int + if err := c.Wait(); err != nil { + status = 255 + if exitErr, ok := err.(*exec.ExitError); ok { + if ws, ok := exitErr.Sys().(syscall.WaitStatus); ok { + status = ws.ExitStatus() + } + } + } + ec <- Exit{ + Timestamp: time.Now(), + Pid: c.Process.Pid, + Status: status, + } + close(ec) + }() + return ec, nil +} + +func (m *defaultMonitor) Wait(c *exec.Cmd, ec chan Exit) (int, error) { + e := <-ec + return e.Status, nil +} diff --git a/src/runtime/vendor/github.com/containerd/go-runc/runc.go b/src/runtime/vendor/github.com/containerd/go-runc/runc.go new file mode 100644 index 000000000..c3a95af25 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/runc.go @@ -0,0 +1,715 @@ +/* + Copyright The containerd Authors. + + 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 runc + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "syscall" + "time" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +// Format is the type of log formatting options avaliable +type Format string + +// TopBody represents the structured data of the full ps output +type TopResults struct { + // Processes running in the container, where each is process is an array of values corresponding to the headers + Processes [][]string `json:"Processes"` + + // Headers are the names of the columns + Headers []string `json:"Headers"` +} + +const ( + none Format = "" + JSON Format = "json" + Text Format = "text" + // DefaultCommand is the default command for Runc + DefaultCommand = "runc" +) + +// Runc is the client to the runc cli +type Runc struct { + //If command is empty, DefaultCommand is used + Command string + Root string + Debug bool + Log string + LogFormat Format + PdeathSignal syscall.Signal + Setpgid bool + Criu string + SystemdCgroup bool + Rootless *bool // nil stands for "auto" +} + +// List returns all containers created inside the provided runc root directory +func (r *Runc) List(context context.Context) ([]*Container, error) { + data, err := cmdOutput(r.command(context, "list", "--format=json"), false) + defer putBuf(data) + if err != nil { + return nil, err + } + var out []*Container + if err := json.Unmarshal(data.Bytes(), &out); err != nil { + return nil, err + } + return out, nil +} + +// State returns the state for the container provided by id +func (r *Runc) State(context context.Context, id string) (*Container, error) { + data, err := cmdOutput(r.command(context, "state", id), true) + defer putBuf(data) + if err != nil { + return nil, fmt.Errorf("%s: %s", err, data.String()) + } + var c Container + if err := json.Unmarshal(data.Bytes(), &c); err != nil { + return nil, err + } + return &c, nil +} + +type ConsoleSocket interface { + Path() string +} + +type CreateOpts struct { + IO + // PidFile is a path to where a pid file should be created + PidFile string + ConsoleSocket ConsoleSocket + Detach bool + NoPivot bool + NoNewKeyring bool + ExtraFiles []*os.File +} + +func (o *CreateOpts) args() (out []string, err error) { + if o.PidFile != "" { + abs, err := filepath.Abs(o.PidFile) + if err != nil { + return nil, err + } + out = append(out, "--pid-file", abs) + } + if o.ConsoleSocket != nil { + out = append(out, "--console-socket", o.ConsoleSocket.Path()) + } + if o.NoPivot { + out = append(out, "--no-pivot") + } + if o.NoNewKeyring { + out = append(out, "--no-new-keyring") + } + if o.Detach { + out = append(out, "--detach") + } + if o.ExtraFiles != nil { + out = append(out, "--preserve-fds", strconv.Itoa(len(o.ExtraFiles))) + } + return out, nil +} + +// Create creates a new container and returns its pid if it was created successfully +func (r *Runc) Create(context context.Context, id, bundle string, opts *CreateOpts) error { + args := []string{"create", "--bundle", bundle} + if opts != nil { + oargs, err := opts.args() + if err != nil { + return err + } + args = append(args, oargs...) + } + cmd := r.command(context, append(args, id)...) + if opts != nil && opts.IO != nil { + opts.Set(cmd) + } + cmd.ExtraFiles = opts.ExtraFiles + + if cmd.Stdout == nil && cmd.Stderr == nil { + data, err := cmdOutput(cmd, true) + defer putBuf(data) + if err != nil { + return fmt.Errorf("%s: %s", err, data.String()) + } + return nil + } + ec, err := Monitor.Start(cmd) + if err != nil { + return err + } + if opts != nil && opts.IO != nil { + if c, ok := opts.IO.(StartCloser); ok { + if err := c.CloseAfterStart(); err != nil { + return err + } + } + } + status, err := Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate successfully", cmd.Args[0]) + } + return err +} + +// Start will start an already created container +func (r *Runc) Start(context context.Context, id string) error { + return r.runOrError(r.command(context, "start", id)) +} + +type ExecOpts struct { + IO + PidFile string + ConsoleSocket ConsoleSocket + Detach bool +} + +func (o *ExecOpts) args() (out []string, err error) { + if o.ConsoleSocket != nil { + out = append(out, "--console-socket", o.ConsoleSocket.Path()) + } + if o.Detach { + out = append(out, "--detach") + } + if o.PidFile != "" { + abs, err := filepath.Abs(o.PidFile) + if err != nil { + return nil, err + } + out = append(out, "--pid-file", abs) + } + return out, nil +} + +// Exec executres and additional process inside the container based on a full +// OCI Process specification +func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts *ExecOpts) error { + f, err := ioutil.TempFile(os.Getenv("XDG_RUNTIME_DIR"), "runc-process") + if err != nil { + return err + } + defer os.Remove(f.Name()) + err = json.NewEncoder(f).Encode(spec) + f.Close() + if err != nil { + return err + } + args := []string{"exec", "--process", f.Name()} + if opts != nil { + oargs, err := opts.args() + if err != nil { + return err + } + args = append(args, oargs...) + } + cmd := r.command(context, append(args, id)...) + if opts != nil && opts.IO != nil { + opts.Set(cmd) + } + if cmd.Stdout == nil && cmd.Stderr == nil { + data, err := cmdOutput(cmd, true) + defer putBuf(data) + if err != nil { + return fmt.Errorf("%s: %s", err, data.String()) + } + return nil + } + ec, err := Monitor.Start(cmd) + if err != nil { + return err + } + if opts != nil && opts.IO != nil { + if c, ok := opts.IO.(StartCloser); ok { + if err := c.CloseAfterStart(); err != nil { + return err + } + } + } + status, err := Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate successfully", cmd.Args[0]) + } + return err +} + +// Run runs the create, start, delete lifecycle of the container +// and returns its exit status after it has exited +func (r *Runc) Run(context context.Context, id, bundle string, opts *CreateOpts) (int, error) { + args := []string{"run", "--bundle", bundle} + if opts != nil { + oargs, err := opts.args() + if err != nil { + return -1, err + } + args = append(args, oargs...) + } + cmd := r.command(context, append(args, id)...) + if opts != nil && opts.IO != nil { + opts.Set(cmd) + } + ec, err := Monitor.Start(cmd) + if err != nil { + return -1, err + } + status, err := Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate successfully", cmd.Args[0]) + } + return status, err +} + +type DeleteOpts struct { + Force bool +} + +func (o *DeleteOpts) args() (out []string) { + if o.Force { + out = append(out, "--force") + } + return out +} + +// Delete deletes the container +func (r *Runc) Delete(context context.Context, id string, opts *DeleteOpts) error { + args := []string{"delete"} + if opts != nil { + args = append(args, opts.args()...) + } + return r.runOrError(r.command(context, append(args, id)...)) +} + +// KillOpts specifies options for killing a container and its processes +type KillOpts struct { + All bool +} + +func (o *KillOpts) args() (out []string) { + if o.All { + out = append(out, "--all") + } + return out +} + +// Kill sends the specified signal to the container +func (r *Runc) Kill(context context.Context, id string, sig int, opts *KillOpts) error { + args := []string{ + "kill", + } + if opts != nil { + args = append(args, opts.args()...) + } + return r.runOrError(r.command(context, append(args, id, strconv.Itoa(sig))...)) +} + +// Stats return the stats for a container like cpu, memory, and io +func (r *Runc) Stats(context context.Context, id string) (*Stats, error) { + cmd := r.command(context, "events", "--stats", id) + rd, err := cmd.StdoutPipe() + if err != nil { + return nil, err + } + ec, err := Monitor.Start(cmd) + if err != nil { + return nil, err + } + defer func() { + rd.Close() + Monitor.Wait(cmd, ec) + }() + var e Event + if err := json.NewDecoder(rd).Decode(&e); err != nil { + return nil, err + } + return e.Stats, nil +} + +// Events returns an event stream from runc for a container with stats and OOM notifications +func (r *Runc) Events(context context.Context, id string, interval time.Duration) (chan *Event, error) { + cmd := r.command(context, "events", fmt.Sprintf("--interval=%ds", int(interval.Seconds())), id) + rd, err := cmd.StdoutPipe() + if err != nil { + return nil, err + } + ec, err := Monitor.Start(cmd) + if err != nil { + rd.Close() + return nil, err + } + var ( + dec = json.NewDecoder(rd) + c = make(chan *Event, 128) + ) + go func() { + defer func() { + close(c) + rd.Close() + Monitor.Wait(cmd, ec) + }() + for { + var e Event + if err := dec.Decode(&e); err != nil { + if err == io.EOF { + return + } + e = Event{ + Type: "error", + Err: err, + } + } + c <- &e + } + }() + return c, nil +} + +// Pause the container with the provided id +func (r *Runc) Pause(context context.Context, id string) error { + return r.runOrError(r.command(context, "pause", id)) +} + +// Resume the container with the provided id +func (r *Runc) Resume(context context.Context, id string) error { + return r.runOrError(r.command(context, "resume", id)) +} + +// Ps lists all the processes inside the container returning their pids +func (r *Runc) Ps(context context.Context, id string) ([]int, error) { + data, err := cmdOutput(r.command(context, "ps", "--format", "json", id), true) + defer putBuf(data) + if err != nil { + return nil, fmt.Errorf("%s: %s", err, data.String()) + } + var pids []int + if err := json.Unmarshal(data.Bytes(), &pids); err != nil { + return nil, err + } + return pids, nil +} + +// Top lists all the processes inside the container returning the full ps data +func (r *Runc) Top(context context.Context, id string, psOptions string) (*TopResults, error) { + data, err := cmdOutput(r.command(context, "ps", "--format", "table", id, psOptions), true) + defer putBuf(data) + if err != nil { + return nil, fmt.Errorf("%s: %s", err, data.String()) + } + + topResults, err := ParsePSOutput(data.Bytes()) + if err != nil { + return nil, fmt.Errorf("%s: ", err) + } + return topResults, nil +} + +type CheckpointOpts struct { + // ImagePath is the path for saving the criu image file + ImagePath string + // WorkDir is the working directory for criu + WorkDir string + // ParentPath is the path for previous image files from a pre-dump + ParentPath string + // AllowOpenTCP allows open tcp connections to be checkpointed + AllowOpenTCP bool + // AllowExternalUnixSockets allows external unix sockets to be checkpointed + AllowExternalUnixSockets bool + // AllowTerminal allows the terminal(pty) to be checkpointed with a container + AllowTerminal bool + // CriuPageServer is the address:port for the criu page server + CriuPageServer string + // FileLocks handle file locks held by the container + FileLocks bool + // Cgroups is the cgroup mode for how to handle the checkpoint of a container's cgroups + Cgroups CgroupMode + // EmptyNamespaces creates a namespace for the container but does not save its properties + // Provide the namespaces you wish to be checkpointed without their settings on restore + EmptyNamespaces []string +} + +type CgroupMode string + +const ( + Soft CgroupMode = "soft" + Full CgroupMode = "full" + Strict CgroupMode = "strict" +) + +func (o *CheckpointOpts) args() (out []string) { + if o.ImagePath != "" { + out = append(out, "--image-path", o.ImagePath) + } + if o.WorkDir != "" { + out = append(out, "--work-path", o.WorkDir) + } + if o.ParentPath != "" { + out = append(out, "--parent-path", o.ParentPath) + } + if o.AllowOpenTCP { + out = append(out, "--tcp-established") + } + if o.AllowExternalUnixSockets { + out = append(out, "--ext-unix-sk") + } + if o.AllowTerminal { + out = append(out, "--shell-job") + } + if o.CriuPageServer != "" { + out = append(out, "--page-server", o.CriuPageServer) + } + if o.FileLocks { + out = append(out, "--file-locks") + } + if string(o.Cgroups) != "" { + out = append(out, "--manage-cgroups-mode", string(o.Cgroups)) + } + for _, ns := range o.EmptyNamespaces { + out = append(out, "--empty-ns", ns) + } + return out +} + +type CheckpointAction func([]string) []string + +// LeaveRunning keeps the container running after the checkpoint has been completed +func LeaveRunning(args []string) []string { + return append(args, "--leave-running") +} + +// PreDump allows a pre-dump of the checkpoint to be made and completed later +func PreDump(args []string) []string { + return append(args, "--pre-dump") +} + +// Checkpoint allows you to checkpoint a container using criu +func (r *Runc) Checkpoint(context context.Context, id string, opts *CheckpointOpts, actions ...CheckpointAction) error { + args := []string{"checkpoint"} + if opts != nil { + args = append(args, opts.args()...) + } + for _, a := range actions { + args = a(args) + } + return r.runOrError(r.command(context, append(args, id)...)) +} + +type RestoreOpts struct { + CheckpointOpts + IO + + Detach bool + PidFile string + NoSubreaper bool + NoPivot bool + ConsoleSocket ConsoleSocket +} + +func (o *RestoreOpts) args() ([]string, error) { + out := o.CheckpointOpts.args() + if o.Detach { + out = append(out, "--detach") + } + if o.PidFile != "" { + abs, err := filepath.Abs(o.PidFile) + if err != nil { + return nil, err + } + out = append(out, "--pid-file", abs) + } + if o.ConsoleSocket != nil { + out = append(out, "--console-socket", o.ConsoleSocket.Path()) + } + if o.NoPivot { + out = append(out, "--no-pivot") + } + if o.NoSubreaper { + out = append(out, "-no-subreaper") + } + return out, nil +} + +// Restore restores a container with the provide id from an existing checkpoint +func (r *Runc) Restore(context context.Context, id, bundle string, opts *RestoreOpts) (int, error) { + args := []string{"restore"} + if opts != nil { + oargs, err := opts.args() + if err != nil { + return -1, err + } + args = append(args, oargs...) + } + args = append(args, "--bundle", bundle) + cmd := r.command(context, append(args, id)...) + if opts != nil && opts.IO != nil { + opts.Set(cmd) + } + ec, err := Monitor.Start(cmd) + if err != nil { + return -1, err + } + if opts != nil && opts.IO != nil { + if c, ok := opts.IO.(StartCloser); ok { + if err := c.CloseAfterStart(); err != nil { + return -1, err + } + } + } + status, err := Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate successfully", cmd.Args[0]) + } + return status, err +} + +// Update updates the current container with the provided resource spec +func (r *Runc) Update(context context.Context, id string, resources *specs.LinuxResources) error { + buf := getBuf() + defer putBuf(buf) + + if err := json.NewEncoder(buf).Encode(resources); err != nil { + return err + } + args := []string{"update", "--resources", "-", id} + cmd := r.command(context, args...) + cmd.Stdin = buf + return r.runOrError(cmd) +} + +var ErrParseRuncVersion = errors.New("unable to parse runc version") + +type Version struct { + Runc string + Commit string + Spec string +} + +// Version returns the runc and runtime-spec versions +func (r *Runc) Version(context context.Context) (Version, error) { + data, err := cmdOutput(r.command(context, "--version"), false) + defer putBuf(data) + if err != nil { + return Version{}, err + } + return parseVersion(data.Bytes()) +} + +func parseVersion(data []byte) (Version, error) { + var v Version + parts := strings.Split(strings.TrimSpace(string(data)), "\n") + + if len(parts) > 0 { + if !strings.HasPrefix(parts[0], "runc version ") { + return v, nil + } + v.Runc = parts[0][13:] + + for _, part := range parts[1:] { + if strings.HasPrefix(part, "commit: ") { + v.Commit = part[8:] + } else if strings.HasPrefix(part, "spec: ") { + v.Spec = part[6:] + } + } + } + + return v, nil +} + +func (r *Runc) args() (out []string) { + if r.Root != "" { + out = append(out, "--root", r.Root) + } + if r.Debug { + out = append(out, "--debug") + } + if r.Log != "" { + out = append(out, "--log", r.Log) + } + if r.LogFormat != none { + out = append(out, "--log-format", string(r.LogFormat)) + } + if r.Criu != "" { + out = append(out, "--criu", r.Criu) + } + if r.SystemdCgroup { + out = append(out, "--systemd-cgroup") + } + if r.Rootless != nil { + // nil stands for "auto" (differs from explicit "false") + out = append(out, "--rootless="+strconv.FormatBool(*r.Rootless)) + } + return out +} + +// runOrError will run the provided command. If an error is +// encountered and neither Stdout or Stderr was set the error and the +// stderr of the command will be returned in the format of : +// +func (r *Runc) runOrError(cmd *exec.Cmd) error { + if cmd.Stdout != nil || cmd.Stderr != nil { + ec, err := Monitor.Start(cmd) + if err != nil { + return err + } + status, err := Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate successfully", cmd.Args[0]) + } + return err + } + data, err := cmdOutput(cmd, true) + defer putBuf(data) + if err != nil { + return fmt.Errorf("%s: %s", err, data.String()) + } + return nil +} + +// callers of cmdOutput are expected to call putBuf on the returned Buffer +// to ensure it is released back to the shared pool after use. +func cmdOutput(cmd *exec.Cmd, combined bool) (*bytes.Buffer, error) { + b := getBuf() + + cmd.Stdout = b + if combined { + cmd.Stderr = b + } + ec, err := Monitor.Start(cmd) + if err != nil { + return nil, err + } + + status, err := Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate successfully", cmd.Args[0]) + } + + return b, err +} diff --git a/src/runtime/vendor/github.com/containerd/go-runc/utils.go b/src/runtime/vendor/github.com/containerd/go-runc/utils.go new file mode 100644 index 000000000..948b6336a --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/go-runc/utils.go @@ -0,0 +1,111 @@ +/* + Copyright The containerd Authors. + + 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 runc + +import ( + "bytes" + "io/ioutil" + "strconv" + "strings" + "sync" + "syscall" +) + +// ReadPidFile reads the pid file at the provided path and returns +// the pid or an error if the read and conversion is unsuccessful +func ReadPidFile(path string) (int, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return -1, err + } + return strconv.Atoi(string(data)) +} + +const exitSignalOffset = 128 + +// exitStatus returns the correct exit status for a process based on if it +// was signaled or exited cleanly +func exitStatus(status syscall.WaitStatus) int { + if status.Signaled() { + return exitSignalOffset + int(status.Signal()) + } + return status.ExitStatus() +} + +var bytesBufferPool = sync.Pool{ + New: func() interface{} { + return bytes.NewBuffer(nil) + }, +} + +func getBuf() *bytes.Buffer { + return bytesBufferPool.Get().(*bytes.Buffer) +} + +func putBuf(b *bytes.Buffer) { + if b == nil { + return + } + + b.Reset() + bytesBufferPool.Put(b) +} + +// fieldsASCII is similar to strings.Fields but only allows ASCII whitespaces +func fieldsASCII(s string) []string { + fn := func(r rune) bool { + switch r { + case '\t', '\n', '\f', '\r', ' ': + return true + } + return false + } + return strings.FieldsFunc(s, fn) +} + +// ParsePSOutput parses the runtime's ps raw output and returns a TopResults +func ParsePSOutput(output []byte) (*TopResults, error) { + topResults := &TopResults{} + + lines := strings.Split(string(output), "\n") + topResults.Headers = fieldsASCII(lines[0]) + + pidIndex := -1 + for i, name := range topResults.Headers { + if name == "PID" { + pidIndex = i + } + } + + for _, line := range lines[1:] { + if len(line) == 0 { + continue + } + + fields := fieldsASCII(line) + + if fields[pidIndex] == "-" { + continue + } + + process := fields[:len(topResults.Headers)-1] + process = append(process, strings.Join(fields[len(topResults.Headers)-1:], " ")) + topResults.Processes = append(topResults.Processes, process) + + } + return topResults, nil +} diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/.gitignore b/src/runtime/vendor/github.com/containerd/ttrpc/.gitignore new file mode 100644 index 000000000..a1338d685 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/.gitignore @@ -0,0 +1,14 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/.travis.yml b/src/runtime/vendor/github.com/containerd/ttrpc/.travis.yml new file mode 100644 index 000000000..4df13c9db --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/.travis.yml @@ -0,0 +1,17 @@ +language: go + +go: + - "1.12.x" + +install: + - go get -u github.com/vbatts/git-validation + - go get -u github.com/kunalkushwaha/ltag + - go get -t ./... + +before_script: + - pushd ..; git clone https://github.com/containerd/project; popd + +script: + - DCO_VERBOSITY=-q ../project/script/validate/dco + - ../project/script/validate/fileheader ../project/ + - go test -race -v ./... diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/LICENSE b/src/runtime/vendor/github.com/containerd/ttrpc/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/README.md b/src/runtime/vendor/github.com/containerd/ttrpc/README.md new file mode 100644 index 000000000..c345c844e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/README.md @@ -0,0 +1,62 @@ +# ttrpc + +[![Build Status](https://travis-ci.org/containerd/ttrpc.svg?branch=master)](https://travis-ci.org/containerd/ttrpc) + +GRPC for low-memory environments. + +The existing grpc-go project requires a lot of memory overhead for importing +packages and at runtime. While this is great for many services with low density +requirements, this can be a problem when running a large number of services on +a single machine or on a machine with a small amount of memory. + +Using the same GRPC definitions, this project reduces the binary size and +protocol overhead required. We do this by eliding the `net/http`, `net/http2` +and `grpc` package used by grpc replacing it with a lightweight framing +protocol. The result are smaller binaries that use less resident memory with +the same ease of use as GRPC. + +Please note that while this project supports generating either end of the +protocol, the generated service definitions will be incompatible with regular +GRPC services, as they do not speak the same protocol. + +# Usage + +Create a gogo vanity binary (see +[`cmd/protoc-gen-gogottrpc/main.go`](cmd/protoc-gen-gogottrpc/main.go) for an +example with the ttrpc plugin enabled. + +It's recommended to use [`protobuild`](https://github.com//stevvooe/protobuild) +to build the protobufs for this project, but this will work with protoc +directly, if required. + +# Differences from GRPC + +- The protocol stack has been replaced with a lighter protocol that doesn't + require http, http2 and tls. +- The client and server interface are identical whereas in GRPC there is a + client and server interface that are different. +- The Go stdlib context package is used instead. +- No support for streams yet. + +# Status + +Very new. YMMV. + +TODO: + +- [X] Plumb error codes and GRPC status +- [X] Remove use of any type and dependency on typeurl package +- [X] Ensure that protocol can support streaming in the future +- [ ] Document protocol layout +- [ ] Add testing under concurrent load to ensure +- [ ] Verify connection error handling + +# Project details + +ttrpc is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/channel.go b/src/runtime/vendor/github.com/containerd/ttrpc/channel.go new file mode 100644 index 000000000..aa8c9541c --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/channel.go @@ -0,0 +1,153 @@ +/* + Copyright The containerd Authors. + + 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 ttrpc + +import ( + "bufio" + "encoding/binary" + "io" + "net" + "sync" + + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const ( + messageHeaderLength = 10 + messageLengthMax = 4 << 20 +) + +type messageType uint8 + +const ( + messageTypeRequest messageType = 0x1 + messageTypeResponse messageType = 0x2 +) + +// messageHeader represents the fixed-length message header of 10 bytes sent +// with every request. +type messageHeader struct { + Length uint32 // length excluding this header. b[:4] + StreamID uint32 // identifies which request stream message is a part of. b[4:8] + Type messageType // message type b[8] + Flags uint8 // reserved b[9] +} + +func readMessageHeader(p []byte, r io.Reader) (messageHeader, error) { + _, err := io.ReadFull(r, p[:messageHeaderLength]) + if err != nil { + return messageHeader{}, err + } + + return messageHeader{ + Length: binary.BigEndian.Uint32(p[:4]), + StreamID: binary.BigEndian.Uint32(p[4:8]), + Type: messageType(p[8]), + Flags: p[9], + }, nil +} + +func writeMessageHeader(w io.Writer, p []byte, mh messageHeader) error { + binary.BigEndian.PutUint32(p[:4], mh.Length) + binary.BigEndian.PutUint32(p[4:8], mh.StreamID) + p[8] = byte(mh.Type) + p[9] = mh.Flags + + _, err := w.Write(p[:]) + return err +} + +var buffers sync.Pool + +type channel struct { + conn net.Conn + bw *bufio.Writer + br *bufio.Reader + hrbuf [messageHeaderLength]byte // avoid alloc when reading header + hwbuf [messageHeaderLength]byte +} + +func newChannel(conn net.Conn) *channel { + return &channel{ + conn: conn, + bw: bufio.NewWriter(conn), + br: bufio.NewReader(conn), + } +} + +// recv a message from the channel. The returned buffer contains the message. +// +// If a valid grpc status is returned, the message header +// returned will be valid and caller should send that along to +// the correct consumer. The bytes on the underlying channel +// will be discarded. +func (ch *channel) recv() (messageHeader, []byte, error) { + mh, err := readMessageHeader(ch.hrbuf[:], ch.br) + if err != nil { + return messageHeader{}, nil, err + } + + if mh.Length > uint32(messageLengthMax) { + if _, err := ch.br.Discard(int(mh.Length)); err != nil { + return mh, nil, errors.Wrapf(err, "failed to discard after receiving oversized message") + } + + return mh, nil, status.Errorf(codes.ResourceExhausted, "message length %v exceed maximum message size of %v", mh.Length, messageLengthMax) + } + + p := ch.getmbuf(int(mh.Length)) + if _, err := io.ReadFull(ch.br, p); err != nil { + return messageHeader{}, nil, errors.Wrapf(err, "failed reading message") + } + + return mh, p, nil +} + +func (ch *channel) send(streamID uint32, t messageType, p []byte) error { + if err := writeMessageHeader(ch.bw, ch.hwbuf[:], messageHeader{Length: uint32(len(p)), StreamID: streamID, Type: t}); err != nil { + return err + } + + _, err := ch.bw.Write(p) + if err != nil { + return err + } + + return ch.bw.Flush() +} + +func (ch *channel) getmbuf(size int) []byte { + // we can't use the standard New method on pool because we want to allocate + // based on size. + b, ok := buffers.Get().(*[]byte) + if !ok || cap(*b) < size { + // TODO(stevvooe): It may be better to allocate these in fixed length + // buckets to reduce fragmentation but its not clear that would help + // with performance. An ilogb approach or similar would work well. + bb := make([]byte, size) + b = &bb + } else { + *b = (*b)[:size] + } + return *b +} + +func (ch *channel) putmbuf(p []byte) { + buffers.Put(&p) +} diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/client.go b/src/runtime/vendor/github.com/containerd/ttrpc/client.go new file mode 100644 index 000000000..bdd1d12e7 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/client.go @@ -0,0 +1,350 @@ +/* + Copyright The containerd Authors. + + 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 ttrpc + +import ( + "context" + "io" + "net" + "os" + "strings" + "sync" + "syscall" + "time" + + "github.com/gogo/protobuf/proto" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// ErrClosed is returned by client methods when the underlying connection is +// closed. +var ErrClosed = errors.New("ttrpc: closed") + +// Client for a ttrpc server +type Client struct { + codec codec + conn net.Conn + channel *channel + calls chan *callRequest + + ctx context.Context + closed func() + + closeOnce sync.Once + userCloseFunc func() + + errOnce sync.Once + err error + interceptor UnaryClientInterceptor +} + +// ClientOpts configures a client +type ClientOpts func(c *Client) + +// WithOnClose sets the close func whenever the client's Close() method is called +func WithOnClose(onClose func()) ClientOpts { + return func(c *Client) { + c.userCloseFunc = onClose + } +} + +// WithUnaryClientInterceptor sets the provided client interceptor +func WithUnaryClientInterceptor(i UnaryClientInterceptor) ClientOpts { + return func(c *Client) { + c.interceptor = i + } +} + +func NewClient(conn net.Conn, opts ...ClientOpts) *Client { + ctx, cancel := context.WithCancel(context.Background()) + c := &Client{ + codec: codec{}, + conn: conn, + channel: newChannel(conn), + calls: make(chan *callRequest), + closed: cancel, + ctx: ctx, + userCloseFunc: func() {}, + interceptor: defaultClientInterceptor, + } + + for _, o := range opts { + o(c) + } + + go c.run() + return c +} + +type callRequest struct { + ctx context.Context + req *Request + resp *Response // response will be written back here + errs chan error // error written here on completion +} + +func (c *Client) Call(ctx context.Context, service, method string, req, resp interface{}) error { + payload, err := c.codec.Marshal(req) + if err != nil { + return err + } + + var ( + creq = &Request{ + Service: service, + Method: method, + Payload: payload, + } + + cresp = &Response{} + ) + + if metadata, ok := GetMetadata(ctx); ok { + metadata.setRequest(creq) + } + + if dl, ok := ctx.Deadline(); ok { + creq.TimeoutNano = dl.Sub(time.Now()).Nanoseconds() + } + + info := &UnaryClientInfo{ + FullMethod: fullPath(service, method), + } + if err := c.interceptor(ctx, creq, cresp, info, c.dispatch); err != nil { + return err + } + + if err := c.codec.Unmarshal(cresp.Payload, resp); err != nil { + return err + } + + if cresp.Status != nil && cresp.Status.Code != int32(codes.OK) { + return status.ErrorProto(cresp.Status) + } + return nil +} + +func (c *Client) dispatch(ctx context.Context, req *Request, resp *Response) error { + errs := make(chan error, 1) + call := &callRequest{ + ctx: ctx, + req: req, + resp: resp, + errs: errs, + } + + select { + case <-ctx.Done(): + return ctx.Err() + case c.calls <- call: + case <-c.ctx.Done(): + return c.error() + } + + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-errs: + return filterCloseErr(err) + case <-c.ctx.Done(): + return c.error() + } +} + +func (c *Client) Close() error { + c.closeOnce.Do(func() { + c.closed() + }) + return nil +} + +type message struct { + messageHeader + p []byte + err error +} + +type receiver struct { + wg *sync.WaitGroup + messages chan *message + err error +} + +func (r *receiver) run(ctx context.Context, c *channel) { + defer r.wg.Done() + + for { + select { + case <-ctx.Done(): + r.err = ctx.Err() + return + default: + mh, p, err := c.recv() + if err != nil { + _, ok := status.FromError(err) + if !ok { + // treat all errors that are not an rpc status as terminal. + // all others poison the connection. + r.err = filterCloseErr(err) + return + } + } + select { + case r.messages <- &message{ + messageHeader: mh, + p: p[:mh.Length], + err: err, + }: + case <-ctx.Done(): + r.err = ctx.Err() + return + } + } + } +} + +func (c *Client) run() { + var ( + streamID uint32 = 1 + waiters = make(map[uint32]*callRequest) + calls = c.calls + incoming = make(chan *message) + receiversDone = make(chan struct{}) + wg sync.WaitGroup + ) + + // broadcast the shutdown error to the remaining waiters. + abortWaiters := func(wErr error) { + for _, waiter := range waiters { + waiter.errs <- wErr + } + } + recv := &receiver{ + wg: &wg, + messages: incoming, + } + wg.Add(1) + + go func() { + wg.Wait() + close(receiversDone) + }() + go recv.run(c.ctx, c.channel) + + defer func() { + c.conn.Close() + c.userCloseFunc() + }() + + for { + select { + case call := <-calls: + if err := c.send(streamID, messageTypeRequest, call.req); err != nil { + call.errs <- err + continue + } + + waiters[streamID] = call + streamID += 2 // enforce odd client initiated request ids + case msg := <-incoming: + call, ok := waiters[msg.StreamID] + if !ok { + logrus.Errorf("ttrpc: received message for unknown channel %v", msg.StreamID) + continue + } + + call.errs <- c.recv(call.resp, msg) + delete(waiters, msg.StreamID) + case <-receiversDone: + // all the receivers have exited + if recv.err != nil { + c.setError(recv.err) + } + // don't return out, let the close of the context trigger the abort of waiters + c.Close() + case <-c.ctx.Done(): + abortWaiters(c.error()) + return + } + } +} + +func (c *Client) error() error { + c.errOnce.Do(func() { + if c.err == nil { + c.err = ErrClosed + } + }) + return c.err +} + +func (c *Client) setError(err error) { + c.errOnce.Do(func() { + c.err = err + }) +} + +func (c *Client) send(streamID uint32, mtype messageType, msg interface{}) error { + p, err := c.codec.Marshal(msg) + if err != nil { + return err + } + + return c.channel.send(streamID, mtype, p) +} + +func (c *Client) recv(resp *Response, msg *message) error { + if msg.err != nil { + return msg.err + } + + if msg.Type != messageTypeResponse { + return errors.New("unknown message type received") + } + + defer c.channel.putmbuf(msg.p) + return proto.Unmarshal(msg.p, resp) +} + +// filterCloseErr rewrites EOF and EPIPE errors to ErrClosed. Use when +// returning from call or handling errors from main read loop. +// +// This purposely ignores errors with a wrapped cause. +func filterCloseErr(err error) error { + switch { + case err == nil: + return nil + case err == io.EOF: + return ErrClosed + case errors.Cause(err) == io.EOF: + return ErrClosed + case strings.Contains(err.Error(), "use of closed network connection"): + return ErrClosed + default: + // if we have an epipe on a write, we cast to errclosed + if oerr, ok := err.(*net.OpError); ok && oerr.Op == "write" { + if serr, ok := oerr.Err.(*os.SyscallError); ok && serr.Err == syscall.EPIPE { + return ErrClosed + } + } + } + + return err +} diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/codec.go b/src/runtime/vendor/github.com/containerd/ttrpc/codec.go new file mode 100644 index 000000000..b4aac2fac --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/codec.go @@ -0,0 +1,42 @@ +/* + Copyright The containerd Authors. + + 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 ttrpc + +import ( + "github.com/gogo/protobuf/proto" + "github.com/pkg/errors" +) + +type codec struct{} + +func (c codec) Marshal(msg interface{}) ([]byte, error) { + switch v := msg.(type) { + case proto.Message: + return proto.Marshal(v) + default: + return nil, errors.Errorf("ttrpc: cannot marshal unknown type: %T", msg) + } +} + +func (c codec) Unmarshal(p []byte, msg interface{}) error { + switch v := msg.(type) { + case proto.Message: + return proto.Unmarshal(p, v) + default: + return errors.Errorf("ttrpc: cannot unmarshal into unknown type: %T", msg) + } +} diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/config.go b/src/runtime/vendor/github.com/containerd/ttrpc/config.go new file mode 100644 index 000000000..6a53c112b --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/config.go @@ -0,0 +1,52 @@ +/* + Copyright The containerd Authors. + + 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 ttrpc + +import "github.com/pkg/errors" + +type serverConfig struct { + handshaker Handshaker + interceptor UnaryServerInterceptor +} + +// ServerOpt for configuring a ttrpc server +type ServerOpt func(*serverConfig) error + +// WithServerHandshaker can be passed to NewServer to ensure that the +// handshaker is called before every connection attempt. +// +// Only one handshaker is allowed per server. +func WithServerHandshaker(handshaker Handshaker) ServerOpt { + return func(c *serverConfig) error { + if c.handshaker != nil { + return errors.New("only one handshaker allowed per server") + } + c.handshaker = handshaker + return nil + } +} + +// WithUnaryServerInterceptor sets the provided interceptor on the server +func WithUnaryServerInterceptor(i UnaryServerInterceptor) ServerOpt { + return func(c *serverConfig) error { + if c.interceptor != nil { + return errors.New("only one interceptor allowed per server") + } + c.interceptor = i + return nil + } +} diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/handshake.go b/src/runtime/vendor/github.com/containerd/ttrpc/handshake.go new file mode 100644 index 000000000..a424b67a4 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/handshake.go @@ -0,0 +1,50 @@ +/* + Copyright The containerd Authors. + + 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 ttrpc + +import ( + "context" + "net" +) + +// Handshaker defines the interface for connection handshakes performed on the +// server or client when first connecting. +type Handshaker interface { + // Handshake should confirm or decorate a connection that may be incoming + // to a server or outgoing from a client. + // + // If this returns without an error, the caller should use the connection + // in place of the original connection. + // + // The second return value can contain credential specific data, such as + // unix socket credentials or TLS information. + // + // While we currently only have implementations on the server-side, this + // interface should be sufficient to implement similar handshakes on the + // client-side. + Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) +} + +type handshakerFunc func(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) + +func (fn handshakerFunc) Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) { + return fn(ctx, conn) +} + +func noopHandshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) { + return conn, nil, nil +} diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/interceptor.go b/src/runtime/vendor/github.com/containerd/ttrpc/interceptor.go new file mode 100644 index 000000000..c1219dac6 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/interceptor.go @@ -0,0 +1,50 @@ +/* + Copyright The containerd Authors. + + 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 ttrpc + +import "context" + +// UnaryServerInfo provides information about the server request +type UnaryServerInfo struct { + FullMethod string +} + +// UnaryClientInfo provides information about the client request +type UnaryClientInfo struct { + FullMethod string +} + +// Unmarshaler contains the server request data and allows it to be unmarshaled +// into a concrete type +type Unmarshaler func(interface{}) error + +// Invoker invokes the client's request and response from the ttrpc server +type Invoker func(context.Context, *Request, *Response) error + +// UnaryServerInterceptor specifies the interceptor function for server request/response +type UnaryServerInterceptor func(context.Context, Unmarshaler, *UnaryServerInfo, Method) (interface{}, error) + +// UnaryClientInterceptor specifies the interceptor function for client request/response +type UnaryClientInterceptor func(context.Context, *Request, *Response, *UnaryClientInfo, Invoker) error + +func defaultServerInterceptor(ctx context.Context, unmarshal Unmarshaler, info *UnaryServerInfo, method Method) (interface{}, error) { + return method(ctx, unmarshal) +} + +func defaultClientInterceptor(ctx context.Context, req *Request, resp *Response, _ *UnaryClientInfo, invoker Invoker) error { + return invoker(ctx, req, resp) +} diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/metadata.go b/src/runtime/vendor/github.com/containerd/ttrpc/metadata.go new file mode 100644 index 000000000..ce8c0d13c --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/metadata.go @@ -0,0 +1,107 @@ +/* + Copyright The containerd Authors. + + 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 ttrpc + +import ( + "context" + "strings" +) + +// MD is the user type for ttrpc metadata +type MD map[string][]string + +// Get returns the metadata for a given key when they exist. +// If there is no metadata, a nil slice and false are returned. +func (m MD) Get(key string) ([]string, bool) { + key = strings.ToLower(key) + list, ok := m[key] + if !ok || len(list) == 0 { + return nil, false + } + + return list, true +} + +// Set sets the provided values for a given key. +// The values will overwrite any existing values. +// If no values provided, a key will be deleted. +func (m MD) Set(key string, values ...string) { + key = strings.ToLower(key) + if len(values) == 0 { + delete(m, key) + return + } + m[key] = values +} + +// Append appends additional values to the given key. +func (m MD) Append(key string, values ...string) { + key = strings.ToLower(key) + if len(values) == 0 { + return + } + current, ok := m[key] + if ok { + m.Set(key, append(current, values...)...) + } else { + m.Set(key, values...) + } +} + +func (m MD) setRequest(r *Request) { + for k, values := range m { + for _, v := range values { + r.Metadata = append(r.Metadata, &KeyValue{ + Key: k, + Value: v, + }) + } + } +} + +func (m MD) fromRequest(r *Request) { + for _, kv := range r.Metadata { + m[kv.Key] = append(m[kv.Key], kv.Value) + } +} + +type metadataKey struct{} + +// GetMetadata retrieves metadata from context.Context (previously attached with WithMetadata) +func GetMetadata(ctx context.Context) (MD, bool) { + metadata, ok := ctx.Value(metadataKey{}).(MD) + return metadata, ok +} + +// GetMetadataValue gets a specific metadata value by name from context.Context +func GetMetadataValue(ctx context.Context, name string) (string, bool) { + metadata, ok := GetMetadata(ctx) + if !ok { + return "", false + } + + if list, ok := metadata.Get(name); ok { + return list[0], true + } + + return "", false +} + +// WithMetadata attaches metadata map to a context.Context +func WithMetadata(ctx context.Context, md MD) context.Context { + return context.WithValue(ctx, metadataKey{}, md) +} diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/server.go b/src/runtime/vendor/github.com/containerd/ttrpc/server.go new file mode 100644 index 000000000..1d4f1df65 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/server.go @@ -0,0 +1,485 @@ +/* + Copyright The containerd Authors. + + 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 ttrpc + +import ( + "context" + "io" + "math/rand" + "net" + "sync" + "sync/atomic" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var ( + ErrServerClosed = errors.New("ttrpc: server closed") +) + +type Server struct { + config *serverConfig + services *serviceSet + codec codec + + mu sync.Mutex + listeners map[net.Listener]struct{} + connections map[*serverConn]struct{} // all connections to current state + done chan struct{} // marks point at which we stop serving requests +} + +func NewServer(opts ...ServerOpt) (*Server, error) { + config := &serverConfig{} + for _, opt := range opts { + if err := opt(config); err != nil { + return nil, err + } + } + if config.interceptor == nil { + config.interceptor = defaultServerInterceptor + } + + return &Server{ + config: config, + services: newServiceSet(config.interceptor), + done: make(chan struct{}), + listeners: make(map[net.Listener]struct{}), + connections: make(map[*serverConn]struct{}), + }, nil +} + +func (s *Server) Register(name string, methods map[string]Method) { + s.services.register(name, methods) +} + +func (s *Server) Serve(ctx context.Context, l net.Listener) error { + s.addListener(l) + defer s.closeListener(l) + + var ( + backoff time.Duration + handshaker = s.config.handshaker + ) + + if handshaker == nil { + handshaker = handshakerFunc(noopHandshake) + } + + for { + conn, err := l.Accept() + if err != nil { + select { + case <-s.done: + return ErrServerClosed + default: + } + + if terr, ok := err.(interface { + Temporary() bool + }); ok && terr.Temporary() { + if backoff == 0 { + backoff = time.Millisecond + } else { + backoff *= 2 + } + + if max := time.Second; backoff > max { + backoff = max + } + + sleep := time.Duration(rand.Int63n(int64(backoff))) + logrus.WithError(err).Errorf("ttrpc: failed accept; backoff %v", sleep) + time.Sleep(sleep) + continue + } + + return err + } + + backoff = 0 + + approved, handshake, err := handshaker.Handshake(ctx, conn) + if err != nil { + logrus.WithError(err).Errorf("ttrpc: refusing connection after handshake") + conn.Close() + continue + } + + sc := s.newConn(approved, handshake) + go sc.run(ctx) + } +} + +func (s *Server) Shutdown(ctx context.Context) error { + s.mu.Lock() + select { + case <-s.done: + default: + // protected by mutex + close(s.done) + } + lnerr := s.closeListeners() + s.mu.Unlock() + + ticker := time.NewTicker(200 * time.Millisecond) + defer ticker.Stop() + for { + if s.closeIdleConns() { + return lnerr + } + select { + case <-ctx.Done(): + return ctx.Err() + case <-ticker.C: + } + } +} + +// Close the server without waiting for active connections. +func (s *Server) Close() error { + s.mu.Lock() + defer s.mu.Unlock() + + select { + case <-s.done: + default: + // protected by mutex + close(s.done) + } + + err := s.closeListeners() + for c := range s.connections { + c.close() + delete(s.connections, c) + } + + return err +} + +func (s *Server) addListener(l net.Listener) { + s.mu.Lock() + defer s.mu.Unlock() + s.listeners[l] = struct{}{} +} + +func (s *Server) closeListener(l net.Listener) error { + s.mu.Lock() + defer s.mu.Unlock() + + return s.closeListenerLocked(l) +} + +func (s *Server) closeListenerLocked(l net.Listener) error { + defer delete(s.listeners, l) + return l.Close() +} + +func (s *Server) closeListeners() error { + var err error + for l := range s.listeners { + if cerr := s.closeListenerLocked(l); cerr != nil && err == nil { + err = cerr + } + } + return err +} + +func (s *Server) addConnection(c *serverConn) { + s.mu.Lock() + defer s.mu.Unlock() + + s.connections[c] = struct{}{} +} + +func (s *Server) closeIdleConns() bool { + s.mu.Lock() + defer s.mu.Unlock() + quiescent := true + for c := range s.connections { + st, ok := c.getState() + if !ok || st != connStateIdle { + quiescent = false + continue + } + c.close() + delete(s.connections, c) + } + return quiescent +} + +type connState int + +const ( + connStateActive = iota + 1 // outstanding requests + connStateIdle // no requests + connStateClosed // closed connection +) + +func (cs connState) String() string { + switch cs { + case connStateActive: + return "active" + case connStateIdle: + return "idle" + case connStateClosed: + return "closed" + default: + return "unknown" + } +} + +func (s *Server) newConn(conn net.Conn, handshake interface{}) *serverConn { + c := &serverConn{ + server: s, + conn: conn, + handshake: handshake, + shutdown: make(chan struct{}), + } + c.setState(connStateIdle) + s.addConnection(c) + return c +} + +type serverConn struct { + server *Server + conn net.Conn + handshake interface{} // data from handshake, not used for now + state atomic.Value + + shutdownOnce sync.Once + shutdown chan struct{} // forced shutdown, used by close +} + +func (c *serverConn) getState() (connState, bool) { + cs, ok := c.state.Load().(connState) + return cs, ok +} + +func (c *serverConn) setState(newstate connState) { + c.state.Store(newstate) +} + +func (c *serverConn) close() error { + c.shutdownOnce.Do(func() { + close(c.shutdown) + }) + + return nil +} + +func (c *serverConn) run(sctx context.Context) { + type ( + request struct { + id uint32 + req *Request + } + + response struct { + id uint32 + resp *Response + } + ) + + var ( + ch = newChannel(c.conn) + ctx, cancel = context.WithCancel(sctx) + active int + state connState = connStateIdle + responses = make(chan response) + requests = make(chan request) + recvErr = make(chan error, 1) + shutdown = c.shutdown + done = make(chan struct{}) + ) + + defer c.conn.Close() + defer cancel() + defer close(done) + + go func(recvErr chan error) { + defer close(recvErr) + sendImmediate := func(id uint32, st *status.Status) bool { + select { + case responses <- response{ + // even though we've had an invalid stream id, we send it + // back on the same stream id so the client knows which + // stream id was bad. + id: id, + resp: &Response{ + Status: st.Proto(), + }, + }: + return true + case <-c.shutdown: + return false + case <-done: + return false + } + } + + for { + select { + case <-c.shutdown: + return + case <-done: + return + default: // proceed + } + + mh, p, err := ch.recv() + if err != nil { + status, ok := status.FromError(err) + if !ok { + recvErr <- err + return + } + + // in this case, we send an error for that particular message + // when the status is defined. + if !sendImmediate(mh.StreamID, status) { + return + } + + continue + } + + if mh.Type != messageTypeRequest { + // we must ignore this for future compat. + continue + } + + var req Request + if err := c.server.codec.Unmarshal(p, &req); err != nil { + ch.putmbuf(p) + if !sendImmediate(mh.StreamID, status.Newf(codes.InvalidArgument, "unmarshal request error: %v", err)) { + return + } + continue + } + ch.putmbuf(p) + + if mh.StreamID%2 != 1 { + // enforce odd client initiated identifiers. + if !sendImmediate(mh.StreamID, status.Newf(codes.InvalidArgument, "StreamID must be odd for client initiated streams")) { + return + } + continue + } + + // Forward the request to the main loop. We don't wait on s.done + // because we have already accepted the client request. + select { + case requests <- request{ + id: mh.StreamID, + req: &req, + }: + case <-done: + return + } + } + }(recvErr) + + for { + newstate := state + switch { + case active > 0: + newstate = connStateActive + shutdown = nil + case active == 0: + newstate = connStateIdle + shutdown = c.shutdown // only enable this branch in idle mode + } + + if newstate != state { + c.setState(newstate) + state = newstate + } + + select { + case request := <-requests: + active++ + go func(id uint32) { + ctx, cancel := getRequestContext(ctx, request.req) + defer cancel() + + p, status := c.server.services.call(ctx, request.req.Service, request.req.Method, request.req.Payload) + resp := &Response{ + Status: status.Proto(), + Payload: p, + } + + select { + case responses <- response{ + id: id, + resp: resp, + }: + case <-done: + } + }(request.id) + case response := <-responses: + p, err := c.server.codec.Marshal(response.resp) + if err != nil { + logrus.WithError(err).Error("failed marshaling response") + return + } + + if err := ch.send(response.id, messageTypeResponse, p); err != nil { + logrus.WithError(err).Error("failed sending message on channel") + return + } + + active-- + case err := <-recvErr: + // TODO(stevvooe): Not wildly clear what we should do in this + // branch. Basically, it means that we are no longer receiving + // requests due to a terminal error. + recvErr = nil // connection is now "closing" + if err == io.EOF || err == io.ErrUnexpectedEOF { + // The client went away and we should stop processing + // requests, so that the client connection is closed + return + } + if err != nil { + logrus.WithError(err).Error("error receiving message") + } + case <-shutdown: + return + } + } +} + +var noopFunc = func() {} + +func getRequestContext(ctx context.Context, req *Request) (retCtx context.Context, cancel func()) { + if len(req.Metadata) > 0 { + md := MD{} + md.fromRequest(req) + ctx = WithMetadata(ctx, md) + } + + cancel = noopFunc + if req.TimeoutNano == 0 { + return ctx, cancel + } + + ctx, cancel = context.WithTimeout(ctx, time.Duration(req.TimeoutNano)) + return ctx, cancel +} diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/services.go b/src/runtime/vendor/github.com/containerd/ttrpc/services.go new file mode 100644 index 000000000..0eacfd79a --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/services.go @@ -0,0 +1,156 @@ +/* + Copyright The containerd Authors. + + 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 ttrpc + +import ( + "context" + "io" + "os" + "path" + + "github.com/gogo/protobuf/proto" + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type Method func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) + +type ServiceDesc struct { + Methods map[string]Method + + // TODO(stevvooe): Add stream support. +} + +type serviceSet struct { + services map[string]ServiceDesc + interceptor UnaryServerInterceptor +} + +func newServiceSet(interceptor UnaryServerInterceptor) *serviceSet { + return &serviceSet{ + services: make(map[string]ServiceDesc), + interceptor: interceptor, + } +} + +func (s *serviceSet) register(name string, methods map[string]Method) { + if _, ok := s.services[name]; ok { + panic(errors.Errorf("duplicate service %v registered", name)) + } + + s.services[name] = ServiceDesc{ + Methods: methods, + } +} + +func (s *serviceSet) call(ctx context.Context, serviceName, methodName string, p []byte) ([]byte, *status.Status) { + p, err := s.dispatch(ctx, serviceName, methodName, p) + st, ok := status.FromError(err) + if !ok { + st = status.New(convertCode(err), err.Error()) + } + + return p, st +} + +func (s *serviceSet) dispatch(ctx context.Context, serviceName, methodName string, p []byte) ([]byte, error) { + method, err := s.resolve(serviceName, methodName) + if err != nil { + return nil, err + } + + unmarshal := func(obj interface{}) error { + switch v := obj.(type) { + case proto.Message: + if err := proto.Unmarshal(p, v); err != nil { + return status.Errorf(codes.Internal, "ttrpc: error unmarshalling payload: %v", err.Error()) + } + default: + return status.Errorf(codes.Internal, "ttrpc: error unsupported request type: %T", v) + } + return nil + } + + info := &UnaryServerInfo{ + FullMethod: fullPath(serviceName, methodName), + } + + resp, err := s.interceptor(ctx, unmarshal, info, method) + if err != nil { + return nil, err + } + + switch v := resp.(type) { + case proto.Message: + r, err := proto.Marshal(v) + if err != nil { + return nil, status.Errorf(codes.Internal, "ttrpc: error marshaling payload: %v", err.Error()) + } + + return r, nil + default: + return nil, status.Errorf(codes.Internal, "ttrpc: error unsupported response type: %T", v) + } +} + +func (s *serviceSet) resolve(service, method string) (Method, error) { + srv, ok := s.services[service] + if !ok { + return nil, status.Errorf(codes.NotFound, "service %v", service) + } + + mthd, ok := srv.Methods[method] + if !ok { + return nil, status.Errorf(codes.NotFound, "method %v", method) + } + + return mthd, nil +} + +// convertCode maps stdlib go errors into grpc space. +// +// This is ripped from the grpc-go code base. +func convertCode(err error) codes.Code { + switch err { + case nil: + return codes.OK + case io.EOF: + return codes.OutOfRange + case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF: + return codes.FailedPrecondition + case os.ErrInvalid: + return codes.InvalidArgument + case context.Canceled: + return codes.Canceled + case context.DeadlineExceeded: + return codes.DeadlineExceeded + } + switch { + case os.IsExist(err): + return codes.AlreadyExists + case os.IsNotExist(err): + return codes.NotFound + case os.IsPermission(err): + return codes.PermissionDenied + } + return codes.Unknown +} + +func fullPath(service, method string) string { + return "/" + path.Join(service, method) +} diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/types.go b/src/runtime/vendor/github.com/containerd/ttrpc/types.go new file mode 100644 index 000000000..9a1c19a72 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/types.go @@ -0,0 +1,63 @@ +/* + Copyright The containerd Authors. + + 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 ttrpc + +import ( + "fmt" + + spb "google.golang.org/genproto/googleapis/rpc/status" +) + +type Request struct { + Service string `protobuf:"bytes,1,opt,name=service,proto3"` + Method string `protobuf:"bytes,2,opt,name=method,proto3"` + Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3"` + TimeoutNano int64 `protobuf:"varint,4,opt,name=timeout_nano,proto3"` + Metadata []*KeyValue `protobuf:"bytes,5,rep,name=metadata,proto3"` +} + +func (r *Request) Reset() { *r = Request{} } +func (r *Request) String() string { return fmt.Sprintf("%+#v", r) } +func (r *Request) ProtoMessage() {} + +type Response struct { + Status *spb.Status `protobuf:"bytes,1,opt,name=status,proto3"` + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3"` +} + +func (r *Response) Reset() { *r = Response{} } +func (r *Response) String() string { return fmt.Sprintf("%+#v", r) } +func (r *Response) ProtoMessage() {} + +type StringList struct { + List []string `protobuf:"bytes,1,rep,name=list,proto3"` +} + +func (r *StringList) Reset() { *r = StringList{} } +func (r *StringList) String() string { return fmt.Sprintf("%+#v", r) } +func (r *StringList) ProtoMessage() {} + +func makeStringList(item ...string) StringList { return StringList{List: item} } + +type KeyValue struct { + Key string `protobuf:"bytes,1,opt,name=key,proto3"` + Value string `protobuf:"bytes,2,opt,name=value,proto3"` +} + +func (m *KeyValue) Reset() { *m = KeyValue{} } +func (*KeyValue) ProtoMessage() {} +func (m *KeyValue) String() string { return fmt.Sprintf("%+#v", m) } diff --git a/src/runtime/vendor/github.com/containerd/ttrpc/unixcreds_linux.go b/src/runtime/vendor/github.com/containerd/ttrpc/unixcreds_linux.go new file mode 100644 index 000000000..a471bd365 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/ttrpc/unixcreds_linux.go @@ -0,0 +1,108 @@ +/* + Copyright The containerd Authors. + + 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 ttrpc + +import ( + "context" + "net" + "os" + "syscall" + + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +type UnixCredentialsFunc func(*unix.Ucred) error + +func (fn UnixCredentialsFunc) Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) { + uc, err := requireUnixSocket(conn) + if err != nil { + return nil, nil, errors.Wrap(err, "ttrpc.UnixCredentialsFunc: require unix socket") + } + + rs, err := uc.SyscallConn() + if err != nil { + return nil, nil, errors.Wrap(err, "ttrpc.UnixCredentialsFunc: (net.UnixConn).SyscallConn failed") + } + var ( + ucred *unix.Ucred + ucredErr error + ) + if err := rs.Control(func(fd uintptr) { + ucred, ucredErr = unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED) + }); err != nil { + return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: (*syscall.RawConn).Control failed") + } + + if ucredErr != nil { + return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: failed to retrieve socket peer credentials") + } + + if err := fn(ucred); err != nil { + return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: credential check failed") + } + + return uc, ucred, nil +} + +// UnixSocketRequireUidGid requires specific *effective* UID/GID, rather than the real UID/GID. +// +// For example, if a daemon binary is owned by the root (UID 0) with SUID bit but running as an +// unprivileged user (UID 1001), the effective UID becomes 0, and the real UID becomes 1001. +// So calling this function with uid=0 allows a connection from effective UID 0 but rejects +// a connection from effective UID 1001. +// +// See socket(7), SO_PEERCRED: "The returned credentials are those that were in effect at the time of the call to connect(2) or socketpair(2)." +func UnixSocketRequireUidGid(uid, gid int) UnixCredentialsFunc { + return func(ucred *unix.Ucred) error { + return requireUidGid(ucred, uid, gid) + } +} + +func UnixSocketRequireRoot() UnixCredentialsFunc { + return UnixSocketRequireUidGid(0, 0) +} + +// UnixSocketRequireSameUser resolves the current effective unix user and returns a +// UnixCredentialsFunc that will validate incoming unix connections against the +// current credentials. +// +// This is useful when using abstract sockets that are accessible by all users. +func UnixSocketRequireSameUser() UnixCredentialsFunc { + euid, egid := os.Geteuid(), os.Getegid() + return UnixSocketRequireUidGid(euid, egid) +} + +func requireRoot(ucred *unix.Ucred) error { + return requireUidGid(ucred, 0, 0) +} + +func requireUidGid(ucred *unix.Ucred, uid, gid int) error { + if (uid != -1 && uint32(uid) != ucred.Uid) || (gid != -1 && uint32(gid) != ucred.Gid) { + return errors.Wrap(syscall.EPERM, "ttrpc: invalid credentials") + } + return nil +} + +func requireUnixSocket(conn net.Conn) (*net.UnixConn, error) { + uc, ok := conn.(*net.UnixConn) + if !ok { + return nil, errors.New("a unix socket connection is required") + } + + return uc, nil +} diff --git a/src/runtime/vendor/github.com/containerd/typeurl/.travis.yml b/src/runtime/vendor/github.com/containerd/typeurl/.travis.yml new file mode 100644 index 000000000..a9791e1d6 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/typeurl/.travis.yml @@ -0,0 +1,21 @@ +language: go +go: + - 1.9.x + - 1.10.x + - tip + +install: + - go get -t -v ./... + - go get -u github.com/vbatts/git-validation + - go get -u github.com/kunalkushwaha/ltag + +before_script: + - pushd ..; git clone https://github.com/containerd/project; popd + +script: + - DCO_VERBOSITY=-q ../project/script/validate/dco + - ../project/script/validate/fileheader ../project/ + - go test -race -coverprofile=coverage.txt -covermode=atomic + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/src/runtime/vendor/github.com/containerd/typeurl/LICENSE b/src/runtime/vendor/github.com/containerd/typeurl/LICENSE new file mode 100644 index 000000000..584149b6e --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/typeurl/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright The containerd Authors + + 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 + + https://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. diff --git a/src/runtime/vendor/github.com/containerd/typeurl/README.md b/src/runtime/vendor/github.com/containerd/typeurl/README.md new file mode 100644 index 000000000..67f1b8447 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/typeurl/README.md @@ -0,0 +1,19 @@ +# typeurl + +[![Build Status](https://travis-ci.org/containerd/typeurl.svg?branch=master)](https://travis-ci.org/containerd/typeurl) + +[![codecov](https://codecov.io/gh/containerd/typeurl/branch/master/graph/badge.svg)](https://codecov.io/gh/containerd/typeurl) + +A Go package for managing the registration, marshaling, and unmarshaling of encoded types. + +This package helps when types are sent over a GRPC API and marshaled as a [protobuf.Any](). + +## Project details + +**typeurl** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/src/runtime/vendor/github.com/containerd/typeurl/doc.go b/src/runtime/vendor/github.com/containerd/typeurl/doc.go new file mode 100644 index 000000000..c0d0fd205 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/typeurl/doc.go @@ -0,0 +1,83 @@ +/* + Copyright The containerd Authors. + + 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 typeurl + +// Package typeurl assists with managing the registration, marshaling, and +// unmarshaling of types encoded as protobuf.Any. +// +// A protobuf.Any is a proto message that can contain any arbitrary data. It +// consists of two components, a TypeUrl and a Value, and its proto definition +// looks like this: +// +// message Any { +// string type_url = 1; +// bytes value = 2; +// } +// +// The TypeUrl is used to distinguish the contents from other proto.Any +// messages. This typeurl library manages these URLs to enable automagic +// marshaling and unmarshaling of the contents. +// +// For example, consider this go struct: +// +// type Foo struct { +// Field1 string +// Field2 string +// } +// +// To use typeurl, types must first be registered. This is typically done in +// the init function +// +// func init() { +// typeurl.Register(&Foo{}, "Foo") +// } +// +// This will register the type Foo with the url path "Foo". The arguments to +// Register are variadic, and are used to construct a url path. Consider this +// example, from the github.com/containerd/containerd/client package: +// +// func init() { +// const prefix = "types.containerd.io" +// // register TypeUrls for commonly marshaled external types +// major := strconv.Itoa(specs.VersionMajor) +// typeurl.Register(&specs.Spec{}, prefix, "opencontainers/runtime-spec", major, "Spec") +// // this function has more Register calls, which are elided. +// } +// +// This registers several types under a more complex url, which ends up mapping +// to `types.containerd.io/opencontainers/runtime-spec/1/Spec` (or some other +// value for major). +// +// Once a type is registered, it can be marshaled to a proto.Any message simply +// by calling `MarshalAny`, like this: +// +// foo := &Foo{Field1: "value1", Field2: "value2"} +// anyFoo, err := typeurl.MarshalAny(foo) +// +// MarshalAny will resolve the correct URL for the type. If the type in +// question implements the proto.Message interface, then it will be marshaled +// as a proto message. Otherwise, it will be marshaled as json. This means that +// typeurl will work on any arbitrary data, whether or not it has a proto +// definition, as long as it can be serialized to json. +// +// To unmarshal, the process is simply inverse: +// +// iface, err := typeurl.UnmarshalAny(anyFoo) +// foo := iface.(*Foo) +// +// The correct type is automatically chosen from the type registry, and the +// returned interface can be cast straight to that type. diff --git a/src/runtime/vendor/github.com/containerd/typeurl/types.go b/src/runtime/vendor/github.com/containerd/typeurl/types.go new file mode 100644 index 000000000..4f9c069f4 --- /dev/null +++ b/src/runtime/vendor/github.com/containerd/typeurl/types.go @@ -0,0 +1,161 @@ +/* + Copyright The containerd Authors. + + 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 typeurl + +import ( + "encoding/json" + "path" + "reflect" + "sync" + + "github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/types" + "github.com/pkg/errors" +) + +var ( + mu sync.Mutex + registry = make(map[reflect.Type]string) +) + +var ErrNotFound = errors.New("not found") + +// Register a type with the base url of the type +func Register(v interface{}, args ...string) { + var ( + t = tryDereference(v) + p = path.Join(args...) + ) + mu.Lock() + defer mu.Unlock() + if et, ok := registry[t]; ok { + if et != p { + panic(errors.Errorf("type registred with alternate path %q != %q", et, p)) + } + return + } + registry[t] = p +} + +// TypeURL returns the type url for a registred type +func TypeURL(v interface{}) (string, error) { + mu.Lock() + u, ok := registry[tryDereference(v)] + mu.Unlock() + if !ok { + // fallback to the proto registry if it is a proto message + pb, ok := v.(proto.Message) + if !ok { + return "", errors.Wrapf(ErrNotFound, "type %s", reflect.TypeOf(v)) + } + return proto.MessageName(pb), nil + } + return u, nil +} + +// Is returns true if the type of the Any is the same as v +func Is(any *types.Any, v interface{}) bool { + // call to check that v is a pointer + tryDereference(v) + url, err := TypeURL(v) + if err != nil { + return false + } + return any.TypeUrl == url +} + +// MarshalAny marshals the value v into an any with the correct TypeUrl. +// If the provided object is already a proto.Any message, then it will be +// returned verbatim. If it is of type proto.Message, it will be marshaled as a +// protocol buffer. Otherwise, the object will be marshaled to json. +func MarshalAny(v interface{}) (*types.Any, error) { + var marshal func(v interface{}) ([]byte, error) + switch t := v.(type) { + case *types.Any: + // avoid reserializing the type if we have an any. + return t, nil + case proto.Message: + marshal = func(v interface{}) ([]byte, error) { + return proto.Marshal(t) + } + default: + marshal = json.Marshal + } + + url, err := TypeURL(v) + if err != nil { + return nil, err + } + + data, err := marshal(v) + if err != nil { + return nil, err + } + return &types.Any{ + TypeUrl: url, + Value: data, + }, nil +} + +// UnmarshalAny unmarshals the any type into a concrete type +func UnmarshalAny(any *types.Any) (interface{}, error) { + t, err := getTypeByUrl(any.TypeUrl) + if err != nil { + return nil, err + } + v := reflect.New(t.t).Interface() + if t.isProto { + err = proto.Unmarshal(any.Value, v.(proto.Message)) + } else { + err = json.Unmarshal(any.Value, v) + } + return v, err +} + +type urlType struct { + t reflect.Type + isProto bool +} + +func getTypeByUrl(url string) (urlType, error) { + for t, u := range registry { + if u == url { + return urlType{ + t: t, + }, nil + } + } + // fallback to proto registry + t := proto.MessageType(url) + if t != nil { + return urlType{ + // get the underlying Elem because proto returns a pointer to the type + t: t.Elem(), + isProto: true, + }, nil + } + return urlType{}, errors.Wrapf(ErrNotFound, "type with url %s", url) +} + +func tryDereference(v interface{}) reflect.Type { + t := reflect.TypeOf(v) + if t.Kind() == reflect.Ptr { + // require check of pointer but dereference to register + return t.Elem() + } + panic("v is not a pointer to a type") +} diff --git a/src/runtime/vendor/github.com/containernetworking/cni/LICENSE b/src/runtime/vendor/github.com/containernetworking/cni/LICENSE new file mode 100644 index 000000000..8f71f43fe --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/cni/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + diff --git a/src/runtime/vendor/github.com/containernetworking/cni/pkg/skel/skel.go b/src/runtime/vendor/github.com/containernetworking/cni/pkg/skel/skel.go new file mode 100644 index 000000000..af56b8a1c --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/cni/pkg/skel/skel.go @@ -0,0 +1,307 @@ +// Copyright 2014-2016 CNI authors +// +// 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 skel provides skeleton code for a CNI plugin. +// In particular, it implements argument parsing and validation. +package skel + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "strings" + + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/version" +) + +// CmdArgs captures all the arguments passed in to the plugin +// via both env vars and stdin +type CmdArgs struct { + ContainerID string + Netns string + IfName string + Args string + Path string + StdinData []byte +} + +type dispatcher struct { + Getenv func(string) string + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer + + ConfVersionDecoder version.ConfigDecoder + VersionReconciler version.Reconciler +} + +type reqForCmdEntry map[string]bool + +// internal only error to indicate lack of required environment variables +type missingEnvError struct { + msg string +} + +func (e missingEnvError) Error() string { + return e.msg +} + +func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) { + var cmd, contID, netns, ifName, args, path string + + vars := []struct { + name string + val *string + reqForCmd reqForCmdEntry + }{ + { + "CNI_COMMAND", + &cmd, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + { + "CNI_CONTAINERID", + &contID, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + { + "CNI_NETNS", + &netns, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": false, + }, + }, + { + "CNI_IFNAME", + &ifName, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + { + "CNI_ARGS", + &args, + reqForCmdEntry{ + "ADD": false, + "CHECK": false, + "DEL": false, + }, + }, + { + "CNI_PATH", + &path, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + } + + argsMissing := make([]string, 0) + for _, v := range vars { + *v.val = t.Getenv(v.name) + if *v.val == "" { + if v.reqForCmd[cmd] || v.name == "CNI_COMMAND" { + argsMissing = append(argsMissing, v.name) + } + } + } + + if len(argsMissing) > 0 { + joined := strings.Join(argsMissing, ",") + return "", nil, missingEnvError{fmt.Sprintf("required env variables [%s] missing", joined)} + } + + if cmd == "VERSION" { + t.Stdin = bytes.NewReader(nil) + } + + stdinData, err := ioutil.ReadAll(t.Stdin) + if err != nil { + return "", nil, fmt.Errorf("error reading from stdin: %v", err) + } + + cmdArgs := &CmdArgs{ + ContainerID: contID, + Netns: netns, + IfName: ifName, + Args: args, + Path: path, + StdinData: stdinData, + } + return cmd, cmdArgs, nil +} + +func createTypedError(f string, args ...interface{}) *types.Error { + return &types.Error{ + Code: 100, + Msg: fmt.Sprintf(f, args...), + } +} + +func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) error { + configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) + if err != nil { + return err + } + verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo) + if verErr != nil { + return &types.Error{ + Code: types.ErrIncompatibleCNIVersion, + Msg: "incompatible CNI versions", + Details: verErr.Details(), + } + } + + return toCall(cmdArgs) +} + +func validateConfig(jsonBytes []byte) error { + var conf struct { + Name string `json:"name"` + } + if err := json.Unmarshal(jsonBytes, &conf); err != nil { + return fmt.Errorf("error reading network config: %s", err) + } + if conf.Name == "" { + return fmt.Errorf("missing network name") + } + return nil +} + +func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { + cmd, cmdArgs, err := t.getCmdArgsFromEnv() + if err != nil { + // Print the about string to stderr when no command is set + if _, ok := err.(missingEnvError); ok && t.Getenv("CNI_COMMAND") == "" && about != "" { + fmt.Fprintln(t.Stderr, about) + return nil + } + return createTypedError(err.Error()) + } + + if cmd != "VERSION" { + err = validateConfig(cmdArgs.StdinData) + if err != nil { + return createTypedError(err.Error()) + } + } + + switch cmd { + case "ADD": + err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdAdd) + case "CHECK": + configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) + if err != nil { + return createTypedError(err.Error()) + } + if gtet, err := version.GreaterThanOrEqualTo(configVersion, "0.4.0"); err != nil { + return createTypedError(err.Error()) + } else if !gtet { + return &types.Error{ + Code: types.ErrIncompatibleCNIVersion, + Msg: "config version does not allow CHECK", + } + } + for _, pluginVersion := range versionInfo.SupportedVersions() { + gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion) + if err != nil { + return createTypedError(err.Error()) + } else if gtet { + if err := t.checkVersionAndCall(cmdArgs, versionInfo, cmdCheck); err != nil { + return createTypedError(err.Error()) + } + return nil + } + } + return &types.Error{ + Code: types.ErrIncompatibleCNIVersion, + Msg: "plugin version does not allow CHECK", + } + case "DEL": + err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel) + case "VERSION": + err = versionInfo.Encode(t.Stdout) + default: + return createTypedError("unknown CNI_COMMAND: %v", cmd) + } + + if err != nil { + if e, ok := err.(*types.Error); ok { + // don't wrap Error in Error + return e + } + return createTypedError(err.Error()) + } + return nil +} + +// PluginMainWithError is the core "main" for a plugin. It accepts +// callback functions for add, check, and del CNI commands and returns an error. +// +// The caller must also specify what CNI spec versions the plugin supports. +// +// It is the responsibility of the caller to check for non-nil error return. +// +// For a plugin to comply with the CNI spec, it must print any error to stdout +// as JSON and then exit with nonzero status code. +// +// To let this package automatically handle errors and call os.Exit(1) for you, +// use PluginMain() instead. +func PluginMainWithError(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { + return (&dispatcher{ + Getenv: os.Getenv, + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + }).pluginMain(cmdAdd, cmdCheck, cmdDel, versionInfo, about) +} + +// PluginMain is the core "main" for a plugin which includes automatic error handling. +// +// The caller must also specify what CNI spec versions the plugin supports. +// +// The caller can specify an "about" string, which is printed on stderr +// when no CNI_COMMAND is specified. The recommended output is "CNI plugin v" +// +// When an error occurs in either cmdAdd, cmdCheck, or cmdDel, PluginMain will print the error +// as JSON to stdout and call os.Exit(1). +// +// To have more control over error handling, use PluginMainWithError() instead. +func PluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) { + if e := PluginMainWithError(cmdAdd, cmdCheck, cmdDel, versionInfo, about); e != nil { + if err := e.Print(); err != nil { + log.Print("Error writing error JSON to stdout: ", err) + } + os.Exit(1) + } +} diff --git a/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/020/types.go b/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/020/types.go new file mode 100644 index 000000000..53256167f --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/020/types.go @@ -0,0 +1,140 @@ +// Copyright 2016 CNI authors +// +// 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 types020 + +import ( + "encoding/json" + "fmt" + "io" + "net" + "os" + + "github.com/containernetworking/cni/pkg/types" +) + +const ImplementedSpecVersion string = "0.2.0" + +var SupportedVersions = []string{"", "0.1.0", ImplementedSpecVersion} + +// Compatibility types for CNI version 0.1.0 and 0.2.0 + +func NewResult(data []byte) (types.Result, error) { + result := &Result{} + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + return result, nil +} + +func GetResult(r types.Result) (*Result, error) { + // We expect version 0.1.0/0.2.0 results + result020, err := r.GetAsVersion(ImplementedSpecVersion) + if err != nil { + return nil, err + } + result, ok := result020.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + return result, nil +} + +// Result is what gets returned from the plugin (via stdout) to the caller +type Result struct { + CNIVersion string `json:"cniVersion,omitempty"` + IP4 *IPConfig `json:"ip4,omitempty"` + IP6 *IPConfig `json:"ip6,omitempty"` + DNS types.DNS `json:"dns,omitempty"` +} + +func (r *Result) Version() string { + return ImplementedSpecVersion +} + +func (r *Result) GetAsVersion(version string) (types.Result, error) { + for _, supportedVersion := range SupportedVersions { + if version == supportedVersion { + r.CNIVersion = version + return r, nil + } + } + return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version) +} + +func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { + data, err := json.MarshalIndent(r, "", " ") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where +// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the +// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string. +func (r *Result) String() string { + var str string + if r.IP4 != nil { + str = fmt.Sprintf("IP4:%+v, ", *r.IP4) + } + if r.IP6 != nil { + str += fmt.Sprintf("IP6:%+v, ", *r.IP6) + } + return fmt.Sprintf("%sDNS:%+v", str, r.DNS) +} + +// IPConfig contains values necessary to configure an interface +type IPConfig struct { + IP net.IPNet + Gateway net.IP + Routes []types.Route +} + +// net.IPNet is not JSON (un)marshallable so this duality is needed +// for our custom IPNet type + +// JSON (un)marshallable types +type ipConfig struct { + IP types.IPNet `json:"ip"` + Gateway net.IP `json:"gateway,omitempty"` + Routes []types.Route `json:"routes,omitempty"` +} + +func (c *IPConfig) MarshalJSON() ([]byte, error) { + ipc := ipConfig{ + IP: types.IPNet(c.IP), + Gateway: c.Gateway, + Routes: c.Routes, + } + + return json.Marshal(ipc) +} + +func (c *IPConfig) UnmarshalJSON(data []byte) error { + ipc := ipConfig{} + if err := json.Unmarshal(data, &ipc); err != nil { + return err + } + + c.IP = net.IPNet(ipc.IP) + c.Gateway = ipc.Gateway + c.Routes = ipc.Routes + return nil +} diff --git a/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/args.go b/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/args.go new file mode 100644 index 000000000..bd8640fc9 --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/args.go @@ -0,0 +1,112 @@ +// Copyright 2015 CNI authors +// +// 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 types + +import ( + "encoding" + "fmt" + "reflect" + "strings" +) + +// UnmarshallableBool typedef for builtin bool +// because builtin type's methods can't be declared +type UnmarshallableBool bool + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Returns boolean true if the string is "1" or "[Tt]rue" +// Returns boolean false if the string is "0" or "[Ff]alse" +func (b *UnmarshallableBool) UnmarshalText(data []byte) error { + s := strings.ToLower(string(data)) + switch s { + case "1", "true": + *b = true + case "0", "false": + *b = false + default: + return fmt.Errorf("Boolean unmarshal error: invalid input %s", s) + } + return nil +} + +// UnmarshallableString typedef for builtin string +type UnmarshallableString string + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Returns the string +func (s *UnmarshallableString) UnmarshalText(data []byte) error { + *s = UnmarshallableString(data) + return nil +} + +// CommonArgs contains the IgnoreUnknown argument +// and must be embedded by all Arg structs +type CommonArgs struct { + IgnoreUnknown UnmarshallableBool `json:"ignoreunknown,omitempty"` +} + +// GetKeyField is a helper function to receive Values +// Values that represent a pointer to a struct +func GetKeyField(keyString string, v reflect.Value) reflect.Value { + return v.Elem().FieldByName(keyString) +} + +// UnmarshalableArgsError is used to indicate error unmarshalling args +// from the args-string in the form "K=V;K2=V2;..." +type UnmarshalableArgsError struct { + error +} + +// LoadArgs parses args from a string in the form "K=V;K2=V2;..." +func LoadArgs(args string, container interface{}) error { + if args == "" { + return nil + } + + containerValue := reflect.ValueOf(container) + + pairs := strings.Split(args, ";") + unknownArgs := []string{} + for _, pair := range pairs { + kv := strings.Split(pair, "=") + if len(kv) != 2 { + return fmt.Errorf("ARGS: invalid pair %q", pair) + } + keyString := kv[0] + valueString := kv[1] + keyField := GetKeyField(keyString, containerValue) + if !keyField.IsValid() { + unknownArgs = append(unknownArgs, pair) + continue + } + keyFieldIface := keyField.Addr().Interface() + u, ok := keyFieldIface.(encoding.TextUnmarshaler) + if !ok { + return UnmarshalableArgsError{fmt.Errorf( + "ARGS: cannot unmarshal into field '%s' - type '%s' does not implement encoding.TextUnmarshaler", + keyString, reflect.TypeOf(keyFieldIface))} + } + err := u.UnmarshalText([]byte(valueString)) + if err != nil { + return fmt.Errorf("ARGS: error parsing value of pair %q: %v)", pair, err) + } + } + + isIgnoreUnknown := GetKeyField("IgnoreUnknown", containerValue).Bool() + if len(unknownArgs) > 0 && !isIgnoreUnknown { + return fmt.Errorf("ARGS: unknown args %q", unknownArgs) + } + return nil +} diff --git a/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/current/types.go b/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/current/types.go new file mode 100644 index 000000000..7267a2e6d --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/current/types.go @@ -0,0 +1,293 @@ +// Copyright 2016 CNI authors +// +// 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 current + +import ( + "encoding/json" + "fmt" + "io" + "net" + "os" + + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/types/020" +) + +const ImplementedSpecVersion string = "0.4.0" + +var SupportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion} + +func NewResult(data []byte) (types.Result, error) { + result := &Result{} + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + return result, nil +} + +func GetResult(r types.Result) (*Result, error) { + resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) + if err != nil { + return nil, err + } + result, ok := resultCurrent.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + return result, nil +} + +var resultConverters = []struct { + versions []string + convert func(types.Result) (*Result, error) +}{ + {types020.SupportedVersions, convertFrom020}, + {SupportedVersions, convertFrom030}, +} + +func convertFrom020(result types.Result) (*Result, error) { + oldResult, err := types020.GetResult(result) + if err != nil { + return nil, err + } + + newResult := &Result{ + CNIVersion: ImplementedSpecVersion, + DNS: oldResult.DNS, + Routes: []*types.Route{}, + } + + if oldResult.IP4 != nil { + newResult.IPs = append(newResult.IPs, &IPConfig{ + Version: "4", + Address: oldResult.IP4.IP, + Gateway: oldResult.IP4.Gateway, + }) + for _, route := range oldResult.IP4.Routes { + newResult.Routes = append(newResult.Routes, &types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } + } + + if oldResult.IP6 != nil { + newResult.IPs = append(newResult.IPs, &IPConfig{ + Version: "6", + Address: oldResult.IP6.IP, + Gateway: oldResult.IP6.Gateway, + }) + for _, route := range oldResult.IP6.Routes { + newResult.Routes = append(newResult.Routes, &types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } + } + + return newResult, nil +} + +func convertFrom030(result types.Result) (*Result, error) { + newResult, ok := result.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + newResult.CNIVersion = ImplementedSpecVersion + return newResult, nil +} + +func NewResultFromResult(result types.Result) (*Result, error) { + version := result.Version() + for _, converter := range resultConverters { + for _, supportedVersion := range converter.versions { + if version == supportedVersion { + return converter.convert(result) + } + } + } + return nil, fmt.Errorf("unsupported CNI result22 version %q", version) +} + +// Result is what gets returned from the plugin (via stdout) to the caller +type Result struct { + CNIVersion string `json:"cniVersion,omitempty"` + Interfaces []*Interface `json:"interfaces,omitempty"` + IPs []*IPConfig `json:"ips,omitempty"` + Routes []*types.Route `json:"routes,omitempty"` + DNS types.DNS `json:"dns,omitempty"` +} + +// Convert to the older 0.2.0 CNI spec Result type +func (r *Result) convertTo020() (*types020.Result, error) { + oldResult := &types020.Result{ + CNIVersion: types020.ImplementedSpecVersion, + DNS: r.DNS, + } + + for _, ip := range r.IPs { + // Only convert the first IP address of each version as 0.2.0 + // and earlier cannot handle multiple IP addresses + if ip.Version == "4" && oldResult.IP4 == nil { + oldResult.IP4 = &types020.IPConfig{ + IP: ip.Address, + Gateway: ip.Gateway, + } + } else if ip.Version == "6" && oldResult.IP6 == nil { + oldResult.IP6 = &types020.IPConfig{ + IP: ip.Address, + Gateway: ip.Gateway, + } + } + + if oldResult.IP4 != nil && oldResult.IP6 != nil { + break + } + } + + for _, route := range r.Routes { + is4 := route.Dst.IP.To4() != nil + if is4 && oldResult.IP4 != nil { + oldResult.IP4.Routes = append(oldResult.IP4.Routes, types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } else if !is4 && oldResult.IP6 != nil { + oldResult.IP6.Routes = append(oldResult.IP6.Routes, types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } + } + + if oldResult.IP4 == nil && oldResult.IP6 == nil { + return nil, fmt.Errorf("cannot convert: no valid IP addresses") + } + + return oldResult, nil +} + +func (r *Result) Version() string { + return ImplementedSpecVersion +} + +func (r *Result) GetAsVersion(version string) (types.Result, error) { + switch version { + case "0.3.0", "0.3.1", ImplementedSpecVersion: + r.CNIVersion = version + return r, nil + case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]: + return r.convertTo020() + } + return nil, fmt.Errorf("cannot convert version 0.3.x to %q", version) +} + +func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { + data, err := json.MarshalIndent(r, "", " ") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// String returns a formatted string in the form of "[Interfaces: $1,][ IP: $2,] DNS: $3" where +// $1 represents the receiver's Interfaces, $2 represents the receiver's IP addresses and $3 the +// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string. +func (r *Result) String() string { + var str string + if len(r.Interfaces) > 0 { + str += fmt.Sprintf("Interfaces:%+v, ", r.Interfaces) + } + if len(r.IPs) > 0 { + str += fmt.Sprintf("IP:%+v, ", r.IPs) + } + if len(r.Routes) > 0 { + str += fmt.Sprintf("Routes:%+v, ", r.Routes) + } + return fmt.Sprintf("%sDNS:%+v", str, r.DNS) +} + +// Convert this old version result to the current CNI version result +func (r *Result) Convert() (*Result, error) { + return r, nil +} + +// Interface contains values about the created interfaces +type Interface struct { + Name string `json:"name"` + Mac string `json:"mac,omitempty"` + Sandbox string `json:"sandbox,omitempty"` +} + +func (i *Interface) String() string { + return fmt.Sprintf("%+v", *i) +} + +// Int returns a pointer to the int value passed in. Used to +// set the IPConfig.Interface field. +func Int(v int) *int { + return &v +} + +// IPConfig contains values necessary to configure an IP address on an interface +type IPConfig struct { + // IP version, either "4" or "6" + Version string + // Index into Result structs Interfaces list + Interface *int + Address net.IPNet + Gateway net.IP +} + +func (i *IPConfig) String() string { + return fmt.Sprintf("%+v", *i) +} + +// JSON (un)marshallable types +type ipConfig struct { + Version string `json:"version"` + Interface *int `json:"interface,omitempty"` + Address types.IPNet `json:"address"` + Gateway net.IP `json:"gateway,omitempty"` +} + +func (c *IPConfig) MarshalJSON() ([]byte, error) { + ipc := ipConfig{ + Version: c.Version, + Interface: c.Interface, + Address: types.IPNet(c.Address), + Gateway: c.Gateway, + } + + return json.Marshal(ipc) +} + +func (c *IPConfig) UnmarshalJSON(data []byte) error { + ipc := ipConfig{} + if err := json.Unmarshal(data, &ipc); err != nil { + return err + } + + c.Version = ipc.Version + c.Interface = ipc.Interface + c.Address = net.IPNet(ipc.Address) + c.Gateway = ipc.Gateway + return nil +} diff --git a/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/types.go b/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/types.go new file mode 100644 index 000000000..d0d11006a --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/cni/pkg/types/types.go @@ -0,0 +1,199 @@ +// Copyright 2015 CNI authors +// +// 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 types + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net" + "os" +) + +// like net.IPNet but adds JSON marshalling and unmarshalling +type IPNet net.IPNet + +// ParseCIDR takes a string like "10.2.3.1/24" and +// return IPNet with "10.2.3.1" and /24 mask +func ParseCIDR(s string) (*net.IPNet, error) { + ip, ipn, err := net.ParseCIDR(s) + if err != nil { + return nil, err + } + + ipn.IP = ip + return ipn, nil +} + +func (n IPNet) MarshalJSON() ([]byte, error) { + return json.Marshal((*net.IPNet)(&n).String()) +} + +func (n *IPNet) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + + tmp, err := ParseCIDR(s) + if err != nil { + return err + } + + *n = IPNet(*tmp) + return nil +} + +// NetConf describes a network. +type NetConf struct { + CNIVersion string `json:"cniVersion,omitempty"` + + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Capabilities map[string]bool `json:"capabilities,omitempty"` + IPAM IPAM `json:"ipam,omitempty"` + DNS DNS `json:"dns"` + + RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` + PrevResult Result `json:"-"` +} + +type IPAM struct { + Type string `json:"type,omitempty"` +} + +// NetConfList describes an ordered list of networks. +type NetConfList struct { + CNIVersion string `json:"cniVersion,omitempty"` + + Name string `json:"name,omitempty"` + DisableCheck bool `json:"disableCheck,omitempty"` + Plugins []*NetConf `json:"plugins,omitempty"` +} + +type ResultFactoryFunc func([]byte) (Result, error) + +// Result is an interface that provides the result of plugin execution +type Result interface { + // The highest CNI specification result version the result supports + // without having to convert + Version() string + + // Returns the result converted into the requested CNI specification + // result version, or an error if conversion failed + GetAsVersion(version string) (Result, error) + + // Prints the result in JSON format to stdout + Print() error + + // Prints the result in JSON format to provided writer + PrintTo(writer io.Writer) error + + // Returns a JSON string representation of the result + String() string +} + +func PrintResult(result Result, version string) error { + newResult, err := result.GetAsVersion(version) + if err != nil { + return err + } + return newResult.Print() +} + +// DNS contains values interesting for DNS resolvers +type DNS struct { + Nameservers []string `json:"nameservers,omitempty"` + Domain string `json:"domain,omitempty"` + Search []string `json:"search,omitempty"` + Options []string `json:"options,omitempty"` +} + +type Route struct { + Dst net.IPNet + GW net.IP +} + +func (r *Route) String() string { + return fmt.Sprintf("%+v", *r) +} + +// Well known error codes +// see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes +const ( + ErrUnknown uint = iota // 0 + ErrIncompatibleCNIVersion // 1 + ErrUnsupportedField // 2 +) + +type Error struct { + Code uint `json:"code"` + Msg string `json:"msg"` + Details string `json:"details,omitempty"` +} + +func (e *Error) Error() string { + details := "" + if e.Details != "" { + details = fmt.Sprintf("; %v", e.Details) + } + return fmt.Sprintf("%v%v", e.Msg, details) +} + +func (e *Error) Print() error { + return prettyPrint(e) +} + +// net.IPNet is not JSON (un)marshallable so this duality is needed +// for our custom IPNet type + +// JSON (un)marshallable types +type route struct { + Dst IPNet `json:"dst"` + GW net.IP `json:"gw,omitempty"` +} + +func (r *Route) UnmarshalJSON(data []byte) error { + rt := route{} + if err := json.Unmarshal(data, &rt); err != nil { + return err + } + + r.Dst = net.IPNet(rt.Dst) + r.GW = rt.GW + return nil +} + +func (r Route) MarshalJSON() ([]byte, error) { + rt := route{ + Dst: IPNet(r.Dst), + GW: r.GW, + } + + return json.Marshal(rt) +} + +func prettyPrint(obj interface{}) error { + data, err := json.MarshalIndent(obj, "", " ") + if err != nil { + return err + } + _, err = os.Stdout.Write(data) + return err +} + +// NotImplementedError is used to indicate that a method is not implemented for the given platform +var NotImplementedError = errors.New("Not Implemented") diff --git a/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/conf.go b/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/conf.go new file mode 100644 index 000000000..3cca58bbe --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/conf.go @@ -0,0 +1,37 @@ +// Copyright 2016 CNI authors +// +// 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 version + +import ( + "encoding/json" + "fmt" +) + +// ConfigDecoder can decode the CNI version available in network config data +type ConfigDecoder struct{} + +func (*ConfigDecoder) Decode(jsonBytes []byte) (string, error) { + var conf struct { + CNIVersion string `json:"cniVersion"` + } + err := json.Unmarshal(jsonBytes, &conf) + if err != nil { + return "", fmt.Errorf("decoding version from network config: %s", err) + } + if conf.CNIVersion == "" { + return "0.1.0", nil + } + return conf.CNIVersion, nil +} diff --git a/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/plugin.go b/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/plugin.go new file mode 100644 index 000000000..1df427243 --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/plugin.go @@ -0,0 +1,144 @@ +// Copyright 2016 CNI authors +// +// 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 version + +import ( + "encoding/json" + "fmt" + "io" + "strconv" + "strings" +) + +// PluginInfo reports information about CNI versioning +type PluginInfo interface { + // SupportedVersions returns one or more CNI spec versions that the plugin + // supports. If input is provided in one of these versions, then the plugin + // promises to use the same CNI version in its response + SupportedVersions() []string + + // Encode writes this CNI version information as JSON to the given Writer + Encode(io.Writer) error +} + +type pluginInfo struct { + CNIVersion_ string `json:"cniVersion"` + SupportedVersions_ []string `json:"supportedVersions,omitempty"` +} + +// pluginInfo implements the PluginInfo interface +var _ PluginInfo = &pluginInfo{} + +func (p *pluginInfo) Encode(w io.Writer) error { + return json.NewEncoder(w).Encode(p) +} + +func (p *pluginInfo) SupportedVersions() []string { + return p.SupportedVersions_ +} + +// PluginSupports returns a new PluginInfo that will report the given versions +// as supported +func PluginSupports(supportedVersions ...string) PluginInfo { + if len(supportedVersions) < 1 { + panic("programmer error: you must support at least one version") + } + return &pluginInfo{ + CNIVersion_: Current(), + SupportedVersions_: supportedVersions, + } +} + +// PluginDecoder can decode the response returned by a plugin's VERSION command +type PluginDecoder struct{} + +func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) { + var info pluginInfo + err := json.Unmarshal(jsonBytes, &info) + if err != nil { + return nil, fmt.Errorf("decoding version info: %s", err) + } + if info.CNIVersion_ == "" { + return nil, fmt.Errorf("decoding version info: missing field cniVersion") + } + if len(info.SupportedVersions_) == 0 { + if info.CNIVersion_ == "0.2.0" { + return PluginSupports("0.1.0", "0.2.0"), nil + } + return nil, fmt.Errorf("decoding version info: missing field supportedVersions") + } + return &info, nil +} + +// ParseVersion parses a version string like "3.0.1" or "0.4.5" into major, +// minor, and micro numbers or returns an error +func ParseVersion(version string) (int, int, int, error) { + var major, minor, micro int + if version == "" { + return -1, -1, -1, fmt.Errorf("invalid version %q: the version is empty", version) + } + + parts := strings.Split(version, ".") + if len(parts) >= 4 { + return -1, -1, -1, fmt.Errorf("invalid version %q: too many parts", version) + } + + major, err := strconv.Atoi(parts[0]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %v", parts[0], err) + } + + if len(parts) >= 2 { + minor, err = strconv.Atoi(parts[1]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %v", parts[1], err) + } + } + + if len(parts) >= 3 { + micro, err = strconv.Atoi(parts[2]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %v", parts[2], err) + } + } + + return major, minor, micro, nil +} + +// GreaterThanOrEqualTo takes two string versions, parses them into major/minor/micro +// numbers, and compares them to determine whether the first version is greater +// than or equal to the second +func GreaterThanOrEqualTo(version, otherVersion string) (bool, error) { + firstMajor, firstMinor, firstMicro, err := ParseVersion(version) + if err != nil { + return false, err + } + + secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion) + if err != nil { + return false, err + } + + if firstMajor > secondMajor { + return true, nil + } else if firstMajor == secondMajor { + if firstMinor > secondMinor { + return true, nil + } else if firstMinor == secondMinor && firstMicro >= secondMicro { + return true, nil + } + } + return false, nil +} diff --git a/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go b/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go new file mode 100644 index 000000000..25c3810b2 --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go @@ -0,0 +1,49 @@ +// Copyright 2016 CNI authors +// +// 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 version + +import "fmt" + +type ErrorIncompatible struct { + Config string + Supported []string +} + +func (e *ErrorIncompatible) Details() string { + return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Supported) +} + +func (e *ErrorIncompatible) Error() string { + return fmt.Sprintf("incompatible CNI versions: %s", e.Details()) +} + +type Reconciler struct{} + +func (r *Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible { + return r.CheckRaw(configVersion, pluginInfo.SupportedVersions()) +} + +func (*Reconciler) CheckRaw(configVersion string, supportedVersions []string) *ErrorIncompatible { + for _, supportedVersion := range supportedVersions { + if configVersion == supportedVersion { + return nil + } + } + + return &ErrorIncompatible{ + Config: configVersion, + Supported: supportedVersions, + } +} diff --git a/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/version.go b/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/version.go new file mode 100644 index 000000000..8f3508e61 --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/cni/pkg/version/version.go @@ -0,0 +1,83 @@ +// Copyright 2016 CNI authors +// +// 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 version + +import ( + "encoding/json" + "fmt" + + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/types/020" + "github.com/containernetworking/cni/pkg/types/current" +) + +// Current reports the version of the CNI spec implemented by this library +func Current() string { + return "0.4.0" +} + +// Legacy PluginInfo describes a plugin that is backwards compatible with the +// CNI spec version 0.1.0. In particular, a runtime compiled against the 0.1.0 +// library ought to work correctly with a plugin that reports support for +// Legacy versions. +// +// Any future CNI spec versions which meet this definition should be added to +// this list. +var Legacy = PluginSupports("0.1.0", "0.2.0") +var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0") + +var resultFactories = []struct { + supportedVersions []string + newResult types.ResultFactoryFunc +}{ + {current.SupportedVersions, current.NewResult}, + {types020.SupportedVersions, types020.NewResult}, +} + +// Finds a Result object matching the requested version (if any) and asks +// that object to parse the plugin result, returning an error if parsing failed. +func NewResult(version string, resultBytes []byte) (types.Result, error) { + reconciler := &Reconciler{} + for _, resultFactory := range resultFactories { + err := reconciler.CheckRaw(version, resultFactory.supportedVersions) + if err == nil { + // Result supports this version + return resultFactory.newResult(resultBytes) + } + } + + return nil, fmt.Errorf("unsupported CNI result version %q", version) +} + +// ParsePrevResult parses a prevResult in a NetConf structure and sets +// the NetConf's PrevResult member to the parsed Result object. +func ParsePrevResult(conf *types.NetConf) error { + if conf.RawPrevResult == nil { + return nil + } + + resultBytes, err := json.Marshal(conf.RawPrevResult) + if err != nil { + return fmt.Errorf("could not serialize prevResult: %v", err) + } + + conf.RawPrevResult = nil + conf.PrevResult, err = NewResult(conf.CNIVersion, resultBytes) + if err != nil { + return fmt.Errorf("could not parse prevResult: %v", err) + } + + return nil +} diff --git a/src/runtime/vendor/github.com/containernetworking/plugins/LICENSE b/src/runtime/vendor/github.com/containernetworking/plugins/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/plugins/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/src/runtime/vendor/github.com/containernetworking/plugins/pkg/ns/README.md b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/ns/README.md new file mode 100644 index 000000000..1e265c7a0 --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/ns/README.md @@ -0,0 +1,41 @@ +### Namespaces, Threads, and Go +On Linux each OS thread can have a different network namespace. Go's thread scheduling model switches goroutines between OS threads based on OS thread load and whether the goroutine would block other goroutines. This can result in a goroutine switching network namespaces without notice and lead to errors in your code. + +### Namespace Switching +Switching namespaces with the `ns.Set()` method is not recommended without additional strategies to prevent unexpected namespace changes when your goroutines switch OS threads. + +Go provides the `runtime.LockOSThread()` function to ensure a specific goroutine executes on its current OS thread and prevents any other goroutine from running in that thread until the locked one exits. Careful usage of `LockOSThread()` and goroutines can provide good control over which network namespace a given goroutine executes in. + +For example, you cannot rely on the `ns.Set()` namespace being the current namespace after the `Set()` call unless you do two things. First, the goroutine calling `Set()` must have previously called `LockOSThread()`. Second, you must ensure `runtime.UnlockOSThread()` is not called somewhere in-between. You also cannot rely on the initial network namespace remaining the current network namespace if any other code in your program switches namespaces, unless you have already called `LockOSThread()` in that goroutine. Note that `LockOSThread()` prevents the Go scheduler from optimally scheduling goroutines for best performance, so `LockOSThread()` should only be used in small, isolated goroutines that release the lock quickly. + +### Do() The Recommended Thing +The `ns.Do()` method provides **partial** control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace (including the root namespace) should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example: + +```go +err = targetNs.Do(func(hostNs ns.NetNS) error { + dummy := &netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: "dummy0", + }, + } + return netlink.LinkAdd(dummy) +}) +``` + +Note this requirement to wrap every network call is very onerous - any libraries you call might call out to network services such as DNS, and all such calls need to be protected after you call `ns.Do()`. All goroutines spawned from within the `ns.Do` will not inherit the new namespace. The CNI plugins all exit very soon after calling `ns.Do()` which helps to minimize the problem. + +When a new thread is spawned in Linux, it inherits the namespace of its parent. In versions of go **prior to 1.10**, if the runtime spawns a new OS thread, it picks the parent randomly. If the chosen parent thread has been moved to a new namespace (even temporarily), the new OS thread will be permanently "stuck in the wrong namespace", and goroutines will non-deterministically switch namespaces as they are rescheduled. + +In short, **there was no safe way to change network namespaces, even temporarily, from within a long-lived, multithreaded Go process**. If you wish to do this, you must use go 1.10 or greater. + + +### Creating network namespaces +Earlier versions of this library managed namespace creation, but as CNI does not actually utilize this feature (and it was essentially unmaintained), it was removed. If you're writing a container runtime, you should implement namespace management yourself. However, there are some gotchas when doing so, especially around handling `/var/run/netns`. A reasonably correct reference implementation, borrowed from `rkt`, can be found in `pkg/testutils/netns_linux.go` if you're in need of a source of inspiration. + + +### Further Reading + - https://github.com/golang/go/wiki/LockOSThread + - http://morsmachine.dk/go-scheduler + - https://github.com/containernetworking/cni/issues/262 + - https://golang.org/pkg/runtime/ + - https://www.weave.works/blog/linux-namespaces-and-go-don-t-mix diff --git a/src/runtime/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go new file mode 100644 index 000000000..31ad5f622 --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/ns/ns_linux.go @@ -0,0 +1,216 @@ +// Copyright 2015-2017 CNI authors +// +// 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 ns + +import ( + "fmt" + "os" + "runtime" + "sync" + "syscall" + + "golang.org/x/sys/unix" +) + +// Returns an object representing the current OS thread's network namespace +func GetCurrentNS() (NetNS, error) { + return GetNS(getCurrentThreadNetNSPath()) +} + +func getCurrentThreadNetNSPath() string { + // /proc/self/ns/net returns the namespace of the main thread, not + // of whatever thread this goroutine is running on. Make sure we + // use the thread's net namespace since the thread is switching around + return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) +} + +func (ns *netNS) Close() error { + if err := ns.errorIfClosed(); err != nil { + return err + } + + if err := ns.file.Close(); err != nil { + return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err) + } + ns.closed = true + + return nil +} + +func (ns *netNS) Set() error { + if err := ns.errorIfClosed(); err != nil { + return err + } + + if err := unix.Setns(int(ns.Fd()), unix.CLONE_NEWNET); err != nil { + return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err) + } + + return nil +} + +type NetNS interface { + // Executes the passed closure in this object's network namespace, + // attempting to restore the original namespace before returning. + // However, since each OS thread can have a different network namespace, + // and Go's thread scheduling is highly variable, callers cannot + // guarantee any specific namespace is set unless operations that + // require that namespace are wrapped with Do(). Also, no code called + // from Do() should call runtime.UnlockOSThread(), or the risk + // of executing code in an incorrect namespace will be greater. See + // https://github.com/golang/go/wiki/LockOSThread for further details. + Do(toRun func(NetNS) error) error + + // Sets the current network namespace to this object's network namespace. + // Note that since Go's thread scheduling is highly variable, callers + // cannot guarantee the requested namespace will be the current namespace + // after this function is called; to ensure this wrap operations that + // require the namespace with Do() instead. + Set() error + + // Returns the filesystem path representing this object's network namespace + Path() string + + // Returns a file descriptor representing this object's network namespace + Fd() uintptr + + // Cleans up this instance of the network namespace; if this instance + // is the last user the namespace will be destroyed + Close() error +} + +type netNS struct { + file *os.File + closed bool +} + +// netNS implements the NetNS interface +var _ NetNS = &netNS{} + +const ( + // https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h + NSFS_MAGIC = 0x6e736673 + PROCFS_MAGIC = 0x9fa0 +) + +type NSPathNotExistErr struct{ msg string } + +func (e NSPathNotExistErr) Error() string { return e.msg } + +type NSPathNotNSErr struct{ msg string } + +func (e NSPathNotNSErr) Error() string { return e.msg } + +func IsNSorErr(nspath string) error { + stat := syscall.Statfs_t{} + if err := syscall.Statfs(nspath, &stat); err != nil { + if os.IsNotExist(err) { + err = NSPathNotExistErr{msg: fmt.Sprintf("failed to Statfs %q: %v", nspath, err)} + } else { + err = fmt.Errorf("failed to Statfs %q: %v", nspath, err) + } + return err + } + + switch stat.Type { + case PROCFS_MAGIC, NSFS_MAGIC: + return nil + default: + return NSPathNotNSErr{msg: fmt.Sprintf("unknown FS magic on %q: %x", nspath, stat.Type)} + } +} + +// Returns an object representing the namespace referred to by @path +func GetNS(nspath string) (NetNS, error) { + err := IsNSorErr(nspath) + if err != nil { + return nil, err + } + + fd, err := os.Open(nspath) + if err != nil { + return nil, err + } + + return &netNS{file: fd}, nil +} + +func (ns *netNS) Path() string { + return ns.file.Name() +} + +func (ns *netNS) Fd() uintptr { + return ns.file.Fd() +} + +func (ns *netNS) errorIfClosed() error { + if ns.closed { + return fmt.Errorf("%q has already been closed", ns.file.Name()) + } + return nil +} + +func (ns *netNS) Do(toRun func(NetNS) error) error { + if err := ns.errorIfClosed(); err != nil { + return err + } + + containedCall := func(hostNS NetNS) error { + threadNS, err := GetCurrentNS() + if err != nil { + return fmt.Errorf("failed to open current netns: %v", err) + } + defer threadNS.Close() + + // switch to target namespace + if err = ns.Set(); err != nil { + return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err) + } + defer threadNS.Set() // switch back + + return toRun(hostNS) + } + + // save a handle to current network namespace + hostNS, err := GetCurrentNS() + if err != nil { + return fmt.Errorf("Failed to open current namespace: %v", err) + } + defer hostNS.Close() + + var wg sync.WaitGroup + wg.Add(1) + + var innerError error + go func() { + defer wg.Done() + runtime.LockOSThread() + innerError = containedCall(hostNS) + }() + wg.Wait() + + return innerError +} + +// WithNetNSPath executes the passed closure under the given network +// namespace, restoring the original namespace afterwards. +func WithNetNSPath(nspath string, toRun func(NetNS) error) error { + ns, err := GetNS(nspath) + if err != nil { + return err + } + defer ns.Close() + return ns.Do(toRun) +} diff --git a/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/bad_reader.go b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/bad_reader.go new file mode 100644 index 000000000..f9d0aded5 --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/bad_reader.go @@ -0,0 +1,33 @@ +// Copyright 2016 CNI authors +// +// 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 testutils + +import "errors" + +// BadReader is an io.Reader which always errors +type BadReader struct { + Error error +} + +func (r *BadReader) Read(buffer []byte) (int, error) { + if r.Error != nil { + return 0, r.Error + } + return 0, errors.New("banana") +} + +func (r *BadReader) Close() error { + return nil +} diff --git a/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/cmd.go b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/cmd.go new file mode 100644 index 000000000..ce600f683 --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/cmd.go @@ -0,0 +1,112 @@ +// Copyright 2016 CNI authors +// +// 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 testutils + +import ( + "io/ioutil" + "os" + + "github.com/containernetworking/cni/pkg/skel" + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/version" +) + +func envCleanup() { + os.Unsetenv("CNI_COMMAND") + os.Unsetenv("CNI_PATH") + os.Unsetenv("CNI_NETNS") + os.Unsetenv("CNI_IFNAME") + os.Unsetenv("CNI_CONTAINERID") +} + +func CmdAdd(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() error) (types.Result, []byte, error) { + os.Setenv("CNI_COMMAND", "ADD") + os.Setenv("CNI_PATH", os.Getenv("PATH")) + os.Setenv("CNI_NETNS", cniNetns) + os.Setenv("CNI_IFNAME", cniIfname) + os.Setenv("CNI_CONTAINERID", cniContainerID) + defer envCleanup() + + // Redirect stdout to capture plugin result + oldStdout := os.Stdout + r, w, err := os.Pipe() + if err != nil { + return nil, nil, err + } + + os.Stdout = w + err = f() + w.Close() + + var out []byte + if err == nil { + out, err = ioutil.ReadAll(r) + } + os.Stdout = oldStdout + + // Return errors after restoring stdout so Ginkgo will correctly + // emit verbose error information on stdout + if err != nil { + return nil, nil, err + } + + // Plugin must return result in same version as specified in netconf + versionDecoder := &version.ConfigDecoder{} + confVersion, err := versionDecoder.Decode(conf) + if err != nil { + return nil, nil, err + } + + result, err := version.NewResult(confVersion, out) + if err != nil { + return nil, nil, err + } + + return result, out, nil +} + +func CmdAddWithArgs(args *skel.CmdArgs, f func() error) (types.Result, []byte, error) { + return CmdAdd(args.Netns, args.ContainerID, args.IfName, args.StdinData, f) +} + +func CmdCheck(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() error) error { + os.Setenv("CNI_COMMAND", "CHECK") + os.Setenv("CNI_PATH", os.Getenv("PATH")) + os.Setenv("CNI_NETNS", cniNetns) + os.Setenv("CNI_IFNAME", cniIfname) + os.Setenv("CNI_CONTAINERID", cniContainerID) + defer envCleanup() + + return f() +} + +func CmdCheckWithArgs(args *skel.CmdArgs, f func() error) error { + return CmdCheck(args.Netns, args.ContainerID, args.IfName, args.StdinData, f) +} + +func CmdDel(cniNetns, cniContainerID, cniIfname string, f func() error) error { + os.Setenv("CNI_COMMAND", "DEL") + os.Setenv("CNI_PATH", os.Getenv("PATH")) + os.Setenv("CNI_NETNS", cniNetns) + os.Setenv("CNI_IFNAME", cniIfname) + os.Setenv("CNI_CONTAINERID", cniContainerID) + defer envCleanup() + + return f() +} + +func CmdDelWithArgs(args *skel.CmdArgs, f func() error) error { + return CmdDel(args.Netns, args.ContainerID, args.IfName, f) +} diff --git a/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/netns_linux.go b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/netns_linux.go new file mode 100644 index 000000000..6d56e4050 --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/netns_linux.go @@ -0,0 +1,157 @@ +// Copyright 2018 CNI authors +// +// 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 testutils + +import ( + "crypto/rand" + "fmt" + "os" + "path" + "runtime" + "strings" + "sync" + + "github.com/containernetworking/plugins/pkg/ns" + "golang.org/x/sys/unix" +) + +const nsRunDir = "/var/run/netns" + +// Creates a new persistent (bind-mounted) network namespace and returns an object +// representing that namespace, without switching to it. +func NewNS() (ns.NetNS, error) { + + b := make([]byte, 16) + _, err := rand.Reader.Read(b) + if err != nil { + return nil, fmt.Errorf("failed to generate random netns name: %v", err) + } + + // Create the directory for mounting network namespaces + // This needs to be a shared mountpoint in case it is mounted in to + // other namespaces (containers) + err = os.MkdirAll(nsRunDir, 0755) + if err != nil { + return nil, err + } + + // Remount the namespace directory shared. This will fail if it is not + // already a mountpoint, so bind-mount it on to itself to "upgrade" it + // to a mountpoint. + err = unix.Mount("", nsRunDir, "none", unix.MS_SHARED|unix.MS_REC, "") + if err != nil { + if err != unix.EINVAL { + return nil, fmt.Errorf("mount --make-rshared %s failed: %q", nsRunDir, err) + } + + // Recursively remount /var/run/netns on itself. The recursive flag is + // so that any existing netns bindmounts are carried over. + err = unix.Mount(nsRunDir, nsRunDir, "none", unix.MS_BIND|unix.MS_REC, "") + if err != nil { + return nil, fmt.Errorf("mount --rbind %s %s failed: %q", nsRunDir, nsRunDir, err) + } + + // Now we can make it shared + err = unix.Mount("", nsRunDir, "none", unix.MS_SHARED|unix.MS_REC, "") + if err != nil { + return nil, fmt.Errorf("mount --make-rshared %s failed: %q", nsRunDir, err) + } + + } + + nsName := fmt.Sprintf("cnitest-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) + + // create an empty file at the mount point + nsPath := path.Join(nsRunDir, nsName) + mountPointFd, err := os.Create(nsPath) + if err != nil { + return nil, err + } + mountPointFd.Close() + + // Ensure the mount point is cleaned up on errors; if the namespace + // was successfully mounted this will have no effect because the file + // is in-use + defer os.RemoveAll(nsPath) + + var wg sync.WaitGroup + wg.Add(1) + + // do namespace work in a dedicated goroutine, so that we can safely + // Lock/Unlock OSThread without upsetting the lock/unlock state of + // the caller of this function + go (func() { + defer wg.Done() + runtime.LockOSThread() + // Don't unlock. By not unlocking, golang will kill the OS thread when the + // goroutine is done (for go1.10+) + + var origNS ns.NetNS + origNS, err = ns.GetNS(getCurrentThreadNetNSPath()) + if err != nil { + return + } + defer origNS.Close() + + // create a new netns on the current thread + err = unix.Unshare(unix.CLONE_NEWNET) + if err != nil { + return + } + + // Put this thread back to the orig ns, since it might get reused (pre go1.10) + defer origNS.Set() + + // bind mount the netns from the current thread (from /proc) onto the + // mount point. This causes the namespace to persist, even when there + // are no threads in the ns. + err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "") + if err != nil { + err = fmt.Errorf("failed to bind mount ns at %s: %v", nsPath, err) + } + })() + wg.Wait() + + if err != nil { + return nil, fmt.Errorf("failed to create namespace: %v", err) + } + + return ns.GetNS(nsPath) +} + +// UnmountNS unmounts the NS held by the netns object +func UnmountNS(ns ns.NetNS) error { + nsPath := ns.Path() + // Only unmount if it's been bind-mounted (don't touch namespaces in /proc...) + if strings.HasPrefix(nsPath, nsRunDir) { + if err := unix.Unmount(nsPath, 0); err != nil { + return fmt.Errorf("failed to unmount NS: at %s: %v", nsPath, err) + } + + if err := os.Remove(nsPath); err != nil { + return fmt.Errorf("failed to remove ns path %s: %v", nsPath, err) + } + } + + return nil +} + +// getCurrentThreadNetNSPath copied from pkg/ns +func getCurrentThreadNetNSPath() string { + // /proc/self/ns/net returns the namespace of the main thread, not + // of whatever thread this goroutine is running on. Make sure we + // use the thread's net namespace since the thread is switching around + return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) +} diff --git a/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/ping.go b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/ping.go new file mode 100644 index 000000000..5ee9db1c4 --- /dev/null +++ b/src/runtime/vendor/github.com/containernetworking/plugins/pkg/testutils/ping.go @@ -0,0 +1,55 @@ +// Copyright 2017 CNI authors +// +// 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 testutils + +import ( + "bytes" + "fmt" + "os/exec" + "strconv" + "syscall" +) + +// Ping shells out to the `ping` command. Returns nil if successful. +func Ping(saddr, daddr string, isV6 bool, timeoutSec int) error { + args := []string{ + "-c", "1", + "-W", strconv.Itoa(timeoutSec), + "-I", saddr, + daddr, + } + + bin := "ping" + if isV6 { + bin = "ping6" + } + + cmd := exec.Command(bin, args...) + var stderr bytes.Buffer + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + switch e := err.(type) { + case *exec.ExitError: + return fmt.Errorf("%v exit status %d: %s", + args, e.Sys().(syscall.WaitStatus).ExitStatus(), + stderr.String()) + default: + return err + } + } + + return nil +} diff --git a/src/runtime/vendor/github.com/coreos/go-systemd/LICENSE b/src/runtime/vendor/github.com/coreos/go-systemd/LICENSE new file mode 100644 index 000000000..37ec93a14 --- /dev/null +++ b/src/runtime/vendor/github.com/coreos/go-systemd/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/src/runtime/vendor/github.com/coreos/go-systemd/NOTICE b/src/runtime/vendor/github.com/coreos/go-systemd/NOTICE new file mode 100644 index 000000000..23a0ada2f --- /dev/null +++ b/src/runtime/vendor/github.com/coreos/go-systemd/NOTICE @@ -0,0 +1,5 @@ +CoreOS Project +Copyright 2018 CoreOS, Inc + +This product includes software developed at CoreOS, Inc. +(http://www.coreos.com/). diff --git a/src/runtime/vendor/github.com/coreos/go-systemd/dbus/dbus.go b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/dbus.go new file mode 100644 index 000000000..1d54810af --- /dev/null +++ b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/dbus.go @@ -0,0 +1,240 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/ +package dbus + +import ( + "encoding/hex" + "fmt" + "os" + "strconv" + "strings" + "sync" + + "github.com/godbus/dbus" +) + +const ( + alpha = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ` + num = `0123456789` + alphanum = alpha + num + signalBuffer = 100 +) + +// needsEscape checks whether a byte in a potential dbus ObjectPath needs to be escaped +func needsEscape(i int, b byte) bool { + // Escape everything that is not a-z-A-Z-0-9 + // Also escape 0-9 if it's the first character + return strings.IndexByte(alphanum, b) == -1 || + (i == 0 && strings.IndexByte(num, b) != -1) +} + +// PathBusEscape sanitizes a constituent string of a dbus ObjectPath using the +// rules that systemd uses for serializing special characters. +func PathBusEscape(path string) string { + // Special case the empty string + if len(path) == 0 { + return "_" + } + n := []byte{} + for i := 0; i < len(path); i++ { + c := path[i] + if needsEscape(i, c) { + e := fmt.Sprintf("_%x", c) + n = append(n, []byte(e)...) + } else { + n = append(n, c) + } + } + return string(n) +} + +// pathBusUnescape is the inverse of PathBusEscape. +func pathBusUnescape(path string) string { + if path == "_" { + return "" + } + n := []byte{} + for i := 0; i < len(path); i++ { + c := path[i] + if c == '_' && i+2 < len(path) { + res, err := hex.DecodeString(path[i+1 : i+3]) + if err == nil { + n = append(n, res...) + } + i += 2 + } else { + n = append(n, c) + } + } + return string(n) +} + +// Conn is a connection to systemd's dbus endpoint. +type Conn struct { + // sysconn/sysobj are only used to call dbus methods + sysconn *dbus.Conn + sysobj dbus.BusObject + + // sigconn/sigobj are only used to receive dbus signals + sigconn *dbus.Conn + sigobj dbus.BusObject + + jobListener struct { + jobs map[dbus.ObjectPath]chan<- string + sync.Mutex + } + subStateSubscriber struct { + updateCh chan<- *SubStateUpdate + errCh chan<- error + sync.Mutex + ignore map[dbus.ObjectPath]int64 + cleanIgnore int64 + } + propertiesSubscriber struct { + updateCh chan<- *PropertiesUpdate + errCh chan<- error + sync.Mutex + } +} + +// New establishes a connection to any available bus and authenticates. +// Callers should call Close() when done with the connection. +func New() (*Conn, error) { + conn, err := NewSystemConnection() + if err != nil && os.Geteuid() == 0 { + return NewSystemdConnection() + } + return conn, err +} + +// NewSystemConnection establishes a connection to the system bus and authenticates. +// Callers should call Close() when done with the connection +func NewSystemConnection() (*Conn, error) { + return NewConnection(func() (*dbus.Conn, error) { + return dbusAuthHelloConnection(dbus.SystemBusPrivate) + }) +} + +// NewUserConnection establishes a connection to the session bus and +// authenticates. This can be used to connect to systemd user instances. +// Callers should call Close() when done with the connection. +func NewUserConnection() (*Conn, error) { + return NewConnection(func() (*dbus.Conn, error) { + return dbusAuthHelloConnection(dbus.SessionBusPrivate) + }) +} + +// NewSystemdConnection establishes a private, direct connection to systemd. +// This can be used for communicating with systemd without a dbus daemon. +// Callers should call Close() when done with the connection. +func NewSystemdConnection() (*Conn, error) { + return NewConnection(func() (*dbus.Conn, error) { + // We skip Hello when talking directly to systemd. + return dbusAuthConnection(func() (*dbus.Conn, error) { + return dbus.Dial("unix:path=/run/systemd/private") + }) + }) +} + +// Close closes an established connection +func (c *Conn) Close() { + c.sysconn.Close() + c.sigconn.Close() +} + +// NewConnection establishes a connection to a bus using a caller-supplied function. +// This allows connecting to remote buses through a user-supplied mechanism. +// The supplied function may be called multiple times, and should return independent connections. +// The returned connection must be fully initialised: the org.freedesktop.DBus.Hello call must have succeeded, +// and any authentication should be handled by the function. +func NewConnection(dialBus func() (*dbus.Conn, error)) (*Conn, error) { + sysconn, err := dialBus() + if err != nil { + return nil, err + } + + sigconn, err := dialBus() + if err != nil { + sysconn.Close() + return nil, err + } + + c := &Conn{ + sysconn: sysconn, + sysobj: systemdObject(sysconn), + sigconn: sigconn, + sigobj: systemdObject(sigconn), + } + + c.subStateSubscriber.ignore = make(map[dbus.ObjectPath]int64) + c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string) + + // Setup the listeners on jobs so that we can get completions + c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, + "type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'") + + c.dispatch() + return c, nil +} + +// GetManagerProperty returns the value of a property on the org.freedesktop.systemd1.Manager +// interface. The value is returned in its string representation, as defined at +// https://developer.gnome.org/glib/unstable/gvariant-text.html +func (c *Conn) GetManagerProperty(prop string) (string, error) { + variant, err := c.sysobj.GetProperty("org.freedesktop.systemd1.Manager." + prop) + if err != nil { + return "", err + } + return variant.String(), nil +} + +func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) { + conn, err := createBus() + if err != nil { + return nil, err + } + + // Only use EXTERNAL method, and hardcode the uid (not username) + // to avoid a username lookup (which requires a dynamically linked + // libc) + methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))} + + err = conn.Auth(methods) + if err != nil { + conn.Close() + return nil, err + } + + return conn, nil +} + +func dbusAuthHelloConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) { + conn, err := dbusAuthConnection(createBus) + if err != nil { + return nil, err + } + + if err = conn.Hello(); err != nil { + conn.Close() + return nil, err + } + + return conn, nil +} + +func systemdObject(conn *dbus.Conn) dbus.BusObject { + return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1")) +} diff --git a/src/runtime/vendor/github.com/coreos/go-systemd/dbus/methods.go b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/methods.go new file mode 100644 index 000000000..0b4207229 --- /dev/null +++ b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/methods.go @@ -0,0 +1,592 @@ +// Copyright 2015, 2018 CoreOS, Inc. +// +// 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 dbus + +import ( + "errors" + "fmt" + "path" + "strconv" + + "github.com/godbus/dbus" +) + +func (c *Conn) jobComplete(signal *dbus.Signal) { + var id uint32 + var job dbus.ObjectPath + var unit string + var result string + dbus.Store(signal.Body, &id, &job, &unit, &result) + c.jobListener.Lock() + out, ok := c.jobListener.jobs[job] + if ok { + out <- result + delete(c.jobListener.jobs, job) + } + c.jobListener.Unlock() +} + +func (c *Conn) startJob(ch chan<- string, job string, args ...interface{}) (int, error) { + if ch != nil { + c.jobListener.Lock() + defer c.jobListener.Unlock() + } + + var p dbus.ObjectPath + err := c.sysobj.Call(job, 0, args...).Store(&p) + if err != nil { + return 0, err + } + + if ch != nil { + c.jobListener.jobs[p] = ch + } + + // ignore error since 0 is fine if conversion fails + jobID, _ := strconv.Atoi(path.Base(string(p))) + + return jobID, nil +} + +// StartUnit enqueues a start job and depending jobs, if any (unless otherwise +// specified by the mode string). +// +// Takes the unit to activate, plus a mode string. The mode needs to be one of +// replace, fail, isolate, ignore-dependencies, ignore-requirements. If +// "replace" the call will start the unit and its dependencies, possibly +// replacing already queued jobs that conflict with this. If "fail" the call +// will start the unit and its dependencies, but will fail if this would change +// an already queued job. If "isolate" the call will start the unit in question +// and terminate all units that aren't dependencies of it. If +// "ignore-dependencies" it will start a unit but ignore all its dependencies. +// If "ignore-requirements" it will start a unit but only ignore the +// requirement dependencies. It is not recommended to make use of the latter +// two options. +// +// If the provided channel is non-nil, a result string will be sent to it upon +// job completion: one of done, canceled, timeout, failed, dependency, skipped. +// done indicates successful execution of a job. canceled indicates that a job +// has been canceled before it finished execution. timeout indicates that the +// job timeout was reached. failed indicates that the job failed. dependency +// indicates that a job this job has been depending on failed and the job hence +// has been removed too. skipped indicates that a job was skipped because it +// didn't apply to the units current state. +// +// If no error occurs, the ID of the underlying systemd job will be returned. There +// does exist the possibility for no error to be returned, but for the returned job +// ID to be 0. In this case, the actual underlying ID is not 0 and this datapoint +// should not be considered authoritative. +// +// If an error does occur, it will be returned to the user alongside a job ID of 0. +func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode) +} + +// StopUnit is similar to StartUnit but stops the specified unit rather +// than starting it. +func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode) +} + +// ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise. +func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode) +} + +// RestartUnit restarts a service. If a service is restarted that isn't +// running it will be started. +func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode) +} + +// TryRestartUnit is like RestartUnit, except that a service that isn't running +// is not affected by the restart. +func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode) +} + +// ReloadOrRestart attempts a reload if the unit supports it and use a restart +// otherwise. +func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode) +} + +// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try" +// flavored restart otherwise. +func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode) +} + +// StartTransientUnit() may be used to create and start a transient unit, which +// will be released as soon as it is not running or referenced anymore or the +// system is rebooted. name is the unit name including suffix, and must be +// unique. mode is the same as in StartUnit(), properties contains properties +// of the unit. +func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) { + return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0)) +} + +// KillUnit takes the unit name and a UNIX signal number to send. All of the unit's +// processes are killed. +func (c *Conn) KillUnit(name string, signal int32) { + c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store() +} + +// ResetFailedUnit resets the "failed" state of a specific unit. +func (c *Conn) ResetFailedUnit(name string) error { + return c.sysobj.Call("org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store() +} + +// SystemState returns the systemd state. Equivalent to `systemctl is-system-running`. +func (c *Conn) SystemState() (*Property, error) { + var err error + var prop dbus.Variant + + obj := c.sysconn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1") + err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.systemd1.Manager", "SystemState").Store(&prop) + if err != nil { + return nil, err + } + + return &Property{Name: "SystemState", Value: prop}, nil +} + +// getProperties takes the unit path and returns all of its dbus object properties, for the given dbus interface +func (c *Conn) getProperties(path dbus.ObjectPath, dbusInterface string) (map[string]interface{}, error) { + var err error + var props map[string]dbus.Variant + + if !path.IsValid() { + return nil, fmt.Errorf("invalid unit name: %v", path) + } + + obj := c.sysconn.Object("org.freedesktop.systemd1", path) + err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props) + if err != nil { + return nil, err + } + + out := make(map[string]interface{}, len(props)) + for k, v := range props { + out[k] = v.Value() + } + + return out, nil +} + +// GetUnitProperties takes the (unescaped) unit name and returns all of its dbus object properties. +func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) { + path := unitPath(unit) + return c.getProperties(path, "org.freedesktop.systemd1.Unit") +} + +// GetUnitProperties takes the (escaped) unit path and returns all of its dbus object properties. +func (c *Conn) GetUnitPathProperties(path dbus.ObjectPath) (map[string]interface{}, error) { + return c.getProperties(path, "org.freedesktop.systemd1.Unit") +} + +func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) { + var err error + var prop dbus.Variant + + path := unitPath(unit) + if !path.IsValid() { + return nil, errors.New("invalid unit name: " + unit) + } + + obj := c.sysconn.Object("org.freedesktop.systemd1", path) + err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop) + if err != nil { + return nil, err + } + + return &Property{Name: propertyName, Value: prop}, nil +} + +func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) { + return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName) +} + +// GetServiceProperty returns property for given service name and property name +func (c *Conn) GetServiceProperty(service string, propertyName string) (*Property, error) { + return c.getProperty(service, "org.freedesktop.systemd1.Service", propertyName) +} + +// GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type. +// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope +// return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit +func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) { + path := unitPath(unit) + return c.getProperties(path, "org.freedesktop.systemd1."+unitType) +} + +// SetUnitProperties() may be used to modify certain unit properties at runtime. +// Not all properties may be changed at runtime, but many resource management +// settings (primarily those in systemd.cgroup(5)) may. The changes are applied +// instantly, and stored on disk for future boots, unless runtime is true, in which +// case the settings only apply until the next reboot. name is the name of the unit +// to modify. properties are the settings to set, encoded as an array of property +// name and value pairs. +func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error { + return c.sysobj.Call("org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store() +} + +func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) { + return c.getProperty(unit, "org.freedesktop.systemd1."+unitType, propertyName) +} + +type UnitStatus struct { + Name string // The primary unit name as string + Description string // The human readable description string + LoadState string // The load state (i.e. whether the unit file has been loaded successfully) + ActiveState string // The active state (i.e. whether the unit is currently started or not) + SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not) + Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string. + Path dbus.ObjectPath // The unit object path + JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise + JobType string // The job type as string + JobPath dbus.ObjectPath // The job object path +} + +type storeFunc func(retvalues ...interface{}) error + +func (c *Conn) listUnitsInternal(f storeFunc) ([]UnitStatus, error) { + result := make([][]interface{}, 0) + err := f(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + status := make([]UnitStatus, len(result)) + statusInterface := make([]interface{}, len(status)) + for i := range status { + statusInterface[i] = &status[i] + } + + err = dbus.Store(resultInterface, statusInterface...) + if err != nil { + return nil, err + } + + return status, nil +} + +// ListUnits returns an array with all currently loaded units. Note that +// units may be known by multiple names at the same time, and hence there might +// be more unit names loaded than actual units behind them. +func (c *Conn) ListUnits() ([]UnitStatus, error) { + return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store) +} + +// ListUnitsFiltered returns an array with units filtered by state. +// It takes a list of units' statuses to filter. +func (c *Conn) ListUnitsFiltered(states []string) ([]UnitStatus, error) { + return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsFiltered", 0, states).Store) +} + +// ListUnitsByPatterns returns an array with units. +// It takes a list of units' statuses and names to filter. +// Note that units may be known by multiple names at the same time, +// and hence there might be more unit names loaded than actual units behind them. +func (c *Conn) ListUnitsByPatterns(states []string, patterns []string) ([]UnitStatus, error) { + return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsByPatterns", 0, states, patterns).Store) +} + +// ListUnitsByNames returns an array with units. It takes a list of units' +// names and returns an UnitStatus array. Comparing to ListUnitsByPatterns +// method, this method returns statuses even for inactive or non-existing +// units. Input array should contain exact unit names, but not patterns. +// Note: Requires systemd v230 or higher +func (c *Conn) ListUnitsByNames(units []string) ([]UnitStatus, error) { + return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsByNames", 0, units).Store) +} + +type UnitFile struct { + Path string + Type string +} + +func (c *Conn) listUnitFilesInternal(f storeFunc) ([]UnitFile, error) { + result := make([][]interface{}, 0) + err := f(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + files := make([]UnitFile, len(result)) + fileInterface := make([]interface{}, len(files)) + for i := range files { + fileInterface[i] = &files[i] + } + + err = dbus.Store(resultInterface, fileInterface...) + if err != nil { + return nil, err + } + + return files, nil +} + +// ListUnitFiles returns an array of all available units on disk. +func (c *Conn) ListUnitFiles() ([]UnitFile, error) { + return c.listUnitFilesInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitFiles", 0).Store) +} + +// ListUnitFilesByPatterns returns an array of all available units on disk matched the patterns. +func (c *Conn) ListUnitFilesByPatterns(states []string, patterns []string) ([]UnitFile, error) { + return c.listUnitFilesInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns", 0, states, patterns).Store) +} + +type LinkUnitFileChange EnableUnitFileChange + +// LinkUnitFiles() links unit files (that are located outside of the +// usual unit search paths) into the unit search path. +// +// It takes a list of absolute paths to unit files to link and two +// booleans. The first boolean controls whether the unit shall be +// enabled for runtime only (true, /run), or persistently (false, +// /etc). +// The second controls whether symlinks pointing to other units shall +// be replaced if necessary. +// +// This call returns a list of the changes made. The list consists of +// structures with three strings: the type of the change (one of symlink +// or unlink), the file name of the symlink and the destination of the +// symlink. +func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) { + result := make([][]interface{}, 0) + err := c.sysobj.Call("org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]LinkUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return nil, err + } + + return changes, nil +} + +// EnableUnitFiles() may be used to enable one or more units in the system (by +// creating symlinks to them in /etc or /run). +// +// It takes a list of unit files to enable (either just file names or full +// absolute paths if the unit files are residing outside the usual unit +// search paths), and two booleans: the first controls whether the unit shall +// be enabled for runtime only (true, /run), or persistently (false, /etc). +// The second one controls whether symlinks pointing to other units shall +// be replaced if necessary. +// +// This call returns one boolean and an array with the changes made. The +// boolean signals whether the unit files contained any enablement +// information (i.e. an [Install]) section. The changes list consists of +// structures with three strings: the type of the change (one of symlink +// or unlink), the file name of the symlink and the destination of the +// symlink. +func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) { + var carries_install_info bool + + result := make([][]interface{}, 0) + err := c.sysobj.Call("org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result) + if err != nil { + return false, nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]EnableUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return false, nil, err + } + + return carries_install_info, changes, nil +} + +type EnableUnitFileChange struct { + Type string // Type of the change (one of symlink or unlink) + Filename string // File name of the symlink + Destination string // Destination of the symlink +} + +// DisableUnitFiles() may be used to disable one or more units in the system (by +// removing symlinks to them from /etc or /run). +// +// It takes a list of unit files to disable (either just file names or full +// absolute paths if the unit files are residing outside the usual unit +// search paths), and one boolean: whether the unit was enabled for runtime +// only (true, /run), or persistently (false, /etc). +// +// This call returns an array with the changes made. The changes list +// consists of structures with three strings: the type of the change (one of +// symlink or unlink), the file name of the symlink and the destination of the +// symlink. +func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) { + result := make([][]interface{}, 0) + err := c.sysobj.Call("org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]DisableUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return nil, err + } + + return changes, nil +} + +type DisableUnitFileChange struct { + Type string // Type of the change (one of symlink or unlink) + Filename string // File name of the symlink + Destination string // Destination of the symlink +} + +// MaskUnitFiles masks one or more units in the system +// +// It takes three arguments: +// * list of units to mask (either just file names or full +// absolute paths if the unit files are residing outside +// the usual unit search paths) +// * runtime to specify whether the unit was enabled for runtime +// only (true, /run/systemd/..), or persistently (false, /etc/systemd/..) +// * force flag +func (c *Conn) MaskUnitFiles(files []string, runtime bool, force bool) ([]MaskUnitFileChange, error) { + result := make([][]interface{}, 0) + err := c.sysobj.Call("org.freedesktop.systemd1.Manager.MaskUnitFiles", 0, files, runtime, force).Store(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]MaskUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return nil, err + } + + return changes, nil +} + +type MaskUnitFileChange struct { + Type string // Type of the change (one of symlink or unlink) + Filename string // File name of the symlink + Destination string // Destination of the symlink +} + +// UnmaskUnitFiles unmasks one or more units in the system +// +// It takes two arguments: +// * list of unit files to mask (either just file names or full +// absolute paths if the unit files are residing outside +// the usual unit search paths) +// * runtime to specify whether the unit was enabled for runtime +// only (true, /run/systemd/..), or persistently (false, /etc/systemd/..) +func (c *Conn) UnmaskUnitFiles(files []string, runtime bool) ([]UnmaskUnitFileChange, error) { + result := make([][]interface{}, 0) + err := c.sysobj.Call("org.freedesktop.systemd1.Manager.UnmaskUnitFiles", 0, files, runtime).Store(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]UnmaskUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return nil, err + } + + return changes, nil +} + +type UnmaskUnitFileChange struct { + Type string // Type of the change (one of symlink or unlink) + Filename string // File name of the symlink + Destination string // Destination of the symlink +} + +// Reload instructs systemd to scan for and reload unit files. This is +// equivalent to a 'systemctl daemon-reload'. +func (c *Conn) Reload() error { + return c.sysobj.Call("org.freedesktop.systemd1.Manager.Reload", 0).Store() +} + +func unitPath(name string) dbus.ObjectPath { + return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name)) +} + +// unitName returns the unescaped base element of the supplied escaped path +func unitName(dpath dbus.ObjectPath) string { + return pathBusUnescape(path.Base(string(dpath))) +} diff --git a/src/runtime/vendor/github.com/coreos/go-systemd/dbus/properties.go b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/properties.go new file mode 100644 index 000000000..6c8189587 --- /dev/null +++ b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/properties.go @@ -0,0 +1,237 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 dbus + +import ( + "github.com/godbus/dbus" +) + +// From the systemd docs: +// +// The properties array of StartTransientUnit() may take many of the settings +// that may also be configured in unit files. Not all parameters are currently +// accepted though, but we plan to cover more properties with future release. +// Currently you may set the Description, Slice and all dependency types of +// units, as well as RemainAfterExit, ExecStart for service units, +// TimeoutStopUSec and PIDs for scope units, and CPUAccounting, CPUShares, +// BlockIOAccounting, BlockIOWeight, BlockIOReadBandwidth, +// BlockIOWriteBandwidth, BlockIODeviceWeight, MemoryAccounting, MemoryLimit, +// DevicePolicy, DeviceAllow for services/scopes/slices. These fields map +// directly to their counterparts in unit files and as normal D-Bus object +// properties. The exception here is the PIDs field of scope units which is +// used for construction of the scope only and specifies the initial PIDs to +// add to the scope object. + +type Property struct { + Name string + Value dbus.Variant +} + +type PropertyCollection struct { + Name string + Properties []Property +} + +type execStart struct { + Path string // the binary path to execute + Args []string // an array with all arguments to pass to the executed command, starting with argument 0 + UncleanIsFailure bool // a boolean whether it should be considered a failure if the process exits uncleanly +} + +// PropExecStart sets the ExecStart service property. The first argument is a +// slice with the binary path to execute followed by the arguments to pass to +// the executed command. See +// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= +func PropExecStart(command []string, uncleanIsFailure bool) Property { + execStarts := []execStart{ + execStart{ + Path: command[0], + Args: command, + UncleanIsFailure: uncleanIsFailure, + }, + } + + return Property{ + Name: "ExecStart", + Value: dbus.MakeVariant(execStarts), + } +} + +// PropRemainAfterExit sets the RemainAfterExit service property. See +// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit= +func PropRemainAfterExit(b bool) Property { + return Property{ + Name: "RemainAfterExit", + Value: dbus.MakeVariant(b), + } +} + +// PropType sets the Type service property. See +// http://www.freedesktop.org/software/systemd/man/systemd.service.html#Type= +func PropType(t string) Property { + return Property{ + Name: "Type", + Value: dbus.MakeVariant(t), + } +} + +// PropDescription sets the Description unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description= +func PropDescription(desc string) Property { + return Property{ + Name: "Description", + Value: dbus.MakeVariant(desc), + } +} + +func propDependency(name string, units []string) Property { + return Property{ + Name: name, + Value: dbus.MakeVariant(units), + } +} + +// PropRequires sets the Requires unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires= +func PropRequires(units ...string) Property { + return propDependency("Requires", units) +} + +// PropRequiresOverridable sets the RequiresOverridable unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable= +func PropRequiresOverridable(units ...string) Property { + return propDependency("RequiresOverridable", units) +} + +// PropRequisite sets the Requisite unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite= +func PropRequisite(units ...string) Property { + return propDependency("Requisite", units) +} + +// PropRequisiteOverridable sets the RequisiteOverridable unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable= +func PropRequisiteOverridable(units ...string) Property { + return propDependency("RequisiteOverridable", units) +} + +// PropWants sets the Wants unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants= +func PropWants(units ...string) Property { + return propDependency("Wants", units) +} + +// PropBindsTo sets the BindsTo unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo= +func PropBindsTo(units ...string) Property { + return propDependency("BindsTo", units) +} + +// PropRequiredBy sets the RequiredBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy= +func PropRequiredBy(units ...string) Property { + return propDependency("RequiredBy", units) +} + +// PropRequiredByOverridable sets the RequiredByOverridable unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable= +func PropRequiredByOverridable(units ...string) Property { + return propDependency("RequiredByOverridable", units) +} + +// PropWantedBy sets the WantedBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy= +func PropWantedBy(units ...string) Property { + return propDependency("WantedBy", units) +} + +// PropBoundBy sets the BoundBy unit property. See +// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BoundBy= +func PropBoundBy(units ...string) Property { + return propDependency("BoundBy", units) +} + +// PropConflicts sets the Conflicts unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts= +func PropConflicts(units ...string) Property { + return propDependency("Conflicts", units) +} + +// PropConflictedBy sets the ConflictedBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy= +func PropConflictedBy(units ...string) Property { + return propDependency("ConflictedBy", units) +} + +// PropBefore sets the Before unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before= +func PropBefore(units ...string) Property { + return propDependency("Before", units) +} + +// PropAfter sets the After unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After= +func PropAfter(units ...string) Property { + return propDependency("After", units) +} + +// PropOnFailure sets the OnFailure unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure= +func PropOnFailure(units ...string) Property { + return propDependency("OnFailure", units) +} + +// PropTriggers sets the Triggers unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers= +func PropTriggers(units ...string) Property { + return propDependency("Triggers", units) +} + +// PropTriggeredBy sets the TriggeredBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy= +func PropTriggeredBy(units ...string) Property { + return propDependency("TriggeredBy", units) +} + +// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo= +func PropPropagatesReloadTo(units ...string) Property { + return propDependency("PropagatesReloadTo", units) +} + +// PropRequiresMountsFor sets the RequiresMountsFor unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor= +func PropRequiresMountsFor(units ...string) Property { + return propDependency("RequiresMountsFor", units) +} + +// PropSlice sets the Slice unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice= +func PropSlice(slice string) Property { + return Property{ + Name: "Slice", + Value: dbus.MakeVariant(slice), + } +} + +// PropPids sets the PIDs field of scope units used in the initial construction +// of the scope only and specifies the initial PIDs to add to the scope object. +// See https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/#properties +func PropPids(pids ...uint32) Property { + return Property{ + Name: "PIDs", + Value: dbus.MakeVariant(pids), + } +} diff --git a/src/runtime/vendor/github.com/coreos/go-systemd/dbus/set.go b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/set.go new file mode 100644 index 000000000..17c5d4856 --- /dev/null +++ b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/set.go @@ -0,0 +1,47 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 dbus + +type set struct { + data map[string]bool +} + +func (s *set) Add(value string) { + s.data[value] = true +} + +func (s *set) Remove(value string) { + delete(s.data, value) +} + +func (s *set) Contains(value string) (exists bool) { + _, exists = s.data[value] + return +} + +func (s *set) Length() int { + return len(s.data) +} + +func (s *set) Values() (values []string) { + for val := range s.data { + values = append(values, val) + } + return +} + +func newSet() *set { + return &set{make(map[string]bool)} +} diff --git a/src/runtime/vendor/github.com/coreos/go-systemd/dbus/subscription.go b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/subscription.go new file mode 100644 index 000000000..70e63a6f1 --- /dev/null +++ b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/subscription.go @@ -0,0 +1,333 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 dbus + +import ( + "errors" + "log" + "time" + + "github.com/godbus/dbus" +) + +const ( + cleanIgnoreInterval = int64(10 * time.Second) + ignoreInterval = int64(30 * time.Millisecond) +) + +// Subscribe sets up this connection to subscribe to all systemd dbus events. +// This is required before calling SubscribeUnits. When the connection closes +// systemd will automatically stop sending signals so there is no need to +// explicitly call Unsubscribe(). +func (c *Conn) Subscribe() error { + c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, + "type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'") + c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, + "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'") + + return c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store() +} + +// Unsubscribe this connection from systemd dbus events. +func (c *Conn) Unsubscribe() error { + return c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store() +} + +func (c *Conn) dispatch() { + ch := make(chan *dbus.Signal, signalBuffer) + + c.sigconn.Signal(ch) + + go func() { + for { + signal, ok := <-ch + if !ok { + return + } + + if signal.Name == "org.freedesktop.systemd1.Manager.JobRemoved" { + c.jobComplete(signal) + } + + if c.subStateSubscriber.updateCh == nil && + c.propertiesSubscriber.updateCh == nil { + continue + } + + var unitPath dbus.ObjectPath + switch signal.Name { + case "org.freedesktop.systemd1.Manager.JobRemoved": + unitName := signal.Body[2].(string) + c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath) + case "org.freedesktop.systemd1.Manager.UnitNew": + unitPath = signal.Body[1].(dbus.ObjectPath) + case "org.freedesktop.DBus.Properties.PropertiesChanged": + if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" { + unitPath = signal.Path + + if len(signal.Body) >= 2 { + if changed, ok := signal.Body[1].(map[string]dbus.Variant); ok { + c.sendPropertiesUpdate(unitPath, changed) + } + } + } + } + + if unitPath == dbus.ObjectPath("") { + continue + } + + c.sendSubStateUpdate(unitPath) + } + }() +} + +// Returns two unbuffered channels which will receive all changed units every +// interval. Deleted units are sent as nil. +func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) { + return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil) +} + +// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer +// size of the channels, the comparison function for detecting changes and a filter +// function for cutting down on the noise that your channel receives. +func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func(string) bool) (<-chan map[string]*UnitStatus, <-chan error) { + old := make(map[string]*UnitStatus) + statusChan := make(chan map[string]*UnitStatus, buffer) + errChan := make(chan error, buffer) + + go func() { + for { + timerChan := time.After(interval) + + units, err := c.ListUnits() + if err == nil { + cur := make(map[string]*UnitStatus) + for i := range units { + if filterUnit != nil && filterUnit(units[i].Name) { + continue + } + cur[units[i].Name] = &units[i] + } + + // add all new or changed units + changed := make(map[string]*UnitStatus) + for n, u := range cur { + if oldU, ok := old[n]; !ok || isChanged(oldU, u) { + changed[n] = u + } + delete(old, n) + } + + // add all deleted units + for oldN := range old { + changed[oldN] = nil + } + + old = cur + + if len(changed) != 0 { + statusChan <- changed + } + } else { + errChan <- err + } + + <-timerChan + } + }() + + return statusChan, errChan +} + +type SubStateUpdate struct { + UnitName string + SubState string +} + +// SetSubStateSubscriber writes to updateCh when any unit's substate changes. +// Although this writes to updateCh on every state change, the reported state +// may be more recent than the change that generated it (due to an unavoidable +// race in the systemd dbus interface). That is, this method provides a good +// way to keep a current view of all units' states, but is not guaranteed to +// show every state transition they go through. Furthermore, state changes +// will only be written to the channel with non-blocking writes. If updateCh +// is full, it attempts to write an error to errCh; if errCh is full, the error +// passes silently. +func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) { + if c == nil { + msg := "nil receiver" + select { + case errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", msg) + } + return + } + + c.subStateSubscriber.Lock() + defer c.subStateSubscriber.Unlock() + c.subStateSubscriber.updateCh = updateCh + c.subStateSubscriber.errCh = errCh +} + +func (c *Conn) sendSubStateUpdate(unitPath dbus.ObjectPath) { + c.subStateSubscriber.Lock() + defer c.subStateSubscriber.Unlock() + + if c.subStateSubscriber.updateCh == nil { + return + } + + isIgnored := c.shouldIgnore(unitPath) + defer c.cleanIgnore() + if isIgnored { + return + } + + info, err := c.GetUnitPathProperties(unitPath) + if err != nil { + select { + case c.subStateSubscriber.errCh <- err: + default: + log.Printf("full error channel while reporting: %s\n", err) + } + return + } + defer c.updateIgnore(unitPath, info) + + name, ok := info["Id"].(string) + if !ok { + msg := "failed to cast info.Id" + select { + case c.subStateSubscriber.errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", err) + } + return + } + substate, ok := info["SubState"].(string) + if !ok { + msg := "failed to cast info.SubState" + select { + case c.subStateSubscriber.errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", msg) + } + return + } + + update := &SubStateUpdate{name, substate} + select { + case c.subStateSubscriber.updateCh <- update: + default: + msg := "update channel is full" + select { + case c.subStateSubscriber.errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", msg) + } + return + } +} + +// The ignore functions work around a wart in the systemd dbus interface. +// Requesting the properties of an unloaded unit will cause systemd to send a +// pair of UnitNew/UnitRemoved signals. Because we need to get a unit's +// properties on UnitNew (as that's the only indication of a new unit coming up +// for the first time), we would enter an infinite loop if we did not attempt +// to detect and ignore these spurious signals. The signal themselves are +// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an +// unloaded unit's signals for a short time after requesting its properties. +// This means that we will miss e.g. a transient unit being restarted +// *immediately* upon failure and also a transient unit being started +// immediately after requesting its status (with systemctl status, for example, +// because this causes a UnitNew signal to be sent which then causes us to fetch +// the properties). + +func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool { + t, ok := c.subStateSubscriber.ignore[path] + return ok && t >= time.Now().UnixNano() +} + +func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) { + loadState, ok := info["LoadState"].(string) + if !ok { + return + } + + // unit is unloaded - it will trigger bad systemd dbus behavior + if loadState == "not-found" { + c.subStateSubscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval + } +} + +// without this, ignore would grow unboundedly over time +func (c *Conn) cleanIgnore() { + now := time.Now().UnixNano() + if c.subStateSubscriber.cleanIgnore < now { + c.subStateSubscriber.cleanIgnore = now + cleanIgnoreInterval + + for p, t := range c.subStateSubscriber.ignore { + if t < now { + delete(c.subStateSubscriber.ignore, p) + } + } + } +} + +// PropertiesUpdate holds a map of a unit's changed properties +type PropertiesUpdate struct { + UnitName string + Changed map[string]dbus.Variant +} + +// SetPropertiesSubscriber writes to updateCh when any unit's properties +// change. Every property change reported by systemd will be sent; that is, no +// transitions will be "missed" (as they might be with SetSubStateSubscriber). +// However, state changes will only be written to the channel with non-blocking +// writes. If updateCh is full, it attempts to write an error to errCh; if +// errCh is full, the error passes silently. +func (c *Conn) SetPropertiesSubscriber(updateCh chan<- *PropertiesUpdate, errCh chan<- error) { + c.propertiesSubscriber.Lock() + defer c.propertiesSubscriber.Unlock() + c.propertiesSubscriber.updateCh = updateCh + c.propertiesSubscriber.errCh = errCh +} + +// we don't need to worry about shouldIgnore() here because +// sendPropertiesUpdate doesn't call GetProperties() +func (c *Conn) sendPropertiesUpdate(unitPath dbus.ObjectPath, changedProps map[string]dbus.Variant) { + c.propertiesSubscriber.Lock() + defer c.propertiesSubscriber.Unlock() + + if c.propertiesSubscriber.updateCh == nil { + return + } + + update := &PropertiesUpdate{unitName(unitPath), changedProps} + + select { + case c.propertiesSubscriber.updateCh <- update: + default: + msg := "update channel is full" + select { + case c.propertiesSubscriber.errCh <- errors.New(msg): + default: + log.Printf("full error channel while reporting: %s\n", msg) + } + return + } +} diff --git a/src/runtime/vendor/github.com/coreos/go-systemd/dbus/subscription_set.go b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/subscription_set.go new file mode 100644 index 000000000..5b408d584 --- /dev/null +++ b/src/runtime/vendor/github.com/coreos/go-systemd/dbus/subscription_set.go @@ -0,0 +1,57 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 dbus + +import ( + "time" +) + +// SubscriptionSet returns a subscription set which is like conn.Subscribe but +// can filter to only return events for a set of units. +type SubscriptionSet struct { + *set + conn *Conn +} + +func (s *SubscriptionSet) filter(unit string) bool { + return !s.Contains(unit) +} + +// Subscribe starts listening for dbus events for all of the units in the set. +// Returns channels identical to conn.SubscribeUnits. +func (s *SubscriptionSet) Subscribe() (<-chan map[string]*UnitStatus, <-chan error) { + // TODO: Make fully evented by using systemd 209 with properties changed values + return s.conn.SubscribeUnitsCustom(time.Second, 0, + mismatchUnitStatus, + func(unit string) bool { return s.filter(unit) }, + ) +} + +// NewSubscriptionSet returns a new subscription set. +func (conn *Conn) NewSubscriptionSet() *SubscriptionSet { + return &SubscriptionSet{newSet(), conn} +} + +// mismatchUnitStatus returns true if the provided UnitStatus objects +// are not equivalent. false is returned if the objects are equivalent. +// Only the Name, Description and state-related fields are used in +// the comparison. +func mismatchUnitStatus(u1, u2 *UnitStatus) bool { + return u1.Name != u2.Name || + u1.Description != u2.Description || + u1.LoadState != u2.LoadState || + u1.ActiveState != u2.ActiveState || + u1.SubState != u2.SubState +} diff --git a/src/runtime/vendor/github.com/cri-o/cri-o/LICENSE b/src/runtime/vendor/github.com/cri-o/cri-o/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/src/runtime/vendor/github.com/cri-o/cri-o/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/src/runtime/vendor/github.com/cri-o/cri-o/pkg/annotations/annotations.go b/src/runtime/vendor/github.com/cri-o/cri-o/pkg/annotations/annotations.go new file mode 100644 index 000000000..151d93904 --- /dev/null +++ b/src/runtime/vendor/github.com/cri-o/cri-o/pkg/annotations/annotations.go @@ -0,0 +1,93 @@ +package annotations + +const ( + // Annotations carries the received Kubelet annotations + Annotations = "io.kubernetes.cri-o.Annotations" + + // ContainerID is the container ID annotation + ContainerID = "io.kubernetes.cri-o.ContainerID" + + // ContainerName is the container name annotation + ContainerName = "io.kubernetes.cri-o.ContainerName" + + // ContainerType is the container type (sandbox or container) annotation + ContainerType = "io.kubernetes.cri-o.ContainerType" + + // Created is the container creation time annotation + Created = "io.kubernetes.cri-o.Created" + + // HostName is the container host name annotation + HostName = "io.kubernetes.cri-o.HostName" + + // IP is the container ipv4 or ipv6 address + IP = "io.kubernetes.cri-o.IP" + + // Image is the container image ID annotation + Image = "io.kubernetes.cri-o.Image" + + // ImageName is the container image name annotation + ImageName = "io.kubernetes.cri-o.ImageName" + + // ImageRef is the container image ref annotation + ImageRef = "io.kubernetes.cri-o.ImageRef" + + // KubeName is the kubernetes name annotation + KubeName = "io.kubernetes.cri-o.KubeName" + + // Labels are the kubernetes labels annotation + Labels = "io.kubernetes.cri-o.Labels" + + // LogPath is the container logging path annotation + LogPath = "io.kubernetes.cri-o.LogPath" + + // Metadata is the container metadata annotation + Metadata = "io.kubernetes.cri-o.Metadata" + + // Name is the pod name annotation + Name = "io.kubernetes.cri-o.Name" + + // PrivilegedRuntime is the annotation for the privileged runtime path + PrivilegedRuntime = "io.kubernetes.cri-o.PrivilegedRuntime" + + // ResolvPath is the resolver configuration path annotation + ResolvPath = "io.kubernetes.cri-o.ResolvPath" + + // HostnamePath is the path to /etc/hostname to bind mount annotation + HostnamePath = "io.kubernetes.cri-o.HostnamePath" + + // SandboxID is the sandbox ID annotation + SandboxID = "io.kubernetes.cri-o.SandboxID" + + // SandboxName is the sandbox name annotation + SandboxName = "io.kubernetes.cri-o.SandboxName" + + // ShmPath is the shared memory path annotation + ShmPath = "io.kubernetes.cri-o.ShmPath" + + // MountPoint is the mount point of the container rootfs + MountPoint = "io.kubernetes.cri-o.MountPoint" + + // TrustedSandbox is the annotation for trusted sandboxes + TrustedSandbox = "io.kubernetes.cri-o.TrustedSandbox" + + // TTY is the terminal path annotation + TTY = "io.kubernetes.cri-o.TTY" + + // Stdin is the stdin annotation + Stdin = "io.kubernetes.cri-o.Stdin" + + // StdinOnce is the stdin_once annotation + StdinOnce = "io.kubernetes.cri-o.StdinOnce" + + // Volumes is the volumes annotatoin + Volumes = "io.kubernetes.cri-o.Volumes" +) + +// ContainerType values +const ( + // ContainerTypeSandbox represents a pod sandbox container + ContainerTypeSandbox = "sandbox" + + // ContainerTypeContainer represents a container running within a pod + ContainerTypeContainer = "container" +) diff --git a/src/runtime/vendor/github.com/davecgh/go-spew/LICENSE b/src/runtime/vendor/github.com/davecgh/go-spew/LICENSE new file mode 100644 index 000000000..bc52e96f2 --- /dev/null +++ b/src/runtime/vendor/github.com/davecgh/go-spew/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2012-2016 Dave Collins + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/src/runtime/vendor/github.com/davecgh/go-spew/spew/bypass.go b/src/runtime/vendor/github.com/davecgh/go-spew/spew/bypass.go new file mode 100644 index 000000000..792994785 --- /dev/null +++ b/src/runtime/vendor/github.com/davecgh/go-spew/spew/bypass.go @@ -0,0 +1,145 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is not running on Google App Engine, compiled by GopherJS, and +// "-tags safe" is not added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// Go versions prior to 1.4 are disabled because they use a different layout +// for interfaces which make the implementation of unsafeReflectValue more complex. +// +build !js,!appengine,!safe,!disableunsafe,go1.4 + +package spew + +import ( + "reflect" + "unsafe" +) + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = false + + // ptrSize is the size of a pointer on the current arch. + ptrSize = unsafe.Sizeof((*byte)(nil)) +) + +type flag uintptr + +var ( + // flagRO indicates whether the value field of a reflect.Value + // is read-only. + flagRO flag + + // flagAddr indicates whether the address of the reflect.Value's + // value may be taken. + flagAddr flag +) + +// flagKindMask holds the bits that make up the kind +// part of the flags field. In all the supported versions, +// it is in the lower 5 bits. +const flagKindMask = flag(0x1f) + +// Different versions of Go have used different +// bit layouts for the flags type. This table +// records the known combinations. +var okFlags = []struct { + ro, addr flag +}{{ + // From Go 1.4 to 1.5 + ro: 1 << 5, + addr: 1 << 7, +}, { + // Up to Go tip. + ro: 1<<5 | 1<<6, + addr: 1 << 8, +}} + +var flagValOffset = func() uintptr { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + return field.Offset +}() + +// flagField returns a pointer to the flag field of a reflect.Value. +func flagField(v *reflect.Value) *flag { + return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) +} + +// unsafeReflectValue converts the passed reflect.Value into a one that bypasses +// the typical safety restrictions preventing access to unaddressable and +// unexported data. It works by digging the raw pointer to the underlying +// value out of the protected value and generating a new unprotected (unsafe) +// reflect.Value to it. +// +// This allows us to check for implementations of the Stringer and error +// interfaces to be used for pretty printing ordinarily unaddressable and +// inaccessible values such as unexported struct fields. +func unsafeReflectValue(v reflect.Value) reflect.Value { + if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { + return v + } + flagFieldPtr := flagField(&v) + *flagFieldPtr &^= flagRO + *flagFieldPtr |= flagAddr + return v +} + +// Sanity checks against future reflect package changes +// to the type or semantics of the Value.flag field. +func init() { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { + panic("reflect.Value flag field has changed kind") + } + type t0 int + var t struct { + A t0 + // t0 will have flagEmbedRO set. + t0 + // a will have flagStickyRO set + a t0 + } + vA := reflect.ValueOf(t).FieldByName("A") + va := reflect.ValueOf(t).FieldByName("a") + vt0 := reflect.ValueOf(t).FieldByName("t0") + + // Infer flagRO from the difference between the flags + // for the (otherwise identical) fields in t. + flagPublic := *flagField(&vA) + flagWithRO := *flagField(&va) | *flagField(&vt0) + flagRO = flagPublic ^ flagWithRO + + // Infer flagAddr from the difference between a value + // taken from a pointer and not. + vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") + flagNoPtr := *flagField(&vA) + flagPtr := *flagField(&vPtrA) + flagAddr = flagNoPtr ^ flagPtr + + // Check that the inferred flags tally with one of the known versions. + for _, f := range okFlags { + if flagRO == f.ro && flagAddr == f.addr { + return + } + } + panic("reflect.Value read-only flag has changed semantics") +} diff --git a/src/runtime/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/src/runtime/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go new file mode 100644 index 000000000..205c28d68 --- /dev/null +++ b/src/runtime/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go @@ -0,0 +1,38 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is running on Google App Engine, compiled by GopherJS, or +// "-tags safe" is added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// +build js appengine safe disableunsafe !go1.4 + +package spew + +import "reflect" + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = true +) + +// unsafeReflectValue typically converts the passed reflect.Value into a one +// that bypasses the typical safety restrictions preventing access to +// unaddressable and unexported data. However, doing this relies on access to +// the unsafe package. This is a stub version which simply returns the passed +// reflect.Value when the unsafe package is not available. +func unsafeReflectValue(v reflect.Value) reflect.Value { + return v +} diff --git a/src/runtime/vendor/github.com/davecgh/go-spew/spew/common.go b/src/runtime/vendor/github.com/davecgh/go-spew/spew/common.go new file mode 100644 index 000000000..1be8ce945 --- /dev/null +++ b/src/runtime/vendor/github.com/davecgh/go-spew/spew/common.go @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "reflect" + "sort" + "strconv" +) + +// Some constants in the form of bytes to avoid string overhead. This mirrors +// the technique used in the fmt package. +var ( + panicBytes = []byte("(PANIC=") + plusBytes = []byte("+") + iBytes = []byte("i") + trueBytes = []byte("true") + falseBytes = []byte("false") + interfaceBytes = []byte("(interface {})") + commaNewlineBytes = []byte(",\n") + newlineBytes = []byte("\n") + openBraceBytes = []byte("{") + openBraceNewlineBytes = []byte("{\n") + closeBraceBytes = []byte("}") + asteriskBytes = []byte("*") + colonBytes = []byte(":") + colonSpaceBytes = []byte(": ") + openParenBytes = []byte("(") + closeParenBytes = []byte(")") + spaceBytes = []byte(" ") + pointerChainBytes = []byte("->") + nilAngleBytes = []byte("") + maxNewlineBytes = []byte("\n") + maxShortBytes = []byte("") + circularBytes = []byte("") + circularShortBytes = []byte("") + invalidAngleBytes = []byte("") + openBracketBytes = []byte("[") + closeBracketBytes = []byte("]") + percentBytes = []byte("%") + precisionBytes = []byte(".") + openAngleBytes = []byte("<") + closeAngleBytes = []byte(">") + openMapBytes = []byte("map[") + closeMapBytes = []byte("]") + lenEqualsBytes = []byte("len=") + capEqualsBytes = []byte("cap=") +) + +// hexDigits is used to map a decimal value to a hex digit. +var hexDigits = "0123456789abcdef" + +// catchPanic handles any panics that might occur during the handleMethods +// calls. +func catchPanic(w io.Writer, v reflect.Value) { + if err := recover(); err != nil { + w.Write(panicBytes) + fmt.Fprintf(w, "%v", err) + w.Write(closeParenBytes) + } +} + +// handleMethods attempts to call the Error and String methods on the underlying +// type the passed reflect.Value represents and outputes the result to Writer w. +// +// It handles panics in any called methods by catching and displaying the error +// as the formatted value. +func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { + // We need an interface to check if the type implements the error or + // Stringer interface. However, the reflect package won't give us an + // interface on certain things like unexported struct fields in order + // to enforce visibility rules. We use unsafe, when it's available, + // to bypass these restrictions since this package does not mutate the + // values. + if !v.CanInterface() { + if UnsafeDisabled { + return false + } + + v = unsafeReflectValue(v) + } + + // Choose whether or not to do error and Stringer interface lookups against + // the base type or a pointer to the base type depending on settings. + // Technically calling one of these methods with a pointer receiver can + // mutate the value, however, types which choose to satisify an error or + // Stringer interface with a pointer receiver should not be mutating their + // state inside these interface methods. + if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { + v = unsafeReflectValue(v) + } + if v.CanAddr() { + v = v.Addr() + } + + // Is it an error or Stringer? + switch iface := v.Interface().(type) { + case error: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.Error())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + + w.Write([]byte(iface.Error())) + return true + + case fmt.Stringer: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.String())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + w.Write([]byte(iface.String())) + return true + } + return false +} + +// printBool outputs a boolean value as true or false to Writer w. +func printBool(w io.Writer, val bool) { + if val { + w.Write(trueBytes) + } else { + w.Write(falseBytes) + } +} + +// printInt outputs a signed integer value to Writer w. +func printInt(w io.Writer, val int64, base int) { + w.Write([]byte(strconv.FormatInt(val, base))) +} + +// printUint outputs an unsigned integer value to Writer w. +func printUint(w io.Writer, val uint64, base int) { + w.Write([]byte(strconv.FormatUint(val, base))) +} + +// printFloat outputs a floating point value using the specified precision, +// which is expected to be 32 or 64bit, to Writer w. +func printFloat(w io.Writer, val float64, precision int) { + w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) +} + +// printComplex outputs a complex value using the specified float precision +// for the real and imaginary parts to Writer w. +func printComplex(w io.Writer, c complex128, floatPrecision int) { + r := real(c) + w.Write(openParenBytes) + w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) + i := imag(c) + if i >= 0 { + w.Write(plusBytes) + } + w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) + w.Write(iBytes) + w.Write(closeParenBytes) +} + +// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' +// prefix to Writer w. +func printHexPtr(w io.Writer, p uintptr) { + // Null pointer. + num := uint64(p) + if num == 0 { + w.Write(nilAngleBytes) + return + } + + // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix + buf := make([]byte, 18) + + // It's simpler to construct the hex string right to left. + base := uint64(16) + i := len(buf) - 1 + for num >= base { + buf[i] = hexDigits[num%base] + num /= base + i-- + } + buf[i] = hexDigits[num] + + // Add '0x' prefix. + i-- + buf[i] = 'x' + i-- + buf[i] = '0' + + // Strip unused leading bytes. + buf = buf[i:] + w.Write(buf) +} + +// valuesSorter implements sort.Interface to allow a slice of reflect.Value +// elements to be sorted. +type valuesSorter struct { + values []reflect.Value + strings []string // either nil or same len and values + cs *ConfigState +} + +// newValuesSorter initializes a valuesSorter instance, which holds a set of +// surrogate keys on which the data should be sorted. It uses flags in +// ConfigState to decide if and how to populate those surrogate keys. +func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { + vs := &valuesSorter{values: values, cs: cs} + if canSortSimply(vs.values[0].Kind()) { + return vs + } + if !cs.DisableMethods { + vs.strings = make([]string, len(values)) + for i := range vs.values { + b := bytes.Buffer{} + if !handleMethods(cs, &b, vs.values[i]) { + vs.strings = nil + break + } + vs.strings[i] = b.String() + } + } + if vs.strings == nil && cs.SpewKeys { + vs.strings = make([]string, len(values)) + for i := range vs.values { + vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) + } + } + return vs +} + +// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted +// directly, or whether it should be considered for sorting by surrogate keys +// (if the ConfigState allows it). +func canSortSimply(kind reflect.Kind) bool { + // This switch parallels valueSortLess, except for the default case. + switch kind { + case reflect.Bool: + return true + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return true + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + case reflect.Uintptr: + return true + case reflect.Array: + return true + } + return false +} + +// Len returns the number of values in the slice. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Len() int { + return len(s.values) +} + +// Swap swaps the values at the passed indices. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Swap(i, j int) { + s.values[i], s.values[j] = s.values[j], s.values[i] + if s.strings != nil { + s.strings[i], s.strings[j] = s.strings[j], s.strings[i] + } +} + +// valueSortLess returns whether the first value should sort before the second +// value. It is used by valueSorter.Less as part of the sort.Interface +// implementation. +func valueSortLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Bool: + return !a.Bool() && b.Bool() + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return a.Int() < b.Int() + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return a.Uint() < b.Uint() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.String: + return a.String() < b.String() + case reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Array: + // Compare the contents of both arrays. + l := a.Len() + for i := 0; i < l; i++ { + av := a.Index(i) + bv := b.Index(i) + if av.Interface() == bv.Interface() { + continue + } + return valueSortLess(av, bv) + } + } + return a.String() < b.String() +} + +// Less returns whether the value at index i should sort before the +// value at index j. It is part of the sort.Interface implementation. +func (s *valuesSorter) Less(i, j int) bool { + if s.strings == nil { + return valueSortLess(s.values[i], s.values[j]) + } + return s.strings[i] < s.strings[j] +} + +// sortValues is a sort function that handles both native types and any type that +// can be converted to error or Stringer. Other inputs are sorted according to +// their Value.String() value to ensure display stability. +func sortValues(values []reflect.Value, cs *ConfigState) { + if len(values) == 0 { + return + } + sort.Sort(newValuesSorter(values, cs)) +} diff --git a/src/runtime/vendor/github.com/davecgh/go-spew/spew/config.go b/src/runtime/vendor/github.com/davecgh/go-spew/spew/config.go new file mode 100644 index 000000000..2e3d22f31 --- /dev/null +++ b/src/runtime/vendor/github.com/davecgh/go-spew/spew/config.go @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "os" +) + +// ConfigState houses the configuration options used by spew to format and +// display values. There is a global instance, Config, that is used to control +// all top-level Formatter and Dump functionality. Each ConfigState instance +// provides methods equivalent to the top-level functions. +// +// The zero value for ConfigState provides no indentation. You would typically +// want to set it to a space or a tab. +// +// Alternatively, you can use NewDefaultConfig to get a ConfigState instance +// with default settings. See the documentation of NewDefaultConfig for default +// values. +type ConfigState struct { + // Indent specifies the string to use for each indentation level. The + // global config instance that all top-level functions use set this to a + // single space by default. If you would like more indentation, you might + // set this to a tab with "\t" or perhaps two spaces with " ". + Indent string + + // MaxDepth controls the maximum number of levels to descend into nested + // data structures. The default, 0, means there is no limit. + // + // NOTE: Circular data structures are properly detected, so it is not + // necessary to set this value unless you specifically want to limit deeply + // nested data structures. + MaxDepth int + + // DisableMethods specifies whether or not error and Stringer interfaces are + // invoked for types that implement them. + DisableMethods bool + + // DisablePointerMethods specifies whether or not to check for and invoke + // error and Stringer interfaces on types which only accept a pointer + // receiver when the current type is not a pointer. + // + // NOTE: This might be an unsafe action since calling one of these methods + // with a pointer receiver could technically mutate the value, however, + // in practice, types which choose to satisify an error or Stringer + // interface with a pointer receiver should not be mutating their state + // inside these interface methods. As a result, this option relies on + // access to the unsafe package, so it will not have any effect when + // running in environments without access to the unsafe package such as + // Google App Engine or with the "safe" build tag specified. + DisablePointerMethods bool + + // DisablePointerAddresses specifies whether to disable the printing of + // pointer addresses. This is useful when diffing data structures in tests. + DisablePointerAddresses bool + + // DisableCapacities specifies whether to disable the printing of capacities + // for arrays, slices, maps and channels. This is useful when diffing + // data structures in tests. + DisableCapacities bool + + // ContinueOnMethod specifies whether or not recursion should continue once + // a custom error or Stringer interface is invoked. The default, false, + // means it will print the results of invoking the custom error or Stringer + // interface and return immediately instead of continuing to recurse into + // the internals of the data type. + // + // NOTE: This flag does not have any effect if method invocation is disabled + // via the DisableMethods or DisablePointerMethods options. + ContinueOnMethod bool + + // SortKeys specifies map keys should be sorted before being printed. Use + // this to have a more deterministic, diffable output. Note that only + // native types (bool, int, uint, floats, uintptr and string) and types + // that support the error or Stringer interfaces (if methods are + // enabled) are supported, with other types sorted according to the + // reflect.Value.String() output which guarantees display stability. + SortKeys bool + + // SpewKeys specifies that, as a last resort attempt, map keys should + // be spewed to strings and sorted by those strings. This is only + // considered if SortKeys is true. + SpewKeys bool +} + +// Config is the active configuration of the top-level functions. +// The configuration can be changed by modifying the contents of spew.Config. +var Config = ConfigState{Indent: " "} + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the formatted string as a value that satisfies error. See NewFormatter +// for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, c.convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, c.convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, c.convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a Formatter interface returned by c.NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, c.convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Print(a ...interface{}) (n int, err error) { + return fmt.Print(c.convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, c.convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Println(a ...interface{}) (n int, err error) { + return fmt.Println(c.convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprint(a ...interface{}) string { + return fmt.Sprint(c.convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, c.convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a Formatter interface returned by c.NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintln(a ...interface{}) string { + return fmt.Sprintln(c.convertArgs(a)...) +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +c.Printf, c.Println, or c.Printf. +*/ +func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(c, v) +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { + fdump(c, w, a...) +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by modifying the public members +of c. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func (c *ConfigState) Dump(a ...interface{}) { + fdump(c, os.Stdout, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func (c *ConfigState) Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(c, &buf, a...) + return buf.String() +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a spew Formatter interface using +// the ConfigState associated with s. +func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = newFormatter(c, arg) + } + return formatters +} + +// NewDefaultConfig returns a ConfigState with the following default settings. +// +// Indent: " " +// MaxDepth: 0 +// DisableMethods: false +// DisablePointerMethods: false +// ContinueOnMethod: false +// SortKeys: false +func NewDefaultConfig() *ConfigState { + return &ConfigState{Indent: " "} +} diff --git a/src/runtime/vendor/github.com/davecgh/go-spew/spew/doc.go b/src/runtime/vendor/github.com/davecgh/go-spew/spew/doc.go new file mode 100644 index 000000000..aacaac6f1 --- /dev/null +++ b/src/runtime/vendor/github.com/davecgh/go-spew/spew/doc.go @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +Package spew implements a deep pretty printer for Go data structures to aid in +debugging. + +A quick overview of the additional features spew provides over the built-in +printing facilities for Go data types are as follows: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output (only when using + Dump style) + +There are two different approaches spew allows for dumping Go data structures: + + * Dump style which prints with newlines, customizable indentation, + and additional debug information such as types and all pointer addresses + used to indirect to the final value + * A custom Formatter interface that integrates cleanly with the standard fmt + package and replaces %v, %+v, %#v, and %#+v to provide inline printing + similar to the default %v while providing the additional functionality + outlined above and passing unsupported format verbs such as %x and %q + along to fmt + +Quick Start + +This section demonstrates how to quickly get started with spew. See the +sections below for further details on formatting and configuration options. + +To dump a variable with full newlines, indentation, type, and pointer +information use Dump, Fdump, or Sdump: + spew.Dump(myVar1, myVar2, ...) + spew.Fdump(someWriter, myVar1, myVar2, ...) + str := spew.Sdump(myVar1, myVar2, ...) + +Alternatively, if you would prefer to use format strings with a compacted inline +printing style, use the convenience wrappers Printf, Fprintf, etc with +%v (most compact), %+v (adds pointer addresses), %#v (adds types), or +%#+v (adds types and pointer addresses): + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +Configuration Options + +Configuration of spew is handled by fields in the ConfigState type. For +convenience, all of the top-level functions use a global state available +via the spew.Config global. + +It is also possible to create a ConfigState instance that provides methods +equivalent to the top-level functions. This allows concurrent configuration +options. See the ConfigState documentation for more details. + +The following configuration options are available: + * Indent + String to use for each indentation level for Dump functions. + It is a single space by default. A popular alternative is "\t". + + * MaxDepth + Maximum number of levels to descend into nested data structures. + There is no limit by default. + + * DisableMethods + Disables invocation of error and Stringer interface methods. + Method invocation is enabled by default. + + * DisablePointerMethods + Disables invocation of error and Stringer interface methods on types + which only accept pointer receivers from non-pointer variables. + Pointer method invocation is enabled by default. + + * DisablePointerAddresses + DisablePointerAddresses specifies whether to disable the printing of + pointer addresses. This is useful when diffing data structures in tests. + + * DisableCapacities + DisableCapacities specifies whether to disable the printing of + capacities for arrays, slices, maps and channels. This is useful when + diffing data structures in tests. + + * ContinueOnMethod + Enables recursion into types after invoking error and Stringer interface + methods. Recursion after method invocation is disabled by default. + + * SortKeys + Specifies map keys should be sorted before being printed. Use + this to have a more deterministic, diffable output. Note that + only native types (bool, int, uint, floats, uintptr and string) + and types which implement error or Stringer interfaces are + supported with other types sorted according to the + reflect.Value.String() output which guarantees display + stability. Natural map order is used by default. + + * SpewKeys + Specifies that, as a last resort attempt, map keys should be + spewed to strings and sorted by those strings. This is only + considered if SortKeys is true. + +Dump Usage + +Simply call spew.Dump with a list of variables you want to dump: + + spew.Dump(myVar1, myVar2, ...) + +You may also call spew.Fdump if you would prefer to output to an arbitrary +io.Writer. For example, to dump to standard error: + + spew.Fdump(os.Stderr, myVar1, myVar2, ...) + +A third option is to call spew.Sdump to get the formatted output as a string: + + str := spew.Sdump(myVar1, myVar2, ...) + +Sample Dump Output + +See the Dump example for details on the setup of the types and variables being +shown here. + + (main.Foo) { + unexportedField: (*main.Bar)(0xf84002e210)({ + flag: (main.Flag) flagTwo, + data: (uintptr) + }), + ExportedField: (map[interface {}]interface {}) (len=1) { + (string) (len=3) "one": (bool) true + } + } + +Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C +command as shown. + ([]uint8) (len=32 cap=32) { + 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | + 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| + 00000020 31 32 |12| + } + +Custom Formatter + +Spew provides a custom formatter that implements the fmt.Formatter interface +so that it integrates cleanly with standard fmt package printing functions. The +formatter is useful for inline printing of smaller data types similar to the +standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Custom Formatter Usage + +The simplest way to make use of the spew custom formatter is to call one of the +convenience functions such as spew.Printf, spew.Println, or spew.Printf. The +functions have syntax you are most likely already familiar with: + + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Println(myVar, myVar2) + spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +See the Index for the full list convenience functions. + +Sample Formatter Output + +Double pointer to a uint8: + %v: <**>5 + %+v: <**>(0xf8400420d0->0xf8400420c8)5 + %#v: (**uint8)5 + %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 + +Pointer to circular struct with a uint8 field and a pointer to itself: + %v: <*>{1 <*>} + %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} + %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} + %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} + +See the Printf example for details on the setup of variables being shown +here. + +Errors + +Since it is possible for custom Stringer/error interfaces to panic, spew +detects them and handles them internally by printing the panic information +inline with the output. Since spew is intended to provide deep pretty printing +capabilities on structures, it intentionally does not return any errors. +*/ +package spew diff --git a/src/runtime/vendor/github.com/davecgh/go-spew/spew/dump.go b/src/runtime/vendor/github.com/davecgh/go-spew/spew/dump.go new file mode 100644 index 000000000..f78d89fc1 --- /dev/null +++ b/src/runtime/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "os" + "reflect" + "regexp" + "strconv" + "strings" +) + +var ( + // uint8Type is a reflect.Type representing a uint8. It is used to + // convert cgo types to uint8 slices for hexdumping. + uint8Type = reflect.TypeOf(uint8(0)) + + // cCharRE is a regular expression that matches a cgo char. + // It is used to detect character arrays to hexdump them. + cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) + + // cUnsignedCharRE is a regular expression that matches a cgo unsigned + // char. It is used to detect unsigned character arrays to hexdump + // them. + cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) + + // cUint8tCharRE is a regular expression that matches a cgo uint8_t. + // It is used to detect uint8_t arrays to hexdump them. + cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) +) + +// dumpState contains information about the state of a dump operation. +type dumpState struct { + w io.Writer + depth int + pointers map[uintptr]int + ignoreNextType bool + ignoreNextIndent bool + cs *ConfigState +} + +// indent performs indentation according to the depth level and cs.Indent +// option. +func (d *dumpState) indent() { + if d.ignoreNextIndent { + d.ignoreNextIndent = false + return + } + d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) +} + +// unpackValue returns values inside of non-nil interfaces when possible. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface && !v.IsNil() { + v = v.Elem() + } + return v +} + +// dumpPtr handles formatting of pointers by indirecting them as necessary. +func (d *dumpState) dumpPtr(v reflect.Value) { + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range d.pointers { + if depth >= d.depth { + delete(d.pointers, k) + } + } + + // Keep list of all dereferenced pointers to show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by dereferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := d.pointers[addr]; ok && pd < d.depth { + cycleFound = true + indirects-- + break + } + d.pointers[addr] = d.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type information. + d.w.Write(openParenBytes) + d.w.Write(bytes.Repeat(asteriskBytes, indirects)) + d.w.Write([]byte(ve.Type().String())) + d.w.Write(closeParenBytes) + + // Display pointer information. + if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { + d.w.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + d.w.Write(pointerChainBytes) + } + printHexPtr(d.w, addr) + } + d.w.Write(closeParenBytes) + } + + // Display dereferenced value. + d.w.Write(openParenBytes) + switch { + case nilFound: + d.w.Write(nilAngleBytes) + + case cycleFound: + d.w.Write(circularBytes) + + default: + d.ignoreNextType = true + d.dump(ve) + } + d.w.Write(closeParenBytes) +} + +// dumpSlice handles formatting of arrays and slices. Byte (uint8 under +// reflection) arrays and slices are dumped in hexdump -C fashion. +func (d *dumpState) dumpSlice(v reflect.Value) { + // Determine whether this type should be hex dumped or not. Also, + // for types which should be hexdumped, try to use the underlying data + // first, then fall back to trying to convert them to a uint8 slice. + var buf []uint8 + doConvert := false + doHexDump := false + numEntries := v.Len() + if numEntries > 0 { + vt := v.Index(0).Type() + vts := vt.String() + switch { + // C types that need to be converted. + case cCharRE.MatchString(vts): + fallthrough + case cUnsignedCharRE.MatchString(vts): + fallthrough + case cUint8tCharRE.MatchString(vts): + doConvert = true + + // Try to use existing uint8 slices and fall back to converting + // and copying if that fails. + case vt.Kind() == reflect.Uint8: + // We need an addressable interface to convert the type + // to a byte slice. However, the reflect package won't + // give us an interface on certain things like + // unexported struct fields in order to enforce + // visibility rules. We use unsafe, when available, to + // bypass these restrictions since this package does not + // mutate the values. + vs := v + if !vs.CanInterface() || !vs.CanAddr() { + vs = unsafeReflectValue(vs) + } + if !UnsafeDisabled { + vs = vs.Slice(0, numEntries) + + // Use the existing uint8 slice if it can be + // type asserted. + iface := vs.Interface() + if slice, ok := iface.([]uint8); ok { + buf = slice + doHexDump = true + break + } + } + + // The underlying data needs to be converted if it can't + // be type asserted to a uint8 slice. + doConvert = true + } + + // Copy and convert the underlying type if needed. + if doConvert && vt.ConvertibleTo(uint8Type) { + // Convert and copy each element into a uint8 byte + // slice. + buf = make([]uint8, numEntries) + for i := 0; i < numEntries; i++ { + vv := v.Index(i) + buf[i] = uint8(vv.Convert(uint8Type).Uint()) + } + doHexDump = true + } + } + + // Hexdump the entire slice as needed. + if doHexDump { + indent := strings.Repeat(d.cs.Indent, d.depth) + str := indent + hex.Dump(buf) + str = strings.Replace(str, "\n", "\n"+indent, -1) + str = strings.TrimRight(str, d.cs.Indent) + d.w.Write([]byte(str)) + return + } + + // Recursively call dump for each item. + for i := 0; i < numEntries; i++ { + d.dump(d.unpackValue(v.Index(i))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } +} + +// dump is the main workhorse for dumping a value. It uses the passed reflect +// value to figure out what kind of object we are dealing with and formats it +// appropriately. It is a recursive function, however circular data structures +// are detected and handled properly. +func (d *dumpState) dump(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + d.w.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + d.indent() + d.dumpPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !d.ignoreNextType { + d.indent() + d.w.Write(openParenBytes) + d.w.Write([]byte(v.Type().String())) + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + d.ignoreNextType = false + + // Display length and capacity if the built-in len and cap functions + // work with the value's kind and the len/cap itself is non-zero. + valueLen, valueCap := 0, 0 + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.Chan: + valueLen, valueCap = v.Len(), v.Cap() + case reflect.Map, reflect.String: + valueLen = v.Len() + } + if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { + d.w.Write(openParenBytes) + if valueLen != 0 { + d.w.Write(lenEqualsBytes) + printInt(d.w, int64(valueLen), 10) + } + if !d.cs.DisableCapacities && valueCap != 0 { + if valueLen != 0 { + d.w.Write(spaceBytes) + } + d.w.Write(capEqualsBytes) + printInt(d.w, int64(valueCap), 10) + } + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + + // Call Stringer/error interfaces if they exist and the handle methods flag + // is enabled + if !d.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(d.cs, d.w, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(d.w, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(d.w, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(d.w, v.Uint(), 10) + + case reflect.Float32: + printFloat(d.w, v.Float(), 32) + + case reflect.Float64: + printFloat(d.w, v.Float(), 64) + + case reflect.Complex64: + printComplex(d.w, v.Complex(), 32) + + case reflect.Complex128: + printComplex(d.w, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + d.dumpSlice(v) + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.String: + d.w.Write([]byte(strconv.Quote(v.String()))) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + d.w.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + numEntries := v.Len() + keys := v.MapKeys() + if d.cs.SortKeys { + sortValues(keys, d.cs) + } + for i, key := range keys { + d.dump(d.unpackValue(key)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.MapIndex(key))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Struct: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + vt := v.Type() + numFields := v.NumField() + for i := 0; i < numFields; i++ { + d.indent() + vtf := vt.Field(i) + d.w.Write([]byte(vtf.Name)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.Field(i))) + if i < (numFields - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(d.w, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(d.w, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it in case any new + // types are added. + default: + if v.CanInterface() { + fmt.Fprintf(d.w, "%v", v.Interface()) + } else { + fmt.Fprintf(d.w, "%v", v.String()) + } + } +} + +// fdump is a helper function to consolidate the logic from the various public +// methods which take varying writers and config states. +func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { + for _, arg := range a { + if arg == nil { + w.Write(interfaceBytes) + w.Write(spaceBytes) + w.Write(nilAngleBytes) + w.Write(newlineBytes) + continue + } + + d := dumpState{w: w, cs: cs} + d.pointers = make(map[uintptr]int) + d.dump(reflect.ValueOf(arg)) + d.w.Write(newlineBytes) + } +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func Fdump(w io.Writer, a ...interface{}) { + fdump(&Config, w, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(&Config, &buf, a...) + return buf.String() +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by an exported package global, +spew.Config. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func Dump(a ...interface{}) { + fdump(&Config, os.Stdout, a...) +} diff --git a/src/runtime/vendor/github.com/davecgh/go-spew/spew/format.go b/src/runtime/vendor/github.com/davecgh/go-spew/spew/format.go new file mode 100644 index 000000000..b04edb7d7 --- /dev/null +++ b/src/runtime/vendor/github.com/davecgh/go-spew/spew/format.go @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" +) + +// supportedFlags is a list of all the character flags supported by fmt package. +const supportedFlags = "0-+# " + +// formatState implements the fmt.Formatter interface and contains information +// about the state of a formatting operation. The NewFormatter function can +// be used to get a new Formatter which can be used directly as arguments +// in standard fmt package printing calls. +type formatState struct { + value interface{} + fs fmt.State + depth int + pointers map[uintptr]int + ignoreNextType bool + cs *ConfigState +} + +// buildDefaultFormat recreates the original format string without precision +// and width information to pass in to fmt.Sprintf in the case of an +// unrecognized type. Unless new types are added to the language, this +// function won't ever be called. +func (f *formatState) buildDefaultFormat() (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + buf.WriteRune('v') + + format = buf.String() + return format +} + +// constructOrigFormat recreates the original format string including precision +// and width information to pass along to the standard fmt package. This allows +// automatic deferral of all format strings this package doesn't support. +func (f *formatState) constructOrigFormat(verb rune) (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + if width, ok := f.fs.Width(); ok { + buf.WriteString(strconv.Itoa(width)) + } + + if precision, ok := f.fs.Precision(); ok { + buf.Write(precisionBytes) + buf.WriteString(strconv.Itoa(precision)) + } + + buf.WriteRune(verb) + + format = buf.String() + return format +} + +// unpackValue returns values inside of non-nil interfaces when possible and +// ensures that types for values which have been unpacked from an interface +// are displayed when the show types flag is also set. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (f *formatState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface { + f.ignoreNextType = false + if !v.IsNil() { + v = v.Elem() + } + } + return v +} + +// formatPtr handles formatting of pointers by indirecting them as necessary. +func (f *formatState) formatPtr(v reflect.Value) { + // Display nil if top level pointer is nil. + showTypes := f.fs.Flag('#') + if v.IsNil() && (!showTypes || f.ignoreNextType) { + f.fs.Write(nilAngleBytes) + return + } + + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range f.pointers { + if depth >= f.depth { + delete(f.pointers, k) + } + } + + // Keep list of all dereferenced pointers to possibly show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by derferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := f.pointers[addr]; ok && pd < f.depth { + cycleFound = true + indirects-- + break + } + f.pointers[addr] = f.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type or indirection level depending on flags. + if showTypes && !f.ignoreNextType { + f.fs.Write(openParenBytes) + f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) + f.fs.Write([]byte(ve.Type().String())) + f.fs.Write(closeParenBytes) + } else { + if nilFound || cycleFound { + indirects += strings.Count(ve.Type().String(), "*") + } + f.fs.Write(openAngleBytes) + f.fs.Write([]byte(strings.Repeat("*", indirects))) + f.fs.Write(closeAngleBytes) + } + + // Display pointer information depending on flags. + if f.fs.Flag('+') && (len(pointerChain) > 0) { + f.fs.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + f.fs.Write(pointerChainBytes) + } + printHexPtr(f.fs, addr) + } + f.fs.Write(closeParenBytes) + } + + // Display dereferenced value. + switch { + case nilFound: + f.fs.Write(nilAngleBytes) + + case cycleFound: + f.fs.Write(circularShortBytes) + + default: + f.ignoreNextType = true + f.format(ve) + } +} + +// format is the main workhorse for providing the Formatter interface. It +// uses the passed reflect value to figure out what kind of object we are +// dealing with and formats it appropriately. It is a recursive function, +// however circular data structures are detected and handled properly. +func (f *formatState) format(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + f.fs.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + f.formatPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !f.ignoreNextType && f.fs.Flag('#') { + f.fs.Write(openParenBytes) + f.fs.Write([]byte(v.Type().String())) + f.fs.Write(closeParenBytes) + } + f.ignoreNextType = false + + // Call Stringer/error interfaces if they exist and the handle methods + // flag is enabled. + if !f.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(f.cs, f.fs, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(f.fs, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(f.fs, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(f.fs, v.Uint(), 10) + + case reflect.Float32: + printFloat(f.fs, v.Float(), 32) + + case reflect.Float64: + printFloat(f.fs, v.Float(), 64) + + case reflect.Complex64: + printComplex(f.fs, v.Complex(), 32) + + case reflect.Complex128: + printComplex(f.fs, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + f.fs.Write(openBracketBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + numEntries := v.Len() + for i := 0; i < numEntries; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(v.Index(i))) + } + } + f.depth-- + f.fs.Write(closeBracketBytes) + + case reflect.String: + f.fs.Write([]byte(v.String())) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + f.fs.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + + f.fs.Write(openMapBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + keys := v.MapKeys() + if f.cs.SortKeys { + sortValues(keys, f.cs) + } + for i, key := range keys { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(key)) + f.fs.Write(colonBytes) + f.ignoreNextType = true + f.format(f.unpackValue(v.MapIndex(key))) + } + } + f.depth-- + f.fs.Write(closeMapBytes) + + case reflect.Struct: + numFields := v.NumField() + f.fs.Write(openBraceBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + vt := v.Type() + for i := 0; i < numFields; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + vtf := vt.Field(i) + if f.fs.Flag('+') || f.fs.Flag('#') { + f.fs.Write([]byte(vtf.Name)) + f.fs.Write(colonBytes) + } + f.format(f.unpackValue(v.Field(i))) + } + } + f.depth-- + f.fs.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(f.fs, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(f.fs, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it if any get added. + default: + format := f.buildDefaultFormat() + if v.CanInterface() { + fmt.Fprintf(f.fs, format, v.Interface()) + } else { + fmt.Fprintf(f.fs, format, v.String()) + } + } +} + +// Format satisfies the fmt.Formatter interface. See NewFormatter for usage +// details. +func (f *formatState) Format(fs fmt.State, verb rune) { + f.fs = fs + + // Use standard formatting for verbs that are not v. + if verb != 'v' { + format := f.constructOrigFormat(verb) + fmt.Fprintf(fs, format, f.value) + return + } + + if f.value == nil { + if fs.Flag('#') { + fs.Write(interfaceBytes) + } + fs.Write(nilAngleBytes) + return + } + + f.format(reflect.ValueOf(f.value)) +} + +// newFormatter is a helper function to consolidate the logic from the various +// public methods which take varying config states. +func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { + fs := &formatState{value: v, cs: cs} + fs.pointers = make(map[uintptr]int) + return fs +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +Printf, Println, or Fprintf. +*/ +func NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(&Config, v) +} diff --git a/src/runtime/vendor/github.com/davecgh/go-spew/spew/spew.go b/src/runtime/vendor/github.com/davecgh/go-spew/spew/spew.go new file mode 100644 index 000000000..32c0e3388 --- /dev/null +++ b/src/runtime/vendor/github.com/davecgh/go-spew/spew/spew.go @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "fmt" + "io" +) + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the formatted string as a value that satisfies error. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a default Formatter interface returned by NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) +func Print(a ...interface{}) (n int, err error) { + return fmt.Print(convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) +func Println(a ...interface{}) (n int, err error) { + return fmt.Println(convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprint(a ...interface{}) string { + return fmt.Sprint(convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintln(a ...interface{}) string { + return fmt.Sprintln(convertArgs(a)...) +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a default spew Formatter interface. +func convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = NewFormatter(arg) + } + return formatters +} diff --git a/src/runtime/vendor/github.com/dlespiau/covertool/LICENSE b/src/runtime/vendor/github.com/dlespiau/covertool/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/src/runtime/vendor/github.com/dlespiau/covertool/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/src/runtime/vendor/github.com/dlespiau/covertool/pkg/cover/cover.go b/src/runtime/vendor/github.com/dlespiau/covertool/pkg/cover/cover.go new file mode 100644 index 000000000..cc712184f --- /dev/null +++ b/src/runtime/vendor/github.com/dlespiau/covertool/pkg/cover/cover.go @@ -0,0 +1,82 @@ +// Copyright (c) 2017 Intel Corporation +// +// 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 cover + +import ( + "flag" + "io" + "os" + "strings" + "testing" +) + +// ParseAndStriptestFlags runs flag.Parse to parse the standard flags of a test +// binary and remove them from os.Args. +func ParseAndStripTestFlags() { + // Parse the command line using the stdlib flag package so the flags defined + // in the testing package get populated. + flag.Parse() + + // Strip command line arguments that were for the testing package and are now + // handled. + var runtimeArgs []string + for _, arg := range os.Args { + if strings.HasPrefix(arg, "-test.") || + strings.HasPrefix(arg, "-httptest.") { + continue + } + runtimeArgs = append(runtimeArgs, arg) + } + os.Args = runtimeArgs +} + +type dummyTestDeps func(pat, str string) (bool, error) + +func (d dummyTestDeps) MatchString(pat, str string) (bool, error) { return false, nil } +func (d dummyTestDeps) StartCPUProfile(io.Writer) error { return nil } +func (d dummyTestDeps) StopCPUProfile() {} +func (f dummyTestDeps) StartTestLog(w io.Writer) {} +func (f dummyTestDeps) StopTestLog() error { return nil } +func (d dummyTestDeps) WriteHeapProfile(io.Writer) error { return nil } +func (d dummyTestDeps) WriteProfileTo(string, io.Writer, int) error { return nil } +func (f dummyTestDeps) ImportPath() string { return "" } + +// FlushProfiles flushes test profiles to disk. It works by build and executing +// a dummy list of 1 test. This is to ensure we execute the M.after() function +// (a function internal to the testing package) where all reporting (cpu, mem, +// cover, ... profiles) is flushed to disk. +func FlushProfiles() { + // Redirect Stdout/err temporarily so the testing code doesn't output the + // regular: + // PASS + // coverage: 21.4% of statements + // Thanks to this, we can test the output of the instrumented binary the same + // way the normal binary is. + oldstdout := os.Stdout + oldstderr := os.Stderr + os.Stdout, _ = os.Open(os.DevNull) + os.Stderr, _ = os.Open(os.DevNull) + + tests := []testing.InternalTest{} + benchmarks := []testing.InternalBenchmark{} + examples := []testing.InternalExample{} + var f dummyTestDeps + dummyM := testing.MainStart(f, tests, benchmarks, examples) + dummyM.Run() + + // restore stdout/err + os.Stdout = oldstdout + os.Stderr = oldstderr +} diff --git a/src/runtime/vendor/github.com/docker/go-units/CONTRIBUTING.md b/src/runtime/vendor/github.com/docker/go-units/CONTRIBUTING.md new file mode 100644 index 000000000..9ea86d784 --- /dev/null +++ b/src/runtime/vendor/github.com/docker/go-units/CONTRIBUTING.md @@ -0,0 +1,67 @@ +# Contributing to go-units + +Want to hack on go-units? Awesome! Here are instructions to get you started. + +go-units is a part of the [Docker](https://www.docker.com) project, and follows +the same rules and principles. If you're already familiar with the way +Docker does things, you'll feel right at home. + +Otherwise, go read Docker's +[contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), +[issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md), +[review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and +[branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md). + +### Sign your work + +The sign-off is a simple line at the end of the explanation for the patch. Your +signature certifies that you wrote the patch or otherwise have the right to pass +it on as an open-source patch. The rules are pretty simple: if you can certify +the below (from [developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +Then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +Use your real name (sorry, no pseudonyms or anonymous contributions.) + +If you set your `user.name` and `user.email` git configs, you can sign your +commit automatically with `git commit -s`. diff --git a/src/runtime/vendor/github.com/docker/go-units/LICENSE b/src/runtime/vendor/github.com/docker/go-units/LICENSE new file mode 100644 index 000000000..b55b37bc3 --- /dev/null +++ b/src/runtime/vendor/github.com/docker/go-units/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015 Docker, Inc. + + 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 + + https://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. diff --git a/src/runtime/vendor/github.com/docker/go-units/MAINTAINERS b/src/runtime/vendor/github.com/docker/go-units/MAINTAINERS new file mode 100644 index 000000000..9b3b6b101 --- /dev/null +++ b/src/runtime/vendor/github.com/docker/go-units/MAINTAINERS @@ -0,0 +1,46 @@ +# go-units maintainers file +# +# This file describes who runs the docker/go-units project and how. +# This is a living document - if you see something out of date or missing, speak up! +# +# It is structured to be consumable by both humans and programs. +# To extract its contents programmatically, use any TOML-compliant parser. +# +# This file is compiled into the MAINTAINERS file in docker/opensource. +# +[Org] + [Org."Core maintainers"] + people = [ + "akihirosuda", + "dnephin", + "thajeztah", + "vdemeester", + ] + +[people] + +# A reference list of all people associated with the project. +# All other sections should refer to people by their canonical key +# in the people section. + + # ADD YOURSELF HERE IN ALPHABETICAL ORDER + + [people.akihirosuda] + Name = "Akihiro Suda" + Email = "suda.akihiro@lab.ntt.co.jp" + GitHub = "AkihiroSuda" + + [people.dnephin] + Name = "Daniel Nephin" + Email = "dnephin@gmail.com" + GitHub = "dnephin" + + [people.thajeztah] + Name = "Sebastiaan van Stijn" + Email = "github@gone.nl" + GitHub = "thaJeztah" + + [people.vdemeester] + Name = "Vincent Demeester" + Email = "vincent@sbr.pm" + GitHub = "vdemeester" \ No newline at end of file diff --git a/src/runtime/vendor/github.com/docker/go-units/README.md b/src/runtime/vendor/github.com/docker/go-units/README.md new file mode 100644 index 000000000..4f70a4e13 --- /dev/null +++ b/src/runtime/vendor/github.com/docker/go-units/README.md @@ -0,0 +1,16 @@ +[![GoDoc](https://godoc.org/github.com/docker/go-units?status.svg)](https://godoc.org/github.com/docker/go-units) + +# Introduction + +go-units is a library to transform human friendly measurements into machine friendly values. + +## Usage + +See the [docs in godoc](https://godoc.org/github.com/docker/go-units) for examples and documentation. + +## Copyright and license + +Copyright © 2015 Docker, Inc. + +go-units is licensed under the Apache License, Version 2.0. +See [LICENSE](LICENSE) for the full text of the license. diff --git a/src/runtime/vendor/github.com/docker/go-units/circle.yml b/src/runtime/vendor/github.com/docker/go-units/circle.yml new file mode 100644 index 000000000..9043b3547 --- /dev/null +++ b/src/runtime/vendor/github.com/docker/go-units/circle.yml @@ -0,0 +1,11 @@ +dependencies: + post: + # install golint + - go get github.com/golang/lint/golint + +test: + pre: + # run analysis before tests + - go vet ./... + - test -z "$(golint ./... | tee /dev/stderr)" + - test -z "$(gofmt -s -l . | tee /dev/stderr)" diff --git a/src/runtime/vendor/github.com/docker/go-units/duration.go b/src/runtime/vendor/github.com/docker/go-units/duration.go new file mode 100644 index 000000000..ba02af26d --- /dev/null +++ b/src/runtime/vendor/github.com/docker/go-units/duration.go @@ -0,0 +1,35 @@ +// Package units provides helper function to parse and print size and time units +// in human-readable format. +package units + +import ( + "fmt" + "time" +) + +// HumanDuration returns a human-readable approximation of a duration +// (eg. "About a minute", "4 hours ago", etc.). +func HumanDuration(d time.Duration) string { + if seconds := int(d.Seconds()); seconds < 1 { + return "Less than a second" + } else if seconds == 1 { + return "1 second" + } else if seconds < 60 { + return fmt.Sprintf("%d seconds", seconds) + } else if minutes := int(d.Minutes()); minutes == 1 { + return "About a minute" + } else if minutes < 46 { + return fmt.Sprintf("%d minutes", minutes) + } else if hours := int(d.Hours() + 0.5); hours == 1 { + return "About an hour" + } else if hours < 48 { + return fmt.Sprintf("%d hours", hours) + } else if hours < 24*7*2 { + return fmt.Sprintf("%d days", hours/24) + } else if hours < 24*30*2 { + return fmt.Sprintf("%d weeks", hours/24/7) + } else if hours < 24*365*2 { + return fmt.Sprintf("%d months", hours/24/30) + } + return fmt.Sprintf("%d years", int(d.Hours())/24/365) +} diff --git a/src/runtime/vendor/github.com/docker/go-units/size.go b/src/runtime/vendor/github.com/docker/go-units/size.go new file mode 100644 index 000000000..85f6ab071 --- /dev/null +++ b/src/runtime/vendor/github.com/docker/go-units/size.go @@ -0,0 +1,108 @@ +package units + +import ( + "fmt" + "regexp" + "strconv" + "strings" +) + +// See: http://en.wikipedia.org/wiki/Binary_prefix +const ( + // Decimal + + KB = 1000 + MB = 1000 * KB + GB = 1000 * MB + TB = 1000 * GB + PB = 1000 * TB + + // Binary + + KiB = 1024 + MiB = 1024 * KiB + GiB = 1024 * MiB + TiB = 1024 * GiB + PiB = 1024 * TiB +) + +type unitMap map[string]int64 + +var ( + decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB} + binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB} + sizeRegex = regexp.MustCompile(`^(\d+(\.\d+)*) ?([kKmMgGtTpP])?[iI]?[bB]?$`) +) + +var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} +var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} + +func getSizeAndUnit(size float64, base float64, _map []string) (float64, string) { + i := 0 + unitsLimit := len(_map) - 1 + for size >= base && i < unitsLimit { + size = size / base + i++ + } + return size, _map[i] +} + +// CustomSize returns a human-readable approximation of a size +// using custom format. +func CustomSize(format string, size float64, base float64, _map []string) string { + size, unit := getSizeAndUnit(size, base, _map) + return fmt.Sprintf(format, size, unit) +} + +// HumanSizeWithPrecision allows the size to be in any precision, +// instead of 4 digit precision used in units.HumanSize. +func HumanSizeWithPrecision(size float64, precision int) string { + size, unit := getSizeAndUnit(size, 1000.0, decimapAbbrs) + return fmt.Sprintf("%.*g%s", precision, size, unit) +} + +// HumanSize returns a human-readable approximation of a size +// capped at 4 valid numbers (eg. "2.746 MB", "796 KB"). +func HumanSize(size float64) string { + return HumanSizeWithPrecision(size, 4) +} + +// BytesSize returns a human-readable size in bytes, kibibytes, +// mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB"). +func BytesSize(size float64) string { + return CustomSize("%.4g%s", size, 1024.0, binaryAbbrs) +} + +// FromHumanSize returns an integer from a human-readable specification of a +// size using SI standard (eg. "44kB", "17MB"). +func FromHumanSize(size string) (int64, error) { + return parseSize(size, decimalMap) +} + +// RAMInBytes parses a human-readable string representing an amount of RAM +// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and +// returns the number of bytes, or -1 if the string is unparseable. +// Units are case-insensitive, and the 'b' suffix is optional. +func RAMInBytes(size string) (int64, error) { + return parseSize(size, binaryMap) +} + +// Parses the human-readable size string into the amount it represents. +func parseSize(sizeStr string, uMap unitMap) (int64, error) { + matches := sizeRegex.FindStringSubmatch(sizeStr) + if len(matches) != 4 { + return -1, fmt.Errorf("invalid size: '%s'", sizeStr) + } + + size, err := strconv.ParseFloat(matches[1], 64) + if err != nil { + return -1, err + } + + unitPrefix := strings.ToLower(matches[3]) + if mul, ok := uMap[unitPrefix]; ok { + size *= float64(mul) + } + + return int64(size), nil +} diff --git a/src/runtime/vendor/github.com/docker/go-units/ulimit.go b/src/runtime/vendor/github.com/docker/go-units/ulimit.go new file mode 100644 index 000000000..5ac7fd825 --- /dev/null +++ b/src/runtime/vendor/github.com/docker/go-units/ulimit.go @@ -0,0 +1,118 @@ +package units + +import ( + "fmt" + "strconv" + "strings" +) + +// Ulimit is a human friendly version of Rlimit. +type Ulimit struct { + Name string + Hard int64 + Soft int64 +} + +// Rlimit specifies the resource limits, such as max open files. +type Rlimit struct { + Type int `json:"type,omitempty"` + Hard uint64 `json:"hard,omitempty"` + Soft uint64 `json:"soft,omitempty"` +} + +const ( + // magic numbers for making the syscall + // some of these are defined in the syscall package, but not all. + // Also since Windows client doesn't get access to the syscall package, need to + // define these here + rlimitAs = 9 + rlimitCore = 4 + rlimitCPU = 0 + rlimitData = 2 + rlimitFsize = 1 + rlimitLocks = 10 + rlimitMemlock = 8 + rlimitMsgqueue = 12 + rlimitNice = 13 + rlimitNofile = 7 + rlimitNproc = 6 + rlimitRss = 5 + rlimitRtprio = 14 + rlimitRttime = 15 + rlimitSigpending = 11 + rlimitStack = 3 +) + +var ulimitNameMapping = map[string]int{ + //"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container. + "core": rlimitCore, + "cpu": rlimitCPU, + "data": rlimitData, + "fsize": rlimitFsize, + "locks": rlimitLocks, + "memlock": rlimitMemlock, + "msgqueue": rlimitMsgqueue, + "nice": rlimitNice, + "nofile": rlimitNofile, + "nproc": rlimitNproc, + "rss": rlimitRss, + "rtprio": rlimitRtprio, + "rttime": rlimitRttime, + "sigpending": rlimitSigpending, + "stack": rlimitStack, +} + +// ParseUlimit parses and returns a Ulimit from the specified string. +func ParseUlimit(val string) (*Ulimit, error) { + parts := strings.SplitN(val, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid ulimit argument: %s", val) + } + + if _, exists := ulimitNameMapping[parts[0]]; !exists { + return nil, fmt.Errorf("invalid ulimit type: %s", parts[0]) + } + + var ( + soft int64 + hard = &soft // default to soft in case no hard was set + temp int64 + err error + ) + switch limitVals := strings.Split(parts[1], ":"); len(limitVals) { + case 2: + temp, err = strconv.ParseInt(limitVals[1], 10, 64) + if err != nil { + return nil, err + } + hard = &temp + fallthrough + case 1: + soft, err = strconv.ParseInt(limitVals[0], 10, 64) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1]) + } + + if soft > *hard { + return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard) + } + + return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil +} + +// GetRlimit returns the RLimit corresponding to Ulimit. +func (u *Ulimit) GetRlimit() (*Rlimit, error) { + t, exists := ulimitNameMapping[u.Name] + if !exists { + return nil, fmt.Errorf("invalid ulimit name %s", u.Name) + } + + return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil +} + +func (u *Ulimit) String() string { + return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard) +} diff --git a/src/runtime/vendor/github.com/globalsign/mgo/LICENSE b/src/runtime/vendor/github.com/globalsign/mgo/LICENSE new file mode 100644 index 000000000..770c7672b --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/LICENSE @@ -0,0 +1,25 @@ +mgo - MongoDB driver for Go + +Copyright (c) 2010-2013 - Gustavo Niemeyer + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/runtime/vendor/github.com/globalsign/mgo/bson/LICENSE b/src/runtime/vendor/github.com/globalsign/mgo/bson/LICENSE new file mode 100644 index 000000000..890326017 --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/bson/LICENSE @@ -0,0 +1,25 @@ +BSON library for Go + +Copyright (c) 2010-2012 - Gustavo Niemeyer + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/runtime/vendor/github.com/globalsign/mgo/bson/README.md b/src/runtime/vendor/github.com/globalsign/mgo/bson/README.md new file mode 100644 index 000000000..5c5819e61 --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/bson/README.md @@ -0,0 +1,12 @@ +[![GoDoc](https://godoc.org/github.com/globalsign/mgo/bson?status.svg)](https://godoc.org/github.com/globalsign/mgo/bson) + +An Implementation of BSON for Go +-------------------------------- + +Package bson is an implementation of the [BSON specification](http://bsonspec.org) for Go. + +While the BSON package implements the BSON spec as faithfully as possible, there +is some MongoDB specific behaviour (such as map keys `$in`, `$all`, etc) in the +`bson` package. The priority is for backwards compatibility for the `mgo` +driver, though fixes for obviously buggy behaviour is welcome (and features, etc +behind feature flags). diff --git a/src/runtime/vendor/github.com/globalsign/mgo/bson/bson.go b/src/runtime/vendor/github.com/globalsign/mgo/bson/bson.go new file mode 100644 index 000000000..eb87ef620 --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/bson/bson.go @@ -0,0 +1,836 @@ +// BSON library for Go +// +// Copyright (c) 2010-2012 - Gustavo Niemeyer +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Package bson is an implementation of the BSON specification for Go: +// +// http://bsonspec.org +// +// It was created as part of the mgo MongoDB driver for Go, but is standalone +// and may be used on its own without the driver. +package bson + +import ( + "bytes" + "crypto/md5" + "crypto/rand" + "encoding/binary" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "os" + "reflect" + "runtime" + "strings" + "sync" + "sync/atomic" + "time" +) + +//go:generate go run bson_corpus_spec_test_generator.go + +// -------------------------------------------------------------------------- +// The public API. + +// Element types constants from BSON specification. +const ( + ElementFloat64 byte = 0x01 + ElementString byte = 0x02 + ElementDocument byte = 0x03 + ElementArray byte = 0x04 + ElementBinary byte = 0x05 + Element06 byte = 0x06 + ElementObjectId byte = 0x07 + ElementBool byte = 0x08 + ElementDatetime byte = 0x09 + ElementNil byte = 0x0A + ElementRegEx byte = 0x0B + ElementDBPointer byte = 0x0C + ElementJavaScriptWithoutScope byte = 0x0D + ElementSymbol byte = 0x0E + ElementJavaScriptWithScope byte = 0x0F + ElementInt32 byte = 0x10 + ElementTimestamp byte = 0x11 + ElementInt64 byte = 0x12 + ElementDecimal128 byte = 0x13 + ElementMinKey byte = 0xFF + ElementMaxKey byte = 0x7F + + BinaryGeneric byte = 0x00 + BinaryFunction byte = 0x01 + BinaryBinaryOld byte = 0x02 + BinaryUUIDOld byte = 0x03 + BinaryUUID byte = 0x04 + BinaryMD5 byte = 0x05 + BinaryUserDefined byte = 0x80 +) + +// Getter interface: a value implementing the bson.Getter interface will have its GetBSON +// method called when the given value has to be marshalled, and the result +// of this method will be marshaled in place of the actual object. +// +// If GetBSON returns return a non-nil error, the marshalling procedure +// will stop and error out with the provided value. +type Getter interface { + GetBSON() (interface{}, error) +} + +// Setter interface: a value implementing the bson.Setter interface will receive the BSON +// value via the SetBSON method during unmarshaling, and the object +// itself will not be changed as usual. +// +// If setting the value works, the method should return nil or alternatively +// bson.ErrSetZero to set the respective field to its zero value (nil for +// pointer types). If SetBSON returns a value of type bson.TypeError, the +// BSON value will be omitted from a map or slice being decoded and the +// unmarshalling will continue. If it returns any other non-nil error, the +// unmarshalling procedure will stop and error out with the provided value. +// +// This interface is generally useful in pointer receivers, since the method +// will want to change the receiver. A type field that implements the Setter +// interface doesn't have to be a pointer, though. +// +// Unlike the usual behavior, unmarshalling onto a value that implements a +// Setter interface will NOT reset the value to its zero state. This allows +// the value to decide by itself how to be unmarshalled. +// +// For example: +// +// type MyString string +// +// func (s *MyString) SetBSON(raw bson.Raw) error { +// return raw.Unmarshal(s) +// } +// +type Setter interface { + SetBSON(raw Raw) error +} + +// ErrSetZero may be returned from a SetBSON method to have the value set to +// its respective zero value. When used in pointer values, this will set the +// field to nil rather than to the pre-allocated value. +var ErrSetZero = errors.New("set to zero") + +// M is a convenient alias for a map[string]interface{} map, useful for +// dealing with BSON in a native way. For instance: +// +// bson.M{"a": 1, "b": true} +// +// There's no special handling for this type in addition to what's done anyway +// for an equivalent map type. Elements in the map will be dumped in an +// undefined ordered. See also the bson.D type for an ordered alternative. +type M map[string]interface{} + +// D represents a BSON document containing ordered elements. For example: +// +// bson.D{{"a", 1}, {"b", true}} +// +// In some situations, such as when creating indexes for MongoDB, the order in +// which the elements are defined is important. If the order is not important, +// using a map is generally more comfortable. See bson.M and bson.RawD. +type D []DocElem + +// DocElem is an element of the bson.D document representation. +type DocElem struct { + Name string + Value interface{} +} + +// Map returns a map out of the ordered element name/value pairs in d. +func (d D) Map() (m M) { + m = make(M, len(d)) + for _, item := range d { + m[item.Name] = item.Value + } + return m +} + +// The Raw type represents raw unprocessed BSON documents and elements. +// Kind is the kind of element as defined per the BSON specification, and +// Data is the raw unprocessed data for the respective element. +// Using this type it is possible to unmarshal or marshal values partially. +// +// Relevant documentation: +// +// http://bsonspec.org/#/specification +// +type Raw struct { + Kind byte + Data []byte +} + +// RawD represents a BSON document containing raw unprocessed elements. +// This low-level representation may be useful when lazily processing +// documents of uncertain content, or when manipulating the raw content +// documents in general. +type RawD []RawDocElem + +// RawDocElem elements of RawD type. +type RawDocElem struct { + Name string + Value Raw +} + +// ObjectId is a unique ID identifying a BSON value. It must be exactly 12 bytes +// long. MongoDB objects by default have such a property set in their "_id" +// property. +// +// http://www.mongodb.org/display/DOCS/Object+Ids +type ObjectId string + +// ObjectIdHex returns an ObjectId from the provided hex representation. +// Calling this function with an invalid hex representation will +// cause a runtime panic. See the IsObjectIdHex function. +func ObjectIdHex(s string) ObjectId { + d, err := hex.DecodeString(s) + if err != nil || len(d) != 12 { + panic(fmt.Sprintf("invalid input to ObjectIdHex: %q", s)) + } + return ObjectId(d) +} + +// IsObjectIdHex returns whether s is a valid hex representation of +// an ObjectId. See the ObjectIdHex function. +func IsObjectIdHex(s string) bool { + if len(s) != 24 { + return false + } + _, err := hex.DecodeString(s) + return err == nil +} + +// objectIdCounter is atomically incremented when generating a new ObjectId +// using NewObjectId() function. It's used as a counter part of an id. +var objectIdCounter = readRandomUint32() + +// readRandomUint32 returns a random objectIdCounter. +func readRandomUint32() uint32 { + var b [4]byte + _, err := io.ReadFull(rand.Reader, b[:]) + if err != nil { + panic(fmt.Errorf("cannot read random object id: %v", err)) + } + return uint32((uint32(b[0]) << 0) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24)) +} + +// machineId stores machine id generated once and used in subsequent calls +// to NewObjectId function. +var machineId = readMachineId() +var processId = os.Getpid() + +// readMachineId generates and returns a machine id. +// If this function fails to get the hostname it will cause a runtime error. +func readMachineId() []byte { + var sum [3]byte + id := sum[:] + hostname, err1 := os.Hostname() + if err1 != nil { + _, err2 := io.ReadFull(rand.Reader, id) + if err2 != nil { + panic(fmt.Errorf("cannot get hostname: %v; %v", err1, err2)) + } + return id + } + hw := md5.New() + hw.Write([]byte(hostname)) + copy(id, hw.Sum(nil)) + return id +} + +// NewObjectId returns a new unique ObjectId. +func NewObjectId() ObjectId { + var b [12]byte + // Timestamp, 4 bytes, big endian + binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix())) + // Machine, first 3 bytes of md5(hostname) + b[4] = machineId[0] + b[5] = machineId[1] + b[6] = machineId[2] + // Pid, 2 bytes, specs don't specify endianness, but we use big endian. + b[7] = byte(processId >> 8) + b[8] = byte(processId) + // Increment, 3 bytes, big endian + i := atomic.AddUint32(&objectIdCounter, 1) + b[9] = byte(i >> 16) + b[10] = byte(i >> 8) + b[11] = byte(i) + return ObjectId(b[:]) +} + +// NewObjectIdWithTime returns a dummy ObjectId with the timestamp part filled +// with the provided number of seconds from epoch UTC, and all other parts +// filled with zeroes. It's not safe to insert a document with an id generated +// by this method, it is useful only for queries to find documents with ids +// generated before or after the specified timestamp. +func NewObjectIdWithTime(t time.Time) ObjectId { + var b [12]byte + binary.BigEndian.PutUint32(b[:4], uint32(t.Unix())) + return ObjectId(string(b[:])) +} + +// String returns a hex string representation of the id. +// Example: ObjectIdHex("4d88e15b60f486e428412dc9"). +func (id ObjectId) String() string { + return fmt.Sprintf(`ObjectIdHex("%x")`, string(id)) +} + +// Hex returns a hex representation of the ObjectId. +func (id ObjectId) Hex() string { + return hex.EncodeToString([]byte(id)) +} + +// MarshalJSON turns a bson.ObjectId into a json.Marshaller. +func (id ObjectId) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%x"`, string(id))), nil +} + +var nullBytes = []byte("null") + +// UnmarshalJSON turns *bson.ObjectId into a json.Unmarshaller. +func (id *ObjectId) UnmarshalJSON(data []byte) error { + if len(data) > 0 && (data[0] == '{' || data[0] == 'O') { + var v struct { + Id json.RawMessage `json:"$oid"` + Func struct { + Id json.RawMessage + } `json:"$oidFunc"` + } + err := jdec(data, &v) + if err == nil { + if len(v.Id) > 0 { + data = []byte(v.Id) + } else { + data = []byte(v.Func.Id) + } + } + } + if len(data) == 2 && data[0] == '"' && data[1] == '"' || bytes.Equal(data, nullBytes) { + *id = "" + return nil + } + if len(data) != 26 || data[0] != '"' || data[25] != '"' { + return fmt.Errorf("invalid ObjectId in JSON: %s", string(data)) + } + var buf [12]byte + _, err := hex.Decode(buf[:], data[1:25]) + if err != nil { + return fmt.Errorf("invalid ObjectId in JSON: %s (%s)", string(data), err) + } + *id = ObjectId(string(buf[:])) + return nil +} + +// MarshalText turns bson.ObjectId into an encoding.TextMarshaler. +func (id ObjectId) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf("%x", string(id))), nil +} + +// UnmarshalText turns *bson.ObjectId into an encoding.TextUnmarshaler. +func (id *ObjectId) UnmarshalText(data []byte) error { + if len(data) == 1 && data[0] == ' ' || len(data) == 0 { + *id = "" + return nil + } + if len(data) != 24 { + return fmt.Errorf("invalid ObjectId: %s", data) + } + var buf [12]byte + _, err := hex.Decode(buf[:], data[:]) + if err != nil { + return fmt.Errorf("invalid ObjectId: %s (%s)", data, err) + } + *id = ObjectId(string(buf[:])) + return nil +} + +// Valid returns true if id is valid. A valid id must contain exactly 12 bytes. +func (id ObjectId) Valid() bool { + return len(id) == 12 +} + +// byteSlice returns byte slice of id from start to end. +// Calling this function with an invalid id will cause a runtime panic. +func (id ObjectId) byteSlice(start, end int) []byte { + if len(id) != 12 { + panic(fmt.Sprintf("invalid ObjectId: %q", string(id))) + } + return []byte(string(id)[start:end]) +} + +// Time returns the timestamp part of the id. +// It's a runtime error to call this method with an invalid id. +func (id ObjectId) Time() time.Time { + // First 4 bytes of ObjectId is 32-bit big-endian seconds from epoch. + secs := int64(binary.BigEndian.Uint32(id.byteSlice(0, 4))) + return time.Unix(secs, 0) +} + +// Machine returns the 3-byte machine id part of the id. +// It's a runtime error to call this method with an invalid id. +func (id ObjectId) Machine() []byte { + return id.byteSlice(4, 7) +} + +// Pid returns the process id part of the id. +// It's a runtime error to call this method with an invalid id. +func (id ObjectId) Pid() uint16 { + return binary.BigEndian.Uint16(id.byteSlice(7, 9)) +} + +// Counter returns the incrementing value part of the id. +// It's a runtime error to call this method with an invalid id. +func (id ObjectId) Counter() int32 { + b := id.byteSlice(9, 12) + // Counter is stored as big-endian 3-byte value + return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])) +} + +// The Symbol type is similar to a string and is used in languages with a +// distinct symbol type. +type Symbol string + +// Now returns the current time with millisecond precision. MongoDB stores +// timestamps with the same precision, so a Time returned from this method +// will not change after a roundtrip to the database. That's the only reason +// why this function exists. Using the time.Now function also works fine +// otherwise. +func Now() time.Time { + return time.Unix(0, time.Now().UnixNano()/1e6*1e6) +} + +// MongoTimestamp is a special internal type used by MongoDB that for some +// strange reason has its own datatype defined in BSON. +type MongoTimestamp int64 + +// Time returns the time part of ts which is stored with second precision. +func (ts MongoTimestamp) Time() time.Time { + return time.Unix(int64(uint64(ts)>>32), 0) +} + +// Counter returns the counter part of ts. +func (ts MongoTimestamp) Counter() uint32 { + return uint32(ts) +} + +// NewMongoTimestamp creates a timestamp using the given +// date `t` (with second precision) and counter `c` (unique for `t`). +// +// Returns an error if time `t` is not between 1970-01-01T00:00:00Z +// and 2106-02-07T06:28:15Z (inclusive). +// +// Note that two MongoTimestamps should never have the same (time, counter) combination: +// the caller must ensure the counter `c` is increased if creating multiple MongoTimestamp +// values for the same time `t` (ignoring fractions of seconds). +func NewMongoTimestamp(t time.Time, c uint32) (MongoTimestamp, error) { + u := t.Unix() + if u < 0 || u > math.MaxUint32 { + return -1, errors.New("invalid value for time") + } + + i := int64(u<<32 | int64(c)) + + return MongoTimestamp(i), nil +} + +type orderKey int64 + +// MaxKey is a special value that compares higher than all other possible BSON +// values in a MongoDB database. +var MaxKey = orderKey(1<<63 - 1) + +// MinKey is a special value that compares lower than all other possible BSON +// values in a MongoDB database. +var MinKey = orderKey(-1 << 63) + +type undefined struct{} + +// Undefined represents the undefined BSON value. +var Undefined undefined + +// Binary is a representation for non-standard binary values. Any kind should +// work, but the following are known as of this writing: +// +// 0x00 - Generic. This is decoded as []byte(data), not Binary{0x00, data}. +// 0x01 - Function (!?) +// 0x02 - Obsolete generic. +// 0x03 - UUID +// 0x05 - MD5 +// 0x80 - User defined. +// +type Binary struct { + Kind byte + Data []byte +} + +// RegEx represents a regular expression. The Options field may contain +// individual characters defining the way in which the pattern should be +// applied, and must be sorted. Valid options as of this writing are 'i' for +// case insensitive matching, 'm' for multi-line matching, 'x' for verbose +// mode, 'l' to make \w, \W, and similar be locale-dependent, 's' for dot-all +// mode (a '.' matches everything), and 'u' to make \w, \W, and similar match +// unicode. The value of the Options parameter is not verified before being +// marshaled into the BSON format. +type RegEx struct { + Pattern string + Options string +} + +// JavaScript is a type that holds JavaScript code. If Scope is non-nil, it +// will be marshaled as a mapping from identifiers to values that may be +// used when evaluating the provided Code. +type JavaScript struct { + Code string + Scope interface{} +} + +// DBPointer refers to a document id in a namespace. +// +// This type is deprecated in the BSON specification and should not be used +// except for backwards compatibility with ancient applications. +type DBPointer struct { + Namespace string + Id ObjectId +} + +const initialBufferSize = 64 + +func handleErr(err *error) { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } else if _, ok := r.(externalPanic); ok { + panic(r) + } else if s, ok := r.(string); ok { + *err = errors.New(s) + } else if e, ok := r.(error); ok { + *err = e + } else { + panic(r) + } + } +} + +// Marshal serializes the in value, which may be a map or a struct value. +// In the case of struct values, only exported fields will be serialized, +// and the order of serialized fields will match that of the struct itself. +// The lowercased field name is used as the key for each exported field, +// but this behavior may be changed using the respective field tag. +// The tag may also contain flags to tweak the marshalling behavior for +// the field. The tag formats accepted are: +// +// "[][,[,]]" +// +// `(...) bson:"[][,[,]]" (...)` +// +// The following flags are currently supported: +// +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// +// minsize Marshal an int64 value as an int32, if that's feasible +// while preserving the numeric value. +// +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the bson keys of other struct fields. +// +// Some examples: +// +// type T struct { +// A bool +// B int "myb" +// C string "myc,omitempty" +// D string `bson:",omitempty" json:"jsonkey"` +// E int64 ",minsize" +// F int64 "myf,omitempty,minsize" +// } +// +func Marshal(in interface{}) (out []byte, err error) { + return MarshalBuffer(in, make([]byte, 0, initialBufferSize)) +} + +// MarshalBuffer behaves the same way as Marshal, except that instead of +// allocating a new byte slice it tries to use the received byte slice and +// only allocates more memory if necessary to fit the marshaled value. +func MarshalBuffer(in interface{}, buf []byte) (out []byte, err error) { + defer handleErr(&err) + e := &encoder{buf} + e.addDoc(reflect.ValueOf(in)) + return e.out, nil +} + +// Unmarshal deserializes data from in into the out value. The out value +// must be a map, a pointer to a struct, or a pointer to a bson.D value. +// In the case of struct values, only exported fields will be deserialized. +// The lowercased field name is used as the key for each exported field, +// but this behavior may be changed using the respective field tag. +// The tag may also contain flags to tweak the marshalling behavior for +// the field. The tag formats accepted are: +// +// "[][,[,]]" +// +// `(...) bson:"[][,[,]]" (...)` +// +// The following flags are currently supported during unmarshal (see the +// Marshal method for other flags): +// +// inline Inline the field, which must be a struct or a map. +// Inlined structs are handled as if its fields were part +// of the outer struct. An inlined map causes keys that do +// not match any other struct field to be inserted in the +// map rather than being discarded as usual. +// +// The target field or element types of out may not necessarily match +// the BSON values of the provided data. The following conversions are +// made automatically: +// +// - Numeric types are converted if at least the integer part of the +// value would be preserved correctly +// - Bools are converted to numeric types as 1 or 0 +// - Numeric types are converted to bools as true if not 0 or false otherwise +// - Binary and string BSON data is converted to a string, array or byte slice +// +// If the value would not fit the type and cannot be converted, it's +// silently skipped. +// +// Pointer values are initialized when necessary. +func Unmarshal(in []byte, out interface{}) (err error) { + if raw, ok := out.(*Raw); ok { + raw.Kind = 3 + raw.Data = in + return nil + } + defer handleErr(&err) + v := reflect.ValueOf(out) + switch v.Kind() { + case reflect.Ptr: + fallthrough + case reflect.Map: + d := newDecoder(in) + d.readDocTo(v) + if d.i < len(d.in) { + return errors.New("document is corrupted") + } + case reflect.Struct: + return errors.New("unmarshal can't deal with struct values. Use a pointer") + default: + return errors.New("unmarshal needs a map or a pointer to a struct") + } + return nil +} + +// Unmarshal deserializes raw into the out value. If the out value type +// is not compatible with raw, a *bson.TypeError is returned. +// +// See the Unmarshal function documentation for more details on the +// unmarshalling process. +func (raw Raw) Unmarshal(out interface{}) (err error) { + defer handleErr(&err) + v := reflect.ValueOf(out) + switch v.Kind() { + case reflect.Ptr: + v = v.Elem() + fallthrough + case reflect.Map: + d := newDecoder(raw.Data) + good := d.readElemTo(v, raw.Kind) + if !good { + return &TypeError{v.Type(), raw.Kind} + } + case reflect.Struct: + return errors.New("raw Unmarshal can't deal with struct values. Use a pointer") + default: + return errors.New("raw Unmarshal needs a map or a valid pointer") + } + return nil +} + +// TypeError store details for type error occuring +// during unmarshaling +type TypeError struct { + Type reflect.Type + Kind byte +} + +func (e *TypeError) Error() string { + return fmt.Sprintf("BSON kind 0x%02x isn't compatible with type %s", e.Kind, e.Type.String()) +} + +// -------------------------------------------------------------------------- +// Maintain a mapping of keys to structure field indexes + +type structInfo struct { + FieldsMap map[string]fieldInfo + FieldsList []fieldInfo + InlineMap int + Zero reflect.Value +} + +type fieldInfo struct { + Key string + Num int + OmitEmpty bool + MinSize bool + Inline []int +} + +var structMap = make(map[reflect.Type]*structInfo) +var structMapMutex sync.RWMutex + +type externalPanic string + +func (e externalPanic) String() string { + return string(e) +} + +func getStructInfo(st reflect.Type) (*structInfo, error) { + structMapMutex.RLock() + sinfo, found := structMap[st] + structMapMutex.RUnlock() + if found { + return sinfo, nil + } + n := st.NumField() + fieldsMap := make(map[string]fieldInfo) + fieldsList := make([]fieldInfo, 0, n) + inlineMap := -1 + for i := 0; i != n; i++ { + field := st.Field(i) + if field.PkgPath != "" && !field.Anonymous { + continue // Private field + } + + info := fieldInfo{Num: i} + + tag := field.Tag.Get("bson") + + // Fall-back to JSON struct tag, if feature flag is set. + if tag == "" && useJSONTagFallback { + tag = field.Tag.Get("json") + } + + // If there's no bson/json tag available. + if tag == "" { + // If there's no tag, and also no tag: value splits (i.e. no colon) + // then assume the entire tag is the value + if strings.Index(string(field.Tag), ":") < 0 { + tag = string(field.Tag) + } + } + + if tag == "-" { + continue + } + + inline := false + fields := strings.Split(tag, ",") + if len(fields) > 1 { + for _, flag := range fields[1:] { + switch flag { + case "omitempty": + info.OmitEmpty = true + case "minsize": + info.MinSize = true + case "inline": + inline = true + default: + msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st) + panic(externalPanic(msg)) + } + } + tag = fields[0] + } + + if inline { + switch field.Type.Kind() { + case reflect.Map: + if inlineMap >= 0 { + return nil, errors.New("Multiple ,inline maps in struct " + st.String()) + } + if field.Type.Key() != reflect.TypeOf("") { + return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) + } + inlineMap = info.Num + case reflect.Ptr: + // allow only pointer to struct + if kind := field.Type.Elem().Kind(); kind != reflect.Struct { + return nil, errors.New("Option ,inline allows a pointer only to a struct, was given pointer to " + kind.String()) + } + + field.Type = field.Type.Elem() + fallthrough + case reflect.Struct: + sinfo, err := getStructInfo(field.Type) + if err != nil { + return nil, err + } + for _, finfo := range sinfo.FieldsList { + if _, found := fieldsMap[finfo.Key]; found { + msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + if finfo.Inline == nil { + finfo.Inline = []int{i, finfo.Num} + } else { + finfo.Inline = append([]int{i}, finfo.Inline...) + } + fieldsMap[finfo.Key] = finfo + fieldsList = append(fieldsList, finfo) + } + default: + panic("Option ,inline needs a struct value or a pointer to a struct or map field") + } + continue + } + + if tag != "" { + info.Key = tag + } else { + info.Key = strings.ToLower(field.Name) + } + + if _, found = fieldsMap[info.Key]; found { + msg := "Duplicated key '" + info.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + + fieldsList = append(fieldsList, info) + fieldsMap[info.Key] = info + } + sinfo = &structInfo{ + fieldsMap, + fieldsList, + inlineMap, + reflect.New(st).Elem(), + } + structMapMutex.Lock() + structMap[st] = sinfo + structMapMutex.Unlock() + return sinfo, nil +} diff --git a/src/runtime/vendor/github.com/globalsign/mgo/bson/compatibility.go b/src/runtime/vendor/github.com/globalsign/mgo/bson/compatibility.go new file mode 100644 index 000000000..66efd465f --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/bson/compatibility.go @@ -0,0 +1,29 @@ +package bson + +// Current state of the JSON tag fallback option. +var useJSONTagFallback = false +var useRespectNilValues = false + +// SetJSONTagFallback enables or disables the JSON-tag fallback for structure tagging. When this is enabled, structures +// without BSON tags on a field will fall-back to using the JSON tag (if present). +func SetJSONTagFallback(state bool) { + useJSONTagFallback = state +} + +// JSONTagFallbackState returns the current status of the JSON tag fallback compatability option. See SetJSONTagFallback +// for more information. +func JSONTagFallbackState() bool { + return useJSONTagFallback +} + +// SetRespectNilValues enables or disables serializing nil slices or maps to `null` values. +// In other words it enables `encoding/json` compatible behaviour. +func SetRespectNilValues(state bool) { + useRespectNilValues = state +} + +// RespectNilValuesState returns the current status of the JSON nil slices and maps fallback compatibility option. +// See SetRespectNilValues for more information. +func RespectNilValuesState() bool { + return useRespectNilValues +} diff --git a/src/runtime/vendor/github.com/globalsign/mgo/bson/decimal.go b/src/runtime/vendor/github.com/globalsign/mgo/bson/decimal.go new file mode 100644 index 000000000..672ba1825 --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/bson/decimal.go @@ -0,0 +1,312 @@ +// BSON library for Go +// +// Copyright (c) 2010-2012 - Gustavo Niemeyer +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package bson + +import ( + "fmt" + "strconv" + "strings" +) + +// Decimal128 holds decimal128 BSON values. +type Decimal128 struct { + h, l uint64 +} + +func (d Decimal128) String() string { + var pos int // positive sign + var e int // exponent + var h, l uint64 // significand high/low + + if d.h>>63&1 == 0 { + pos = 1 + } + + switch d.h >> 58 & (1<<5 - 1) { + case 0x1F: + return "NaN" + case 0x1E: + return "-Inf"[pos:] + } + + l = d.l + if d.h>>61&3 == 3 { + // Bits: 1*sign 2*ignored 14*exponent 111*significand. + // Implicit 0b100 prefix in significand. + e = int(d.h>>47&(1<<14-1)) - 6176 + //h = 4<<47 | d.h&(1<<47-1) + // Spec says all of these values are out of range. + h, l = 0, 0 + } else { + // Bits: 1*sign 14*exponent 113*significand + e = int(d.h>>49&(1<<14-1)) - 6176 + h = d.h & (1<<49 - 1) + } + + // Would be handled by the logic below, but that's trivial and common. + if h == 0 && l == 0 && e == 0 { + return "-0"[pos:] + } + + var repr [48]byte // Loop 5 times over 9 digits plus dot, negative sign, and leading zero. + var last = len(repr) + var i = len(repr) + var dot = len(repr) + e + var rem uint32 +Loop: + for d9 := 0; d9 < 5; d9++ { + h, l, rem = divmod(h, l, 1e9) + for d1 := 0; d1 < 9; d1++ { + // Handle "-0.0", "0.00123400", "-1.00E-6", "1.050E+3", etc. + if i < len(repr) && (dot == i || l == 0 && h == 0 && rem > 0 && rem < 10 && (dot < i-6 || e > 0)) { + e += len(repr) - i + i-- + repr[i] = '.' + last = i - 1 + dot = len(repr) // Unmark. + } + c := '0' + byte(rem%10) + rem /= 10 + i-- + repr[i] = c + // Handle "0E+3", "1E+3", etc. + if l == 0 && h == 0 && rem == 0 && i == len(repr)-1 && (dot < i-5 || e > 0) { + last = i + break Loop + } + if c != '0' { + last = i + } + // Break early. Works without it, but why. + if dot > i && l == 0 && h == 0 && rem == 0 { + break Loop + } + } + } + repr[last-1] = '-' + last-- + + if e > 0 { + return string(repr[last+pos:]) + "E+" + strconv.Itoa(e) + } + if e < 0 { + return string(repr[last+pos:]) + "E" + strconv.Itoa(e) + } + return string(repr[last+pos:]) +} + +func divmod(h, l uint64, div uint32) (qh, ql uint64, rem uint32) { + div64 := uint64(div) + a := h >> 32 + aq := a / div64 + ar := a % div64 + b := ar<<32 + h&(1<<32-1) + bq := b / div64 + br := b % div64 + c := br<<32 + l>>32 + cq := c / div64 + cr := c % div64 + d := cr<<32 + l&(1<<32-1) + dq := d / div64 + dr := d % div64 + return (aq<<32 | bq), (cq<<32 | dq), uint32(dr) +} + +var dNaN = Decimal128{0x1F << 58, 0} +var dPosInf = Decimal128{0x1E << 58, 0} +var dNegInf = Decimal128{0x3E << 58, 0} + +func dErr(s string) (Decimal128, error) { + return dNaN, fmt.Errorf("cannot parse %q as a decimal128", s) +} + +// ParseDecimal128 parse a string and return the corresponding value as +// a decimal128 +func ParseDecimal128(s string) (Decimal128, error) { + orig := s + if s == "" { + return dErr(orig) + } + neg := s[0] == '-' + if neg || s[0] == '+' { + s = s[1:] + } + + if (len(s) == 3 || len(s) == 8) && (s[0] == 'N' || s[0] == 'n' || s[0] == 'I' || s[0] == 'i') { + if s == "NaN" || s == "nan" || strings.EqualFold(s, "nan") { + return dNaN, nil + } + if s == "Inf" || s == "inf" || strings.EqualFold(s, "inf") || strings.EqualFold(s, "infinity") { + if neg { + return dNegInf, nil + } + return dPosInf, nil + } + return dErr(orig) + } + + var h, l uint64 + var e int + + var add, ovr uint32 + var mul uint32 = 1 + var dot = -1 + var digits = 0 + var i = 0 + for i < len(s) { + c := s[i] + if mul == 1e9 { + h, l, ovr = muladd(h, l, mul, add) + mul, add = 1, 0 + if ovr > 0 || h&((1<<15-1)<<49) > 0 { + return dErr(orig) + } + } + if c >= '0' && c <= '9' { + i++ + if c > '0' || digits > 0 { + digits++ + } + if digits > 34 { + if c == '0' { + // Exact rounding. + e++ + continue + } + return dErr(orig) + } + mul *= 10 + add *= 10 + add += uint32(c - '0') + continue + } + if c == '.' { + i++ + if dot >= 0 || i == 1 && len(s) == 1 { + return dErr(orig) + } + if i == len(s) { + break + } + if s[i] < '0' || s[i] > '9' || e > 0 { + return dErr(orig) + } + dot = i + continue + } + break + } + if i == 0 { + return dErr(orig) + } + if mul > 1 { + h, l, ovr = muladd(h, l, mul, add) + if ovr > 0 || h&((1<<15-1)<<49) > 0 { + return dErr(orig) + } + } + if dot >= 0 { + e += dot - i + } + if i+1 < len(s) && (s[i] == 'E' || s[i] == 'e') { + i++ + eneg := s[i] == '-' + if eneg || s[i] == '+' { + i++ + if i == len(s) { + return dErr(orig) + } + } + n := 0 + for i < len(s) && n < 1e4 { + c := s[i] + i++ + if c < '0' || c > '9' { + return dErr(orig) + } + n *= 10 + n += int(c - '0') + } + if eneg { + n = -n + } + e += n + for e < -6176 { + // Subnormal. + var div uint32 = 1 + for div < 1e9 && e < -6176 { + div *= 10 + e++ + } + var rem uint32 + h, l, rem = divmod(h, l, div) + if rem > 0 { + return dErr(orig) + } + } + for e > 6111 { + // Clamped. + var mul uint32 = 1 + for mul < 1e9 && e > 6111 { + mul *= 10 + e-- + } + h, l, ovr = muladd(h, l, mul, 0) + if ovr > 0 || h&((1<<15-1)<<49) > 0 { + return dErr(orig) + } + } + if e < -6176 || e > 6111 { + return dErr(orig) + } + } + + if i < len(s) { + return dErr(orig) + } + + h |= uint64(e+6176) & uint64(1<<14-1) << 49 + if neg { + h |= 1 << 63 + } + return Decimal128{h, l}, nil +} + +func muladd(h, l uint64, mul uint32, add uint32) (resh, resl uint64, overflow uint32) { + mul64 := uint64(mul) + a := mul64 * (l & (1<<32 - 1)) + b := a>>32 + mul64*(l>>32) + c := b>>32 + mul64*(h&(1<<32-1)) + d := c>>32 + mul64*(h>>32) + + a = a&(1<<32-1) + uint64(add) + b = b&(1<<32-1) + a>>32 + c = c&(1<<32-1) + b>>32 + d = d&(1<<32-1) + c>>32 + + return (d<<32 | c&(1<<32-1)), (b<<32 | a&(1<<32-1)), uint32(d >> 32) +} diff --git a/src/runtime/vendor/github.com/globalsign/mgo/bson/decode.go b/src/runtime/vendor/github.com/globalsign/mgo/bson/decode.go new file mode 100644 index 000000000..658856add --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/bson/decode.go @@ -0,0 +1,1055 @@ +// BSON library for Go +// +// Copyright (c) 2010-2012 - Gustavo Niemeyer +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// gobson - BSON library for Go. + +package bson + +import ( + "errors" + "fmt" + "io" + "math" + "net/url" + "reflect" + "strconv" + "sync" + "time" +) + +type decoder struct { + in []byte + i int + docType reflect.Type +} + +var typeM = reflect.TypeOf(M{}) + +func newDecoder(in []byte) *decoder { + return &decoder{in, 0, typeM} +} + +// -------------------------------------------------------------------------- +// Some helper functions. + +func corrupted() { + panic("Document is corrupted") +} + +// -------------------------------------------------------------------------- +// Unmarshaling of documents. + +const ( + setterUnknown = iota + setterNone + setterType + setterAddr +) + +var setterStyles map[reflect.Type]int +var setterIface reflect.Type +var setterMutex sync.RWMutex + +func init() { + var iface Setter + setterIface = reflect.TypeOf(&iface).Elem() + setterStyles = make(map[reflect.Type]int) +} + +func setterStyle(outt reflect.Type) int { + setterMutex.RLock() + style := setterStyles[outt] + setterMutex.RUnlock() + if style != setterUnknown { + return style + } + + setterMutex.Lock() + defer setterMutex.Unlock() + if outt.Implements(setterIface) { + style = setterType + } else if reflect.PtrTo(outt).Implements(setterIface) { + style = setterAddr + } else { + style = setterNone + } + setterStyles[outt] = style + return style +} + +func getSetter(outt reflect.Type, out reflect.Value) Setter { + style := setterStyle(outt) + if style == setterNone { + return nil + } + if style == setterAddr { + if !out.CanAddr() { + return nil + } + out = out.Addr() + } else if outt.Kind() == reflect.Ptr && out.IsNil() { + out.Set(reflect.New(outt.Elem())) + } + return out.Interface().(Setter) +} + +func clearMap(m reflect.Value) { + var none reflect.Value + for _, k := range m.MapKeys() { + m.SetMapIndex(k, none) + } +} + +func (d *decoder) readDocTo(out reflect.Value) { + var elemType reflect.Type + outt := out.Type() + outk := outt.Kind() + + for { + if outk == reflect.Ptr && out.IsNil() { + out.Set(reflect.New(outt.Elem())) + } + if setter := getSetter(outt, out); setter != nil { + raw := d.readRaw(ElementDocument) + err := setter.SetBSON(raw) + if _, ok := err.(*TypeError); err != nil && !ok { + panic(err) + } + return + } + if outk == reflect.Ptr { + out = out.Elem() + outt = out.Type() + outk = out.Kind() + continue + } + break + } + + var fieldsMap map[string]fieldInfo + var inlineMap reflect.Value + if outt == typeRaw { + out.Set(reflect.ValueOf(d.readRaw(ElementDocument))) + return + } + + origout := out + if outk == reflect.Interface { + if d.docType.Kind() == reflect.Map { + mv := reflect.MakeMap(d.docType) + out.Set(mv) + out = mv + } else { + dv := reflect.New(d.docType).Elem() + out.Set(dv) + out = dv + } + outt = out.Type() + outk = outt.Kind() + } + + docType := d.docType + keyType := typeString + convertKey := false + switch outk { + case reflect.Map: + keyType = outt.Key() + if keyType != typeString { + convertKey = true + } + elemType = outt.Elem() + if elemType == typeIface { + d.docType = outt + } + if out.IsNil() { + out.Set(reflect.MakeMap(out.Type())) + } else if out.Len() > 0 { + clearMap(out) + } + case reflect.Struct: + sinfo, err := getStructInfo(out.Type()) + if err != nil { + panic(err) + } + fieldsMap = sinfo.FieldsMap + out.Set(sinfo.Zero) + if sinfo.InlineMap != -1 { + inlineMap = out.Field(sinfo.InlineMap) + if !inlineMap.IsNil() && inlineMap.Len() > 0 { + clearMap(inlineMap) + } + elemType = inlineMap.Type().Elem() + if elemType == typeIface { + d.docType = inlineMap.Type() + } + } + case reflect.Slice: + switch outt.Elem() { + case typeDocElem: + origout.Set(d.readDocElems(outt)) + return + case typeRawDocElem: + origout.Set(d.readRawDocElems(outt)) + return + } + fallthrough + default: + panic("Unsupported document type for unmarshalling: " + out.Type().String()) + } + + end := int(d.readInt32()) + end += d.i - 4 + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { + corrupted() + } + for d.in[d.i] != '\x00' { + kind := d.readByte() + name := d.readCStr() + if d.i >= end { + corrupted() + } + + switch outk { + case reflect.Map: + e := reflect.New(elemType).Elem() + if d.readElemTo(e, kind) { + k := reflect.ValueOf(name) + if convertKey { + mapKeyType := out.Type().Key() + mapKeyKind := mapKeyType.Kind() + + switch mapKeyKind { + case reflect.Int: + fallthrough + case reflect.Int8: + fallthrough + case reflect.Int16: + fallthrough + case reflect.Int32: + fallthrough + case reflect.Int64: + fallthrough + case reflect.Uint: + fallthrough + case reflect.Uint8: + fallthrough + case reflect.Uint16: + fallthrough + case reflect.Uint32: + fallthrough + case reflect.Uint64: + fallthrough + case reflect.Float32: + fallthrough + case reflect.Float64: + parsed := d.parseMapKeyAsFloat(k, mapKeyKind) + k = reflect.ValueOf(parsed) + case reflect.String: + mapKeyType = keyType + default: + panic("BSON map must have string or decimal keys. Got: " + outt.String()) + } + + k = k.Convert(mapKeyType) + } + out.SetMapIndex(k, e) + } + case reflect.Struct: + if info, ok := fieldsMap[name]; ok { + if info.Inline == nil { + d.readElemTo(out.Field(info.Num), kind) + } else { + d.readElemTo(out.FieldByIndex(info.Inline), kind) + } + } else if inlineMap.IsValid() { + if inlineMap.IsNil() { + inlineMap.Set(reflect.MakeMap(inlineMap.Type())) + } + e := reflect.New(elemType).Elem() + if d.readElemTo(e, kind) { + inlineMap.SetMapIndex(reflect.ValueOf(name), e) + } + } else { + d.dropElem(kind) + } + case reflect.Slice: + } + + if d.i >= end { + corrupted() + } + } + d.i++ // '\x00' + if d.i != end { + corrupted() + } + d.docType = docType +} + +func (decoder) parseMapKeyAsFloat(k reflect.Value, mapKeyKind reflect.Kind) float64 { + parsed, err := strconv.ParseFloat(k.String(), 64) + if err != nil { + panic("Map key is defined to be a decimal type (" + mapKeyKind.String() + ") but got error " + + err.Error()) + } + + return parsed +} + +func (d *decoder) readArrayDocTo(out reflect.Value) { + end := int(d.readInt32()) + end += d.i - 4 + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { + corrupted() + } + i := 0 + l := out.Len() + for d.in[d.i] != '\x00' { + if i >= l { + panic("Length mismatch on array field") + } + kind := d.readByte() + for d.i < end && d.in[d.i] != '\x00' { + d.i++ + } + if d.i >= end { + corrupted() + } + d.i++ + d.readElemTo(out.Index(i), kind) + if d.i >= end { + corrupted() + } + i++ + } + if i != l { + panic("Length mismatch on array field") + } + d.i++ // '\x00' + if d.i != end { + corrupted() + } +} + +func (d *decoder) readSliceDoc(t reflect.Type) interface{} { + tmp := make([]reflect.Value, 0, 8) + elemType := t.Elem() + if elemType == typeRawDocElem { + d.dropElem(ElementArray) + return reflect.Zero(t).Interface() + } + if elemType == typeRaw { + return d.readSliceOfRaw() + } + + end := int(d.readInt32()) + end += d.i - 4 + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { + corrupted() + } + for d.in[d.i] != '\x00' { + kind := d.readByte() + for d.i < end && d.in[d.i] != '\x00' { + d.i++ + } + if d.i >= end { + corrupted() + } + d.i++ + e := reflect.New(elemType).Elem() + if d.readElemTo(e, kind) { + tmp = append(tmp, e) + } + if d.i >= end { + corrupted() + } + } + d.i++ // '\x00' + if d.i != end { + corrupted() + } + + n := len(tmp) + slice := reflect.MakeSlice(t, n, n) + for i := 0; i != n; i++ { + slice.Index(i).Set(tmp[i]) + } + return slice.Interface() +} + +func BSONElementSize(kind byte, offset int, buffer []byte) (int, error) { + switch kind { + case ElementFloat64: // Float64 + return 8, nil + case ElementJavaScriptWithoutScope: // JavaScript without scope + fallthrough + case ElementSymbol: // Symbol + fallthrough + case ElementString: // UTF-8 string + size, err := getSize(offset, buffer) + if err != nil { + return 0, err + } + if size < 1 { + return 0, errors.New("String size can't be less then one byte") + } + size += 4 + if offset+size > len(buffer) { + return 0, io.ErrUnexpectedEOF + } + if buffer[offset+size-1] != 0 { + return 0, errors.New("Invalid string: non zero-terminated") + } + return size, nil + case ElementArray: // Array + fallthrough + case ElementDocument: // Document + size, err := getSize(offset, buffer) + if err != nil { + return 0, err + } + if size < 5 { + return 0, errors.New("Declared document size is too small") + } + return size, nil + case ElementBinary: // Binary + size, err := getSize(offset, buffer) + if err != nil { + return 0, err + } + if size < 0 { + return 0, errors.New("Binary data size can't be negative") + } + return size + 5, nil + case Element06: // Undefined (obsolete, but still seen in the wild) + return 0, nil + case ElementObjectId: // ObjectId + return 12, nil + case ElementBool: // Bool + return 1, nil + case ElementDatetime: // Timestamp + return 8, nil + case ElementNil: // Nil + return 0, nil + case ElementRegEx: // RegEx + end := offset + for i := 0; i < 2; i++ { + for end < len(buffer) && buffer[end] != '\x00' { + end++ + } + end++ + } + if end > len(buffer) { + return 0, io.ErrUnexpectedEOF + } + return end - offset, nil + case ElementDBPointer: // DBPointer + size, err := getSize(offset, buffer) + if err != nil { + return 0, err + } + if size < 1 { + return 0, errors.New("String size can't be less then one byte") + } + return size + 12 + 4, nil + case ElementJavaScriptWithScope: // JavaScript with scope + size, err := getSize(offset, buffer) + if err != nil { + return 0, err + } + if size < 4+5+5 { + return 0, errors.New("Declared document element is too small") + } + return size, nil + case ElementInt32: // Int32 + return 4, nil + case ElementTimestamp: // Mongo-specific timestamp + return 8, nil + case ElementInt64: // Int64 + return 8, nil + case ElementDecimal128: // Decimal128 + return 16, nil + case ElementMaxKey: // Max key + return 0, nil + case ElementMinKey: // Min key + return 0, nil + default: + return 0, errors.New(fmt.Sprintf("Unknown element kind (0x%02X)", kind)) + } +} + +func (d *decoder) readRaw(kind byte) Raw { + size, err := BSONElementSize(kind, d.i, d.in) + if err != nil { + corrupted() + } + if d.i+size > len(d.in) { + corrupted() + } + d.i += size + return Raw{ + Kind: kind, + Data: d.in[d.i-size : d.i], + } +} + +func (d *decoder) readSliceOfRaw() interface{} { + tmp := make([]Raw, 0, 8) + end := int(d.readInt32()) + end += d.i - 4 + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { + corrupted() + } + for d.in[d.i] != '\x00' { + kind := d.readByte() + for d.i < end && d.in[d.i] != '\x00' { + d.i++ + } + if d.i >= end { + corrupted() + } + d.i++ + e := d.readRaw(kind) + tmp = append(tmp, e) + if d.i >= end { + corrupted() + } + } + d.i++ // '\x00' + if d.i != end { + corrupted() + } + return tmp +} + +var typeSlice = reflect.TypeOf([]interface{}{}) +var typeIface = typeSlice.Elem() + +func (d *decoder) readDocElems(typ reflect.Type) reflect.Value { + docType := d.docType + d.docType = typ + slice := make([]DocElem, 0, 8) + d.readDocWith(func(kind byte, name string) { + e := DocElem{Name: name} + v := reflect.ValueOf(&e.Value) + if d.readElemTo(v.Elem(), kind) { + slice = append(slice, e) + } + }) + slicev := reflect.New(typ).Elem() + slicev.Set(reflect.ValueOf(slice)) + d.docType = docType + return slicev +} + +func (d *decoder) readRawDocElems(typ reflect.Type) reflect.Value { + docType := d.docType + d.docType = typ + slice := make([]RawDocElem, 0, 8) + d.readDocWith(func(kind byte, name string) { + e := RawDocElem{Name: name, Value: d.readRaw(kind)} + slice = append(slice, e) + }) + slicev := reflect.New(typ).Elem() + slicev.Set(reflect.ValueOf(slice)) + d.docType = docType + return slicev +} + +func (d *decoder) readDocWith(f func(kind byte, name string)) { + end := int(d.readInt32()) + end += d.i - 4 + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { + corrupted() + } + for d.in[d.i] != '\x00' { + kind := d.readByte() + name := d.readCStr() + if d.i >= end { + corrupted() + } + f(kind, name) + if d.i >= end { + corrupted() + } + } + d.i++ // '\x00' + if d.i != end { + corrupted() + } +} + +// -------------------------------------------------------------------------- +// Unmarshaling of individual elements within a document. +func (d *decoder) dropElem(kind byte) { + size, err := BSONElementSize(kind, d.i, d.in) + if err != nil { + corrupted() + } + if d.i+size > len(d.in) { + corrupted() + } + d.i += size +} + +// Attempt to decode an element from the document and put it into out. +// If the types are not compatible, the returned ok value will be +// false and out will be unchanged. +func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) { + outt := out.Type() + + if outt == typeRaw { + out.Set(reflect.ValueOf(d.readRaw(kind))) + return true + } + + if outt == typeRawPtr { + raw := d.readRaw(kind) + out.Set(reflect.ValueOf(&raw)) + return true + } + + if kind == ElementDocument { + // Delegate unmarshaling of documents. + outt := out.Type() + outk := out.Kind() + switch outk { + case reflect.Interface, reflect.Ptr, reflect.Struct, reflect.Map: + d.readDocTo(out) + return true + } + if setterStyle(outt) != setterNone { + d.readDocTo(out) + return true + } + if outk == reflect.Slice { + switch outt.Elem() { + case typeDocElem: + out.Set(d.readDocElems(outt)) + case typeRawDocElem: + out.Set(d.readRawDocElems(outt)) + default: + d.dropElem(kind) + } + return true + } + d.dropElem(kind) + return true + } + + if setter := getSetter(outt, out); setter != nil { + err := setter.SetBSON(d.readRaw(kind)) + if err == ErrSetZero { + out.Set(reflect.Zero(outt)) + return true + } + if err == nil { + return true + } + if _, ok := err.(*TypeError); !ok { + panic(err) + } + return false + } + + var in interface{} + + switch kind { + case ElementFloat64: + in = d.readFloat64() + case ElementString: + in = d.readStr() + case ElementDocument: + panic("Can't happen. Handled above.") + case ElementArray: + outt := out.Type() + if setterStyle(outt) != setterNone { + // Skip the value so its data is handed to the setter below. + d.dropElem(kind) + break + } + for outt.Kind() == reflect.Ptr { + outt = outt.Elem() + } + switch outt.Kind() { + case reflect.Array: + d.readArrayDocTo(out) + return true + case reflect.Slice: + in = d.readSliceDoc(outt) + default: + in = d.readSliceDoc(typeSlice) + } + case ElementBinary: + b := d.readBinary() + if b.Kind == BinaryGeneric || b.Kind == BinaryBinaryOld { + in = b.Data + } else { + in = b + } + case Element06: // Undefined (obsolete, but still seen in the wild) + in = Undefined + case ElementObjectId: + in = ObjectId(d.readBytes(12)) + case ElementBool: + in = d.readBool() + case ElementDatetime: // Timestamp + // MongoDB handles timestamps as milliseconds. + i := d.readInt64() + if i == -62135596800000 { + in = time.Time{} // In UTC for convenience. + } else { + in = time.Unix(i/1e3, i%1e3*1e6).UTC() + } + case ElementNil: + in = nil + case ElementRegEx: + in = d.readRegEx() + case ElementDBPointer: + in = DBPointer{Namespace: d.readStr(), Id: ObjectId(d.readBytes(12))} + case ElementJavaScriptWithoutScope: + in = JavaScript{Code: d.readStr()} + case ElementSymbol: + in = Symbol(d.readStr()) + case ElementJavaScriptWithScope: + start := d.i + l := int(d.readInt32()) + js := JavaScript{d.readStr(), make(M)} + d.readDocTo(reflect.ValueOf(js.Scope)) + if d.i != start+l { + corrupted() + } + in = js + case ElementInt32: + in = int(d.readInt32()) + case ElementTimestamp: // Mongo-specific timestamp + in = MongoTimestamp(d.readInt64()) + case ElementInt64: + switch out.Type() { + case typeTimeDuration: + in = time.Duration(time.Duration(d.readInt64()) * time.Millisecond) + default: + in = d.readInt64() + } + case ElementDecimal128: + in = Decimal128{ + l: uint64(d.readInt64()), + h: uint64(d.readInt64()), + } + case ElementMaxKey: + in = MaxKey + case ElementMinKey: + in = MinKey + default: + panic(fmt.Sprintf("Unknown element kind (0x%02X)", kind)) + } + + if in == nil { + out.Set(reflect.Zero(outt)) + return true + } + + outk := outt.Kind() + + // Dereference and initialize pointer if necessary. + first := true + for outk == reflect.Ptr { + if !out.IsNil() { + out = out.Elem() + } else { + elem := reflect.New(outt.Elem()) + if first { + // Only set if value is compatible. + first = false + defer func(out, elem reflect.Value) { + if good { + out.Set(elem) + } + }(out, elem) + } else { + out.Set(elem) + } + out = elem + } + outt = out.Type() + outk = outt.Kind() + } + + inv := reflect.ValueOf(in) + if outt == inv.Type() { + out.Set(inv) + return true + } + + switch outk { + case reflect.Interface: + out.Set(inv) + return true + case reflect.String: + switch inv.Kind() { + case reflect.String: + out.SetString(inv.String()) + return true + case reflect.Slice: + if b, ok := in.([]byte); ok { + out.SetString(string(b)) + return true + } + case reflect.Int, reflect.Int64: + if outt == typeJSONNumber { + out.SetString(strconv.FormatInt(inv.Int(), 10)) + return true + } + case reflect.Float64: + if outt == typeJSONNumber { + out.SetString(strconv.FormatFloat(inv.Float(), 'f', -1, 64)) + return true + } + } + case reflect.Slice, reflect.Array: + // Remember, array (0x04) slices are built with the correct + // element type. If we are here, must be a cross BSON kind + // conversion (e.g. 0x05 unmarshalling on string). + if outt.Elem().Kind() != reflect.Uint8 { + break + } + switch inv.Kind() { + case reflect.String: + slice := []byte(inv.String()) + out.Set(reflect.ValueOf(slice)) + return true + case reflect.Slice: + switch outt.Kind() { + case reflect.Array: + reflect.Copy(out, inv) + case reflect.Slice: + out.SetBytes(inv.Bytes()) + } + return true + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch inv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + out.SetInt(inv.Int()) + return true + case reflect.Float32, reflect.Float64: + out.SetInt(int64(inv.Float())) + return true + case reflect.Bool: + if inv.Bool() { + out.SetInt(1) + } else { + out.SetInt(0) + } + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + panic("can't happen: no uint types in BSON (!?)") + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch inv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + out.SetUint(uint64(inv.Int())) + return true + case reflect.Float32, reflect.Float64: + out.SetUint(uint64(inv.Float())) + return true + case reflect.Bool: + if inv.Bool() { + out.SetUint(1) + } else { + out.SetUint(0) + } + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + panic("Can't happen. No uint types in BSON.") + } + case reflect.Float32, reflect.Float64: + switch inv.Kind() { + case reflect.Float32, reflect.Float64: + out.SetFloat(inv.Float()) + return true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + out.SetFloat(float64(inv.Int())) + return true + case reflect.Bool: + if inv.Bool() { + out.SetFloat(1) + } else { + out.SetFloat(0) + } + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + panic("Can't happen. No uint types in BSON?") + } + case reflect.Bool: + switch inv.Kind() { + case reflect.Bool: + out.SetBool(inv.Bool()) + return true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + out.SetBool(inv.Int() != 0) + return true + case reflect.Float32, reflect.Float64: + out.SetBool(inv.Float() != 0) + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + panic("Can't happen. No uint types in BSON?") + } + case reflect.Struct: + if outt == typeURL && inv.Kind() == reflect.String { + u, err := url.Parse(inv.String()) + if err != nil { + panic(err) + } + out.Set(reflect.ValueOf(u).Elem()) + return true + } + if outt == typeBinary { + if b, ok := in.([]byte); ok { + out.Set(reflect.ValueOf(Binary{Data: b})) + return true + } + } + } + + return false +} + +// -------------------------------------------------------------------------- +// Parsers of basic types. + +func (d *decoder) readRegEx() RegEx { + re := RegEx{} + re.Pattern = d.readCStr() + re.Options = d.readCStr() + return re +} + +func (d *decoder) readBinary() Binary { + l := d.readInt32() + b := Binary{} + b.Kind = d.readByte() + if b.Kind == BinaryBinaryOld && l > 4 { + // Weird obsolete format with redundant length. + rl := d.readInt32() + if rl != l-4 { + corrupted() + } + l = rl + } + b.Data = d.readBytes(l) + return b +} + +func (d *decoder) readStr() string { + l := d.readInt32() + b := d.readBytes(l - 1) + if d.readByte() != '\x00' { + corrupted() + } + return string(b) +} + +func (d *decoder) readCStr() string { + start := d.i + end := start + l := len(d.in) + for ; end != l; end++ { + if d.in[end] == '\x00' { + break + } + } + d.i = end + 1 + if d.i > l { + corrupted() + } + return string(d.in[start:end]) +} + +func (d *decoder) readBool() bool { + b := d.readByte() + if b == 0 { + return false + } + if b == 1 { + return true + } + panic(fmt.Sprintf("encoded boolean must be 1 or 0, found %d", b)) +} + +func (d *decoder) readFloat64() float64 { + return math.Float64frombits(uint64(d.readInt64())) +} + +func (d *decoder) readInt32() int32 { + b := d.readBytes(4) + return int32((uint32(b[0]) << 0) | + (uint32(b[1]) << 8) | + (uint32(b[2]) << 16) | + (uint32(b[3]) << 24)) +} + +func getSize(offset int, b []byte) (int, error) { + if offset+4 > len(b) { + return 0, io.ErrUnexpectedEOF + } + return int((uint32(b[offset]) << 0) | + (uint32(b[offset+1]) << 8) | + (uint32(b[offset+2]) << 16) | + (uint32(b[offset+3]) << 24)), nil +} + +func (d *decoder) readInt64() int64 { + b := d.readBytes(8) + return int64((uint64(b[0]) << 0) | + (uint64(b[1]) << 8) | + (uint64(b[2]) << 16) | + (uint64(b[3]) << 24) | + (uint64(b[4]) << 32) | + (uint64(b[5]) << 40) | + (uint64(b[6]) << 48) | + (uint64(b[7]) << 56)) +} + +func (d *decoder) readByte() byte { + i := d.i + d.i++ + if d.i > len(d.in) { + corrupted() + } + return d.in[i] +} + +func (d *decoder) readBytes(length int32) []byte { + if length < 0 { + corrupted() + } + start := d.i + d.i += int(length) + if d.i < start || d.i > len(d.in) { + corrupted() + } + return d.in[start : start+int(length)] +} diff --git a/src/runtime/vendor/github.com/globalsign/mgo/bson/encode.go b/src/runtime/vendor/github.com/globalsign/mgo/bson/encode.go new file mode 100644 index 000000000..d0c6b2a85 --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/bson/encode.go @@ -0,0 +1,645 @@ +// BSON library for Go +// +// Copyright (c) 2010-2012 - Gustavo Niemeyer +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// gobson - BSON library for Go. + +package bson + +import ( + "encoding/json" + "fmt" + "math" + "net/url" + "reflect" + "sort" + "strconv" + "sync" + "time" +) + +// -------------------------------------------------------------------------- +// Some internal infrastructure. + +var ( + typeBinary = reflect.TypeOf(Binary{}) + typeObjectId = reflect.TypeOf(ObjectId("")) + typeDBPointer = reflect.TypeOf(DBPointer{"", ObjectId("")}) + typeSymbol = reflect.TypeOf(Symbol("")) + typeMongoTimestamp = reflect.TypeOf(MongoTimestamp(0)) + typeOrderKey = reflect.TypeOf(MinKey) + typeDocElem = reflect.TypeOf(DocElem{}) + typeRawDocElem = reflect.TypeOf(RawDocElem{}) + typeRaw = reflect.TypeOf(Raw{}) + typeRawPtr = reflect.PtrTo(reflect.TypeOf(Raw{})) + typeURL = reflect.TypeOf(url.URL{}) + typeTime = reflect.TypeOf(time.Time{}) + typeString = reflect.TypeOf("") + typeJSONNumber = reflect.TypeOf(json.Number("")) + typeTimeDuration = reflect.TypeOf(time.Duration(0)) +) + +var ( + // spec for []uint8 or []byte encoding + arrayOps = map[string]bool{ + "$in": true, + "$nin": true, + "$all": true, + } +) + +const itoaCacheSize = 32 + +const ( + getterUnknown = iota + getterNone + getterTypeVal + getterTypePtr + getterAddr +) + +var itoaCache []string + +var getterStyles map[reflect.Type]int +var getterIface reflect.Type +var getterMutex sync.RWMutex + +func init() { + itoaCache = make([]string, itoaCacheSize) + for i := 0; i != itoaCacheSize; i++ { + itoaCache[i] = strconv.Itoa(i) + } + var iface Getter + getterIface = reflect.TypeOf(&iface).Elem() + getterStyles = make(map[reflect.Type]int) +} + +func itoa(i int) string { + if i < itoaCacheSize { + return itoaCache[i] + } + return strconv.Itoa(i) +} + +func getterStyle(outt reflect.Type) int { + getterMutex.RLock() + style := getterStyles[outt] + getterMutex.RUnlock() + if style != getterUnknown { + return style + } + + getterMutex.Lock() + defer getterMutex.Unlock() + if outt.Implements(getterIface) { + vt := outt + for vt.Kind() == reflect.Ptr { + vt = vt.Elem() + } + if vt.Implements(getterIface) { + style = getterTypeVal + } else { + style = getterTypePtr + } + } else if reflect.PtrTo(outt).Implements(getterIface) { + style = getterAddr + } else { + style = getterNone + } + getterStyles[outt] = style + return style +} + +func getGetter(outt reflect.Type, out reflect.Value) Getter { + style := getterStyle(outt) + if style == getterNone { + return nil + } + if style == getterAddr { + if !out.CanAddr() { + return nil + } + return out.Addr().Interface().(Getter) + } + if style == getterTypeVal && out.Kind() == reflect.Ptr && out.IsNil() { + return nil + } + return out.Interface().(Getter) +} + +// -------------------------------------------------------------------------- +// Marshaling of the document value itself. + +type encoder struct { + out []byte +} + +func (e *encoder) addDoc(v reflect.Value) { + for { + if vi, ok := v.Interface().(Getter); ok { + getv, err := vi.GetBSON() + if err != nil { + panic(err) + } + v = reflect.ValueOf(getv) + continue + } + if v.Kind() == reflect.Ptr { + v = v.Elem() + continue + } + break + } + + if v.Type() == typeRaw { + raw := v.Interface().(Raw) + if raw.Kind != 0x03 && raw.Kind != 0x00 { + panic("Attempted to marshal Raw kind " + strconv.Itoa(int(raw.Kind)) + " as a document") + } + if len(raw.Data) == 0 { + panic("Attempted to marshal empty Raw document") + } + e.addBytes(raw.Data...) + return + } + + start := e.reserveInt32() + + switch v.Kind() { + case reflect.Map: + e.addMap(v) + case reflect.Struct: + e.addStruct(v) + case reflect.Array, reflect.Slice: + e.addSlice(v) + default: + panic("Can't marshal " + v.Type().String() + " as a BSON document") + } + + e.addBytes(0) + e.setInt32(start, int32(len(e.out)-start)) +} + +func (e *encoder) addMap(v reflect.Value) { + for _, k := range v.MapKeys() { + e.addElem(fmt.Sprint(k), v.MapIndex(k), false) + } +} + +func (e *encoder) addStruct(v reflect.Value) { + sinfo, err := getStructInfo(v.Type()) + if err != nil { + panic(err) + } + var value reflect.Value + if sinfo.InlineMap >= 0 { + m := v.Field(sinfo.InlineMap) + if m.Len() > 0 { + for _, k := range m.MapKeys() { + ks := k.String() + if _, found := sinfo.FieldsMap[ks]; found { + panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", ks)) + } + e.addElem(ks, m.MapIndex(k), false) + } + } + } + for _, info := range sinfo.FieldsList { + if info.Inline == nil { + value = v.Field(info.Num) + } else { + // as pointers to struct are allowed here, + // there is no guarantee that pointer won't be nil. + // + // It is expected allowed behaviour + // so info.Inline MAY consist index to a nil pointer + // and that is why we safely call v.FieldByIndex and just continue on panic + field, errField := safeFieldByIndex(v, info.Inline) + if errField != nil { + continue + } + + value = field + } + if info.OmitEmpty && isZero(value) { + continue + } + if useRespectNilValues && + (value.Kind() == reflect.Slice || value.Kind() == reflect.Map) && + value.IsNil() { + e.addElem(info.Key, reflect.ValueOf(nil), info.MinSize) + continue + } + e.addElem(info.Key, value, info.MinSize) + } +} + +func safeFieldByIndex(v reflect.Value, index []int) (result reflect.Value, err error) { + defer func() { + if recovered := recover(); recovered != nil { + switch r := recovered.(type) { + case string: + err = fmt.Errorf("%s", r) + case error: + err = r + } + } + }() + + result = v.FieldByIndex(index) + return +} + +func isZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.String: + return len(v.String()) == 0 + case reflect.Ptr, reflect.Interface: + return v.IsNil() + case reflect.Slice: + return v.Len() == 0 + case reflect.Map: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + if vt == typeTime { + return v.Interface().(time.Time).IsZero() + } + for i := 0; i < v.NumField(); i++ { + if vt.Field(i).PkgPath != "" && !vt.Field(i).Anonymous { + continue // Private field + } + if !isZero(v.Field(i)) { + return false + } + } + return true + } + return false +} + +func (e *encoder) addSlice(v reflect.Value) { + vi := v.Interface() + if d, ok := vi.(D); ok { + for _, elem := range d { + e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) + } + return + } + if d, ok := vi.(RawD); ok { + for _, elem := range d { + e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) + } + return + } + l := v.Len() + et := v.Type().Elem() + if et == typeDocElem { + for i := 0; i < l; i++ { + elem := v.Index(i).Interface().(DocElem) + e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) + } + return + } + if et == typeRawDocElem { + for i := 0; i < l; i++ { + elem := v.Index(i).Interface().(RawDocElem) + e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) + } + return + } + for i := 0; i < l; i++ { + e.addElem(itoa(i), v.Index(i), false) + } +} + +// -------------------------------------------------------------------------- +// Marshaling of elements in a document. + +func (e *encoder) addElemName(kind byte, name string) { + e.addBytes(kind) + e.addBytes([]byte(name)...) + e.addBytes(0) +} + +func (e *encoder) addElem(name string, v reflect.Value, minSize bool) { + + if !v.IsValid() { + e.addElemName(0x0A, name) + return + } + + if getter := getGetter(v.Type(), v); getter != nil { + getv, err := getter.GetBSON() + if err != nil { + panic(err) + } + e.addElem(name, reflect.ValueOf(getv), minSize) + return + } + + switch v.Kind() { + + case reflect.Interface: + e.addElem(name, v.Elem(), minSize) + + case reflect.Ptr: + e.addElem(name, v.Elem(), minSize) + + case reflect.String: + s := v.String() + switch v.Type() { + case typeObjectId: + if len(s) != 12 { + panic("ObjectIDs must be exactly 12 bytes long (got " + + strconv.Itoa(len(s)) + ")") + } + e.addElemName(0x07, name) + e.addBytes([]byte(s)...) + case typeSymbol: + e.addElemName(0x0E, name) + e.addStr(s) + case typeJSONNumber: + n := v.Interface().(json.Number) + if i, err := n.Int64(); err == nil { + e.addElemName(0x12, name) + e.addInt64(i) + } else if f, err := n.Float64(); err == nil { + e.addElemName(0x01, name) + e.addFloat64(f) + } else { + panic("failed to convert json.Number to a number: " + s) + } + default: + e.addElemName(0x02, name) + e.addStr(s) + } + + case reflect.Float32, reflect.Float64: + e.addElemName(0x01, name) + e.addFloat64(v.Float()) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + u := v.Uint() + if int64(u) < 0 { + panic("BSON has no uint64 type, and value is too large to fit correctly in an int64") + } else if u <= math.MaxInt32 && (minSize || v.Kind() <= reflect.Uint32) { + e.addElemName(0x10, name) + e.addInt32(int32(u)) + } else { + e.addElemName(0x12, name) + e.addInt64(int64(u)) + } + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch v.Type() { + case typeMongoTimestamp: + e.addElemName(0x11, name) + e.addInt64(v.Int()) + + case typeOrderKey: + if v.Int() == int64(MaxKey) { + e.addElemName(0x7F, name) + } else { + e.addElemName(0xFF, name) + } + case typeTimeDuration: + // Stored as int64 + e.addElemName(0x12, name) + + e.addInt64(int64(v.Int() / 1e6)) + default: + i := v.Int() + if (minSize || v.Type().Kind() != reflect.Int64) && i >= math.MinInt32 && i <= math.MaxInt32 { + // It fits into an int32, encode as such. + e.addElemName(0x10, name) + e.addInt32(int32(i)) + } else { + e.addElemName(0x12, name) + e.addInt64(i) + } + } + + case reflect.Bool: + e.addElemName(0x08, name) + if v.Bool() { + e.addBytes(1) + } else { + e.addBytes(0) + } + + case reflect.Map: + e.addElemName(0x03, name) + e.addDoc(v) + + case reflect.Slice: + vt := v.Type() + et := vt.Elem() + if et.Kind() == reflect.Uint8 { + if arrayOps[name] { + e.addElemName(0x04, name) + e.addDoc(v) + } else { + e.addElemName(0x05, name) + e.addBinary(0x00, v.Bytes()) + } + } else if et == typeDocElem || et == typeRawDocElem { + e.addElemName(0x03, name) + e.addDoc(v) + } else { + e.addElemName(0x04, name) + e.addDoc(v) + } + + case reflect.Array: + et := v.Type().Elem() + if et.Kind() == reflect.Uint8 { + if arrayOps[name] { + e.addElemName(0x04, name) + e.addDoc(v) + } else { + e.addElemName(0x05, name) + if v.CanAddr() { + e.addBinary(0x00, v.Slice(0, v.Len()).Interface().([]byte)) + } else { + n := v.Len() + e.addInt32(int32(n)) + e.addBytes(0x00) + for i := 0; i < n; i++ { + el := v.Index(i) + e.addBytes(byte(el.Uint())) + } + } + } + } else { + e.addElemName(0x04, name) + e.addDoc(v) + } + + case reflect.Struct: + switch s := v.Interface().(type) { + + case Raw: + kind := s.Kind + if kind == 0x00 { + kind = 0x03 + } + if len(s.Data) == 0 && kind != 0x06 && kind != 0x0A && kind != 0xFF && kind != 0x7F { + panic("Attempted to marshal empty Raw document") + } + e.addElemName(kind, name) + e.addBytes(s.Data...) + + case Binary: + e.addElemName(0x05, name) + e.addBinary(s.Kind, s.Data) + + case Decimal128: + e.addElemName(0x13, name) + e.addInt64(int64(s.l)) + e.addInt64(int64(s.h)) + + case DBPointer: + e.addElemName(0x0C, name) + e.addStr(s.Namespace) + if len(s.Id) != 12 { + panic("ObjectIDs must be exactly 12 bytes long (got " + + strconv.Itoa(len(s.Id)) + ")") + } + e.addBytes([]byte(s.Id)...) + + case RegEx: + e.addElemName(0x0B, name) + e.addCStr(s.Pattern) + options := runes(s.Options) + sort.Sort(options) + e.addCStr(string(options)) + + case JavaScript: + if s.Scope == nil { + e.addElemName(0x0D, name) + e.addStr(s.Code) + } else { + e.addElemName(0x0F, name) + start := e.reserveInt32() + e.addStr(s.Code) + e.addDoc(reflect.ValueOf(s.Scope)) + e.setInt32(start, int32(len(e.out)-start)) + } + + case time.Time: + // MongoDB handles timestamps as milliseconds. + e.addElemName(0x09, name) + e.addInt64(s.Unix()*1000 + int64(s.Nanosecond()/1e6)) + + case url.URL: + e.addElemName(0x02, name) + e.addStr(s.String()) + + case undefined: + e.addElemName(0x06, name) + + default: + e.addElemName(0x03, name) + e.addDoc(v) + } + + default: + panic("Can't marshal " + v.Type().String() + " in a BSON document") + } +} + +// ------------- +// Helper method for sorting regex options +type runes []rune + +func (a runes) Len() int { return len(a) } +func (a runes) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a runes) Less(i, j int) bool { return a[i] < a[j] } + +// -------------------------------------------------------------------------- +// Marshaling of base types. + +func (e *encoder) addBinary(subtype byte, v []byte) { + if subtype == 0x02 { + // Wonder how that brilliant idea came to life. Obsolete, luckily. + e.addInt32(int32(len(v) + 4)) + e.addBytes(subtype) + e.addInt32(int32(len(v))) + } else { + e.addInt32(int32(len(v))) + e.addBytes(subtype) + } + e.addBytes(v...) +} + +func (e *encoder) addStr(v string) { + e.addInt32(int32(len(v) + 1)) + e.addCStr(v) +} + +func (e *encoder) addCStr(v string) { + e.addBytes([]byte(v)...) + e.addBytes(0) +} + +func (e *encoder) reserveInt32() (pos int) { + pos = len(e.out) + e.addBytes(0, 0, 0, 0) + return pos +} + +func (e *encoder) setInt32(pos int, v int32) { + e.out[pos+0] = byte(v) + e.out[pos+1] = byte(v >> 8) + e.out[pos+2] = byte(v >> 16) + e.out[pos+3] = byte(v >> 24) +} + +func (e *encoder) addInt32(v int32) { + u := uint32(v) + e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24)) +} + +func (e *encoder) addInt64(v int64) { + u := uint64(v) + e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24), + byte(u>>32), byte(u>>40), byte(u>>48), byte(u>>56)) +} + +func (e *encoder) addFloat64(v float64) { + e.addInt64(int64(math.Float64bits(v))) +} + +func (e *encoder) addBytes(v ...byte) { + e.out = append(e.out, v...) +} diff --git a/src/runtime/vendor/github.com/globalsign/mgo/bson/json.go b/src/runtime/vendor/github.com/globalsign/mgo/bson/json.go new file mode 100644 index 000000000..045c71301 --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/bson/json.go @@ -0,0 +1,384 @@ +package bson + +import ( + "bytes" + "encoding/base64" + "fmt" + "strconv" + "strings" + "time" + + "github.com/globalsign/mgo/internal/json" +) + +// UnmarshalJSON unmarshals a JSON value that may hold non-standard +// syntax as defined in BSON's extended JSON specification. +func UnmarshalJSON(data []byte, value interface{}) error { + d := json.NewDecoder(bytes.NewBuffer(data)) + d.Extend(&jsonExt) + return d.Decode(value) +} + +// MarshalJSON marshals a JSON value that may hold non-standard +// syntax as defined in BSON's extended JSON specification. +func MarshalJSON(value interface{}) ([]byte, error) { + var buf bytes.Buffer + e := json.NewEncoder(&buf) + e.Extend(&jsonExt) + err := e.Encode(value) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// jdec is used internally by the JSON decoding functions +// so they may unmarshal functions without getting into endless +// recursion due to keyed objects. +func jdec(data []byte, value interface{}) error { + d := json.NewDecoder(bytes.NewBuffer(data)) + d.Extend(&funcExt) + return d.Decode(value) +} + +var jsonExt json.Extension +var funcExt json.Extension + +// TODO +// - Shell regular expressions ("/regexp/opts") + +func init() { + jsonExt.DecodeUnquotedKeys(true) + jsonExt.DecodeTrailingCommas(true) + + funcExt.DecodeFunc("BinData", "$binaryFunc", "$type", "$binary") + jsonExt.DecodeKeyed("$binary", jdecBinary) + jsonExt.DecodeKeyed("$binaryFunc", jdecBinary) + jsonExt.EncodeType([]byte(nil), jencBinarySlice) + jsonExt.EncodeType(Binary{}, jencBinaryType) + + funcExt.DecodeFunc("ISODate", "$dateFunc", "S") + funcExt.DecodeFunc("new Date", "$dateFunc", "S") + jsonExt.DecodeKeyed("$date", jdecDate) + jsonExt.DecodeKeyed("$dateFunc", jdecDate) + jsonExt.EncodeType(time.Time{}, jencDate) + + funcExt.DecodeFunc("Timestamp", "$timestamp", "t", "i") + jsonExt.DecodeKeyed("$timestamp", jdecTimestamp) + jsonExt.EncodeType(MongoTimestamp(0), jencTimestamp) + + funcExt.DecodeConst("undefined", Undefined) + + jsonExt.DecodeKeyed("$regex", jdecRegEx) + jsonExt.EncodeType(RegEx{}, jencRegEx) + + funcExt.DecodeFunc("ObjectId", "$oidFunc", "Id") + jsonExt.DecodeKeyed("$oid", jdecObjectId) + jsonExt.DecodeKeyed("$oidFunc", jdecObjectId) + jsonExt.EncodeType(ObjectId(""), jencObjectId) + + funcExt.DecodeFunc("DBRef", "$dbrefFunc", "$ref", "$id") + jsonExt.DecodeKeyed("$dbrefFunc", jdecDBRef) + + funcExt.DecodeFunc("NumberLong", "$numberLongFunc", "N") + jsonExt.DecodeKeyed("$numberLong", jdecNumberLong) + jsonExt.DecodeKeyed("$numberLongFunc", jdecNumberLong) + jsonExt.EncodeType(int64(0), jencNumberLong) + jsonExt.EncodeType(int(0), jencInt) + + funcExt.DecodeConst("MinKey", MinKey) + funcExt.DecodeConst("MaxKey", MaxKey) + jsonExt.DecodeKeyed("$minKey", jdecMinKey) + jsonExt.DecodeKeyed("$maxKey", jdecMaxKey) + jsonExt.EncodeType(orderKey(0), jencMinMaxKey) + + jsonExt.DecodeKeyed("$undefined", jdecUndefined) + jsonExt.EncodeType(Undefined, jencUndefined) + + jsonExt.Extend(&funcExt) +} + +func fbytes(format string, args ...interface{}) []byte { + var buf bytes.Buffer + fmt.Fprintf(&buf, format, args...) + return buf.Bytes() +} + +func jdecBinary(data []byte) (interface{}, error) { + var v struct { + Binary []byte `json:"$binary"` + Type string `json:"$type"` + Func struct { + Binary []byte `json:"$binary"` + Type int64 `json:"$type"` + } `json:"$binaryFunc"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + + var binData []byte + var binKind int64 + if v.Type == "" && v.Binary == nil { + binData = v.Func.Binary + binKind = v.Func.Type + } else if v.Type == "" { + return v.Binary, nil + } else { + binData = v.Binary + binKind, err = strconv.ParseInt(v.Type, 0, 64) + if err != nil { + binKind = -1 + } + } + + if binKind == 0 { + return binData, nil + } + if binKind < 0 || binKind > 255 { + return nil, fmt.Errorf("invalid type in binary object: %s", data) + } + + return Binary{Kind: byte(binKind), Data: binData}, nil +} + +func jencBinarySlice(v interface{}) ([]byte, error) { + in := v.([]byte) + out := make([]byte, base64.StdEncoding.EncodedLen(len(in))) + base64.StdEncoding.Encode(out, in) + return fbytes(`{"$binary":"%s","$type":"0x0"}`, out), nil +} + +func jencBinaryType(v interface{}) ([]byte, error) { + in := v.(Binary) + out := make([]byte, base64.StdEncoding.EncodedLen(len(in.Data))) + base64.StdEncoding.Encode(out, in.Data) + return fbytes(`{"$binary":"%s","$type":"0x%x"}`, out, in.Kind), nil +} + +const jdateFormat = "2006-01-02T15:04:05.999Z07:00" + +func jdecDate(data []byte) (interface{}, error) { + var v struct { + S string `json:"$date"` + Func struct { + S string + } `json:"$dateFunc"` + } + _ = jdec(data, &v) + if v.S == "" { + v.S = v.Func.S + } + if v.S != "" { + var errs []string + for _, format := range []string{jdateFormat, "2006-01-02"} { + t, err := time.Parse(format, v.S) + if err == nil { + return t, nil + } + errs = append(errs, err.Error()) + } + return nil, fmt.Errorf("cannot parse date: %q [%s]", v.S, strings.Join(errs, ", ")) + } + + var vn struct { + Date struct { + N int64 `json:"$numberLong,string"` + } `json:"$date"` + Func struct { + S int64 + } `json:"$dateFunc"` + } + err := jdec(data, &vn) + if err != nil { + return nil, fmt.Errorf("cannot parse date: %q", data) + } + n := vn.Date.N + if n == 0 { + n = vn.Func.S + } + return time.Unix(n/1000, n%1000*1e6).UTC(), nil +} + +func jencDate(v interface{}) ([]byte, error) { + t := v.(time.Time) + return fbytes(`{"$date":%q}`, t.Format(jdateFormat)), nil +} + +func jdecTimestamp(data []byte) (interface{}, error) { + var v struct { + Func struct { + T int32 `json:"t"` + I int32 `json:"i"` + } `json:"$timestamp"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + return MongoTimestamp(uint64(v.Func.T)<<32 | uint64(uint32(v.Func.I))), nil +} + +func jencTimestamp(v interface{}) ([]byte, error) { + ts := uint64(v.(MongoTimestamp)) + return fbytes(`{"$timestamp":{"t":%d,"i":%d}}`, ts>>32, uint32(ts)), nil +} + +func jdecRegEx(data []byte) (interface{}, error) { + var v struct { + Regex string `json:"$regex"` + Options string `json:"$options"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + return RegEx{v.Regex, v.Options}, nil +} + +func jencRegEx(v interface{}) ([]byte, error) { + re := v.(RegEx) + type regex struct { + Regex string `json:"$regex"` + Options string `json:"$options"` + } + return json.Marshal(regex{re.Pattern, re.Options}) +} + +func jdecObjectId(data []byte) (interface{}, error) { + var v struct { + Id string `json:"$oid"` + Func struct { + Id string + } `json:"$oidFunc"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + if v.Id == "" { + v.Id = v.Func.Id + } + return ObjectIdHex(v.Id), nil +} + +func jencObjectId(v interface{}) ([]byte, error) { + return fbytes(`{"$oid":"%s"}`, v.(ObjectId).Hex()), nil +} + +func jdecDBRef(data []byte) (interface{}, error) { + // TODO Support unmarshaling $ref and $id into the input value. + var v struct { + Obj map[string]interface{} `json:"$dbrefFunc"` + } + // TODO Fix this. Must not be required. + v.Obj = make(map[string]interface{}) + err := jdec(data, &v) + if err != nil { + return nil, err + } + return v.Obj, nil +} + +func jdecNumberLong(data []byte) (interface{}, error) { + var v struct { + N int64 `json:"$numberLong,string"` + Func struct { + N int64 `json:",string"` + } `json:"$numberLongFunc"` + } + var vn struct { + N int64 `json:"$numberLong"` + Func struct { + N int64 + } `json:"$numberLongFunc"` + } + err := jdec(data, &v) + if err != nil { + err = jdec(data, &vn) + v.N = vn.N + v.Func.N = vn.Func.N + } + if err != nil { + return nil, err + } + if v.N != 0 { + return v.N, nil + } + return v.Func.N, nil +} + +func jencNumberLong(v interface{}) ([]byte, error) { + n := v.(int64) + f := `{"$numberLong":"%d"}` + if n <= 1<<53 { + f = `{"$numberLong":%d}` + } + return fbytes(f, n), nil +} + +func jencInt(v interface{}) ([]byte, error) { + n := v.(int) + f := `{"$numberLong":"%d"}` + if int64(n) <= 1<<53 { + f = `%d` + } + return fbytes(f, n), nil +} + +func jdecMinKey(data []byte) (interface{}, error) { + var v struct { + N int64 `json:"$minKey"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + if v.N != 1 { + return nil, fmt.Errorf("invalid $minKey object: %s", data) + } + return MinKey, nil +} + +func jdecMaxKey(data []byte) (interface{}, error) { + var v struct { + N int64 `json:"$maxKey"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + if v.N != 1 { + return nil, fmt.Errorf("invalid $maxKey object: %s", data) + } + return MaxKey, nil +} + +func jencMinMaxKey(v interface{}) ([]byte, error) { + switch v.(orderKey) { + case MinKey: + return []byte(`{"$minKey":1}`), nil + case MaxKey: + return []byte(`{"$maxKey":1}`), nil + } + panic(fmt.Sprintf("invalid $minKey/$maxKey value: %d", v)) +} + +func jdecUndefined(data []byte) (interface{}, error) { + var v struct { + B bool `json:"$undefined"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + if !v.B { + return nil, fmt.Errorf("invalid $undefined object: %s", data) + } + return Undefined, nil +} + +func jencUndefined(v interface{}) ([]byte, error) { + return []byte(`{"$undefined":true}`), nil +} diff --git a/src/runtime/vendor/github.com/globalsign/mgo/bson/stream.go b/src/runtime/vendor/github.com/globalsign/mgo/bson/stream.go new file mode 100644 index 000000000..466528457 --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/bson/stream.go @@ -0,0 +1,90 @@ +package bson + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" +) + +const ( + // MinDocumentSize is the size of the smallest possible valid BSON document: + // an int32 size header + 0x00 (end of document). + MinDocumentSize = 5 + + // MaxDocumentSize is the largest possible size for a BSON document allowed by MongoDB, + // that is, 16 MiB (see https://docs.mongodb.com/manual/reference/limits/). + MaxDocumentSize = 16777216 +) + +// ErrInvalidDocumentSize is an error returned when a BSON document's header +// contains a size smaller than MinDocumentSize or greater than MaxDocumentSize. +type ErrInvalidDocumentSize struct { + DocumentSize int32 +} + +func (e ErrInvalidDocumentSize) Error() string { + return fmt.Sprintf("invalid document size %d", e.DocumentSize) +} + +// A Decoder reads and decodes BSON values from an input stream. +type Decoder struct { + source io.Reader +} + +// NewDecoder returns a new Decoder that reads from source. +// It does not add any extra buffering, and may not read data from source beyond the BSON values requested. +func NewDecoder(source io.Reader) *Decoder { + return &Decoder{source: source} +} + +// Decode reads the next BSON-encoded value from its input and stores it in the value pointed to by v. +// See the documentation for Unmarshal for details about the conversion of BSON into a Go value. +func (dec *Decoder) Decode(v interface{}) (err error) { + // BSON documents start with their size as a *signed* int32. + var docSize int32 + if err = binary.Read(dec.source, binary.LittleEndian, &docSize); err != nil { + return + } + + if docSize < MinDocumentSize || docSize > MaxDocumentSize { + return ErrInvalidDocumentSize{DocumentSize: docSize} + } + + docBuffer := bytes.NewBuffer(make([]byte, 0, docSize)) + if err = binary.Write(docBuffer, binary.LittleEndian, docSize); err != nil { + return + } + + // docSize is the *full* document's size (including the 4-byte size header, + // which has already been read). + if _, err = io.CopyN(docBuffer, dec.source, int64(docSize-4)); err != nil { + return + } + + // Let Unmarshal handle the rest. + defer handleErr(&err) + return Unmarshal(docBuffer.Bytes(), v) +} + +// An Encoder encodes and writes BSON values to an output stream. +type Encoder struct { + target io.Writer +} + +// NewEncoder returns a new Encoder that writes to target. +func NewEncoder(target io.Writer) *Encoder { + return &Encoder{target: target} +} + +// Encode encodes v to BSON, and if successful writes it to the Encoder's output stream. +// See the documentation for Marshal for details about the conversion of Go values to BSON. +func (enc *Encoder) Encode(v interface{}) error { + data, err := Marshal(v) + if err != nil { + return err + } + + _, err = enc.target.Write(data) + return err +} diff --git a/src/runtime/vendor/github.com/globalsign/mgo/internal/json/LICENSE b/src/runtime/vendor/github.com/globalsign/mgo/internal/json/LICENSE new file mode 100644 index 000000000..744875676 --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/internal/json/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/runtime/vendor/github.com/globalsign/mgo/internal/json/decode.go b/src/runtime/vendor/github.com/globalsign/mgo/internal/json/decode.go new file mode 100644 index 000000000..d5ca1f9a8 --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/internal/json/decode.go @@ -0,0 +1,1685 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Represents JSON data structure using native Go types: booleans, floats, +// strings, arrays, and maps. + +package json + +import ( + "bytes" + "encoding" + "encoding/base64" + "errors" + "fmt" + "reflect" + "runtime" + "strconv" + "unicode" + "unicode/utf16" + "unicode/utf8" +) + +// Unmarshal parses the JSON-encoded data and stores the result +// in the value pointed to by v. +// +// Unmarshal uses the inverse of the encodings that +// Marshal uses, allocating maps, slices, and pointers as necessary, +// with the following additional rules: +// +// To unmarshal JSON into a pointer, Unmarshal first handles the case of +// the JSON being the JSON literal null. In that case, Unmarshal sets +// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into +// the value pointed at by the pointer. If the pointer is nil, Unmarshal +// allocates a new value for it to point to. +// +// To unmarshal JSON into a struct, Unmarshal matches incoming object +// keys to the keys used by Marshal (either the struct field name or its tag), +// preferring an exact match but also accepting a case-insensitive match. +// Unmarshal will only set exported fields of the struct. +// +// To unmarshal JSON into an interface value, +// Unmarshal stores one of these in the interface value: +// +// bool, for JSON booleans +// float64, for JSON numbers +// string, for JSON strings +// []interface{}, for JSON arrays +// map[string]interface{}, for JSON objects +// nil for JSON null +// +// To unmarshal a JSON array into a slice, Unmarshal resets the slice length +// to zero and then appends each element to the slice. +// As a special case, to unmarshal an empty JSON array into a slice, +// Unmarshal replaces the slice with a new empty slice. +// +// To unmarshal a JSON array into a Go array, Unmarshal decodes +// JSON array elements into corresponding Go array elements. +// If the Go array is smaller than the JSON array, +// the additional JSON array elements are discarded. +// If the JSON array is smaller than the Go array, +// the additional Go array elements are set to zero values. +// +// To unmarshal a JSON object into a map, Unmarshal first establishes a map to +// use, If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal +// reuses the existing map, keeping existing entries. Unmarshal then stores key- +// value pairs from the JSON object into the map. The map's key type must +// either be a string or implement encoding.TextUnmarshaler. +// +// If a JSON value is not appropriate for a given target type, +// or if a JSON number overflows the target type, Unmarshal +// skips that field and completes the unmarshaling as best it can. +// If no more serious errors are encountered, Unmarshal returns +// an UnmarshalTypeError describing the earliest such error. +// +// The JSON null value unmarshals into an interface, map, pointer, or slice +// by setting that Go value to nil. Because null is often used in JSON to mean +// ``not present,'' unmarshaling a JSON null into any other Go type has no effect +// on the value and produces no error. +// +// When unmarshaling quoted strings, invalid UTF-8 or +// invalid UTF-16 surrogate pairs are not treated as an error. +// Instead, they are replaced by the Unicode replacement +// character U+FFFD. +// +func Unmarshal(data []byte, v interface{}) error { + // Check for well-formedness. + // Avoids filling out half a data structure + // before discovering a JSON syntax error. + var d decodeState + err := checkValid(data, &d.scan) + if err != nil { + return err + } + + d.init(data) + return d.unmarshal(v) +} + +// Unmarshaler is the interface implemented by types +// that can unmarshal a JSON description of themselves. +// The input can be assumed to be a valid encoding of +// a JSON value. UnmarshalJSON must copy the JSON data +// if it wishes to retain the data after returning. +type Unmarshaler interface { + UnmarshalJSON([]byte) error +} + +// An UnmarshalTypeError describes a JSON value that was +// not appropriate for a value of a specific Go type. +type UnmarshalTypeError struct { + Value string // description of JSON value - "bool", "array", "number -5" + Type reflect.Type // type of Go value it could not be assigned to + Offset int64 // error occurred after reading Offset bytes +} + +func (e *UnmarshalTypeError) Error() string { + return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() +} + +// An UnmarshalFieldError describes a JSON object key that +// led to an unexported (and therefore unwritable) struct field. +// (No longer used; kept for compatibility.) +type UnmarshalFieldError struct { + Key string + Type reflect.Type + Field reflect.StructField +} + +func (e *UnmarshalFieldError) Error() string { + return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() +} + +// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +// (The argument to Unmarshal must be a non-nil pointer.) +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) Error() string { + if e.Type == nil { + return "json: Unmarshal(nil)" + } + + if e.Type.Kind() != reflect.Ptr { + return "json: Unmarshal(non-pointer " + e.Type.String() + ")" + } + return "json: Unmarshal(nil " + e.Type.String() + ")" +} + +func (d *decodeState) unmarshal(v interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + err = r.(error) + } + }() + + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr || rv.IsNil() { + return &InvalidUnmarshalError{reflect.TypeOf(v)} + } + + d.scan.reset() + // We decode rv not rv.Elem because the Unmarshaler interface + // test must be applied at the top level of the value. + d.value(rv) + return d.savedError +} + +// A Number represents a JSON number literal. +type Number string + +// String returns the literal text of the number. +func (n Number) String() string { return string(n) } + +// Float64 returns the number as a float64. +func (n Number) Float64() (float64, error) { + return strconv.ParseFloat(string(n), 64) +} + +// Int64 returns the number as an int64. +func (n Number) Int64() (int64, error) { + return strconv.ParseInt(string(n), 10, 64) +} + +// isValidNumber reports whether s is a valid JSON number literal. +func isValidNumber(s string) bool { + // This function implements the JSON numbers grammar. + // See https://tools.ietf.org/html/rfc7159#section-6 + // and http://json.org/number.gif + + if s == "" { + return false + } + + // Optional - + if s[0] == '-' { + s = s[1:] + if s == "" { + return false + } + } + + // Digits + switch { + default: + return false + + case s[0] == '0': + s = s[1:] + + case '1' <= s[0] && s[0] <= '9': + s = s[1:] + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + } + } + + // . followed by 1 or more digits. + if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { + s = s[2:] + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + } + } + + // e or E followed by an optional - or + and + // 1 or more digits. + if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { + s = s[1:] + if s[0] == '+' || s[0] == '-' { + s = s[1:] + if s == "" { + return false + } + } + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + } + } + + // Make sure we are at the end. + return s == "" +} + +// decodeState represents the state while decoding a JSON value. +type decodeState struct { + data []byte + off int // read offset in data + scan scanner + nextscan scanner // for calls to nextValue + savedError error + useNumber bool + ext Extension +} + +// errPhase is used for errors that should not happen unless +// there is a bug in the JSON decoder or something is editing +// the data slice while the decoder executes. +var errPhase = errors.New("JSON decoder out of sync - data changing underfoot?") + +func (d *decodeState) init(data []byte) *decodeState { + d.data = data + d.off = 0 + d.savedError = nil + return d +} + +// error aborts the decoding by panicking with err. +func (d *decodeState) error(err error) { + panic(err) +} + +// saveError saves the first err it is called with, +// for reporting at the end of the unmarshal. +func (d *decodeState) saveError(err error) { + if d.savedError == nil { + d.savedError = err + } +} + +// next cuts off and returns the next full JSON value in d.data[d.off:]. +// The next value is known to be an object or array, not a literal. +func (d *decodeState) next() []byte { + c := d.data[d.off] + item, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // Our scanner has seen the opening brace/bracket + // and thinks we're still in the middle of the object. + // invent a closing brace/bracket to get it out. + if c == '{' { + d.scan.step(&d.scan, '}') + } else if c == '[' { + d.scan.step(&d.scan, ']') + } else { + // Was inside a function name. Get out of it. + d.scan.step(&d.scan, '(') + d.scan.step(&d.scan, ')') + } + + return item +} + +// scanWhile processes bytes in d.data[d.off:] until it +// receives a scan code not equal to op. +// It updates d.off and returns the new scan code. +func (d *decodeState) scanWhile(op int) int { + var newOp int + for { + if d.off >= len(d.data) { + newOp = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 + } else { + c := d.data[d.off] + d.off++ + newOp = d.scan.step(&d.scan, c) + } + if newOp != op { + break + } + } + return newOp +} + +// value decodes a JSON value from d.data[d.off:] into the value. +// it updates d.off to point past the decoded value. +func (d *decodeState) value(v reflect.Value) { + if !v.IsValid() { + _, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // d.scan thinks we're still at the beginning of the item. + // Feed in an empty string - the shortest, simplest value - + // so that it knows we got to the end of the value. + if d.scan.redo { + // rewind. + d.scan.redo = false + d.scan.step = stateBeginValue + } + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + + n := len(d.scan.parseState) + if n > 0 && d.scan.parseState[n-1] == parseObjectKey { + // d.scan thinks we just read an object key; finish the object + d.scan.step(&d.scan, ':') + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '}') + } + + return + } + + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(v) + + case scanBeginObject: + d.object(v) + + case scanBeginLiteral: + d.literal(v) + + case scanBeginName: + d.name(v) + } +} + +type unquotedValue struct{} + +// valueQuoted is like value but decodes a +// quoted string literal or literal null into an interface value. +// If it finds anything other than a quoted string literal or null, +// valueQuoted returns unquotedValue{}. +func (d *decodeState) valueQuoted() interface{} { + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(reflect.Value{}) + + case scanBeginObject: + d.object(reflect.Value{}) + + case scanBeginName: + switch v := d.nameInterface().(type) { + case nil, string: + return v + } + + case scanBeginLiteral: + switch v := d.literalInterface().(type) { + case nil, string: + return v + } + } + return unquotedValue{} +} + +// indirect walks down v allocating pointers as needed, +// until it gets to a non-pointer. +// if it encounters an Unmarshaler, indirect stops and returns that. +// if decodingNull is true, indirect stops at the last pointer so it can be set to nil. +func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { + // If v is a named type and is addressable, + // start with its address, so that if the type has pointer methods, + // we find them. + if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { + v = v.Addr() + } + for { + // Load value from interface, but only if the result will be + // usefully addressable. + if v.Kind() == reflect.Interface && !v.IsNil() { + e := v.Elem() + if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { + v = e + continue + } + } + + if v.Kind() != reflect.Ptr { + break + } + + if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { + break + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + if v.Type().NumMethod() > 0 { + if u, ok := v.Interface().(Unmarshaler); ok { + return u, nil, v + } + if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { + return nil, u, v + } + } + v = v.Elem() + } + return nil, nil, v +} + +// array consumes an array from d.data[d.off-1:], decoding into the value v. +// the first byte of the array ('[') has been read already. +func (d *decodeState) array(v reflect.Value) { + // Check for unmarshaler. + u, ut, pv := d.indirect(v, false) + if u != nil { + d.off-- + err := u.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) + d.off-- + d.next() + return + } + + v = pv + + // Check type of target. + switch v.Kind() { + case reflect.Interface: + if v.NumMethod() == 0 { + // Decoding into nil interface? Switch to non-reflect code. + v.Set(reflect.ValueOf(d.arrayInterface())) + return + } + // Otherwise it's invalid. + fallthrough + default: + d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) + d.off-- + d.next() + return + case reflect.Array: + case reflect.Slice: + break + } + + i := 0 + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + // Get element of array, growing if necessary. + if v.Kind() == reflect.Slice { + // Grow slice if necessary + if i >= v.Cap() { + newcap := v.Cap() + v.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newv := reflect.MakeSlice(v.Type(), v.Len(), newcap) + reflect.Copy(newv, v) + v.Set(newv) + } + if i >= v.Len() { + v.SetLen(i + 1) + } + } + + if i < v.Len() { + // Decode into element. + d.value(v.Index(i)) + } else { + // Ran out of fixed array: skip. + d.value(reflect.Value{}) + } + i++ + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + + if i < v.Len() { + if v.Kind() == reflect.Array { + // Array. Zero the rest. + z := reflect.Zero(v.Type().Elem()) + for ; i < v.Len(); i++ { + v.Index(i).Set(z) + } + } else { + v.SetLen(i) + } + } + if i == 0 && v.Kind() == reflect.Slice { + v.Set(reflect.MakeSlice(v.Type(), 0, 0)) + } +} + +var nullLiteral = []byte("null") +var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() + +// object consumes an object from d.data[d.off-1:], decoding into the value v. +// the first byte ('{') of the object has been read already. +func (d *decodeState) object(v reflect.Value) { + // Check for unmarshaler. + u, ut, pv := d.indirect(v, false) + if d.storeKeyed(pv) { + return + } + if u != nil { + d.off-- + err := u.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + if v.Kind() == reflect.Interface && v.NumMethod() == 0 { + v.Set(reflect.ValueOf(d.objectInterface())) + return + } + + // Check type of target: + // struct or + // map[string]T or map[encoding.TextUnmarshaler]T + switch v.Kind() { + case reflect.Map: + // Map key must either have string kind or be an encoding.TextUnmarshaler. + t := v.Type() + if t.Key().Kind() != reflect.String && + !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + if v.IsNil() { + v.Set(reflect.MakeMap(t)) + } + case reflect.Struct: + + default: + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + + var mapElem reflect.Value + + empty := true + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + if !empty && !d.ext.trailingCommas { + d.syntaxError("beginning of object key string") + } + break + } + empty = false + if op == scanBeginName { + if !d.ext.unquotedKeys { + d.syntaxError("beginning of object key string") + } + } else if op != scanBeginLiteral { + d.error(errPhase) + } + unquotedKey := op == scanBeginName + + // Read key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + var key []byte + if unquotedKey { + key = item + // TODO Fix code below to quote item when necessary. + } else { + var ok bool + key, ok = unquoteBytes(item) + if !ok { + d.error(errPhase) + } + } + + // Figure out field corresponding to key. + var subv reflect.Value + destring := false // whether the value is wrapped in a string to be decoded first + + if v.Kind() == reflect.Map { + elemType := v.Type().Elem() + if !mapElem.IsValid() { + mapElem = reflect.New(elemType).Elem() + } else { + mapElem.Set(reflect.Zero(elemType)) + } + subv = mapElem + } else { + var f *field + fields := cachedTypeFields(v.Type()) + for i := range fields { + ff := &fields[i] + if bytes.Equal(ff.nameBytes, key) { + f = ff + break + } + if f == nil && ff.equalFold(ff.nameBytes, key) { + f = ff + } + } + if f != nil { + subv = v + destring = f.quoted + for _, i := range f.index { + if subv.Kind() == reflect.Ptr { + if subv.IsNil() { + subv.Set(reflect.New(subv.Type().Elem())) + } + subv = subv.Elem() + } + subv = subv.Field(i) + } + } + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + if destring { + switch qv := d.valueQuoted().(type) { + case nil: + d.literalStore(nullLiteral, subv, false) + case string: + d.literalStore([]byte(qv), subv, true) + default: + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type())) + } + } else { + d.value(subv) + } + + // Write value back to map; + // if using struct, subv points into struct already. + if v.Kind() == reflect.Map { + kt := v.Type().Key() + var kv reflect.Value + switch { + case kt.Kind() == reflect.String: + kv = reflect.ValueOf(key).Convert(v.Type().Key()) + case reflect.PtrTo(kt).Implements(textUnmarshalerType): + kv = reflect.New(v.Type().Key()) + d.literalStore(item, kv, true) + kv = kv.Elem() + default: + panic("json: Unexpected key type") // should never occur + } + v.SetMapIndex(kv, subv) + } + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } +} + +// isNull returns whether there's a null literal at the provided offset. +func (d *decodeState) isNull(off int) bool { + if off+4 >= len(d.data) || d.data[off] != 'n' || d.data[off+1] != 'u' || d.data[off+2] != 'l' || d.data[off+3] != 'l' { + return false + } + d.nextscan.reset() + for i, c := range d.data[off:] { + if i > 4 { + return false + } + switch d.nextscan.step(&d.nextscan, c) { + case scanContinue, scanBeginName: + continue + } + break + } + return true +} + +// name consumes a const or function from d.data[d.off-1:], decoding into the value v. +// the first byte of the function name has been read already. +func (d *decodeState) name(v reflect.Value) { + if d.isNull(d.off - 1) { + d.literal(v) + return + } + + // Check for unmarshaler. + u, ut, pv := d.indirect(v, false) + if d.storeKeyed(pv) { + return + } + if u != nil { + d.off-- + err := u.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over function in input + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + if v.Kind() == reflect.Interface && v.NumMethod() == 0 { + out := d.nameInterface() + if out == nil { + v.Set(reflect.Zero(v.Type())) + } else { + v.Set(reflect.ValueOf(out)) + } + return + } + + nameStart := d.off - 1 + + op := d.scanWhile(scanContinue) + + name := d.data[nameStart : d.off-1] + if op != scanParam { + // Back up so the byte just read is consumed next. + d.off-- + d.scan.undo(op) + if l, ok := d.convertLiteral(name); ok { + d.storeValue(v, l) + return + } + d.error(&SyntaxError{fmt.Sprintf("json: unknown constant %q", name), int64(d.off)}) + } + + funcName := string(name) + funcData := d.ext.funcs[funcName] + if funcData.key == "" { + d.error(fmt.Errorf("json: unknown function %q", funcName)) + } + + // Check type of target: + // struct or + // map[string]T or map[encoding.TextUnmarshaler]T + switch v.Kind() { + case reflect.Map: + // Map key must either have string kind or be an encoding.TextUnmarshaler. + t := v.Type() + if t.Key().Kind() != reflect.String && + !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + if v.IsNil() { + v.Set(reflect.MakeMap(t)) + } + case reflect.Struct: + + default: + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + + // TODO Fix case of func field as map. + //topv := v + + // Figure out field corresponding to function. + key := []byte(funcData.key) + if v.Kind() == reflect.Map { + elemType := v.Type().Elem() + v = reflect.New(elemType).Elem() + } else { + var f *field + fields := cachedTypeFields(v.Type()) + for i := range fields { + ff := &fields[i] + if bytes.Equal(ff.nameBytes, key) { + f = ff + break + } + if f == nil && ff.equalFold(ff.nameBytes, key) { + f = ff + } + } + if f != nil { + for _, i := range f.index { + if v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + v = v.Field(i) + } + if v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + } + } + + // Check for unmarshaler on func field itself. + u, _, _ = d.indirect(v, false) + if u != nil { + d.off = nameStart + err := u.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + + var mapElem reflect.Value + + // Parse function arguments. + for i := 0; ; i++ { + // closing ) - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndParams { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + if i >= len(funcData.args) { + d.error(fmt.Errorf("json: too many arguments for function %s", funcName)) + } + key := []byte(funcData.args[i]) + + // Figure out field corresponding to key. + var subv reflect.Value + destring := false // whether the value is wrapped in a string to be decoded first + + if v.Kind() == reflect.Map { + elemType := v.Type().Elem() + if !mapElem.IsValid() { + mapElem = reflect.New(elemType).Elem() + } else { + mapElem.Set(reflect.Zero(elemType)) + } + subv = mapElem + } else { + var f *field + fields := cachedTypeFields(v.Type()) + for i := range fields { + ff := &fields[i] + if bytes.Equal(ff.nameBytes, key) { + f = ff + break + } + if f == nil && ff.equalFold(ff.nameBytes, key) { + f = ff + } + } + if f != nil { + subv = v + destring = f.quoted + for _, i := range f.index { + if subv.Kind() == reflect.Ptr { + if subv.IsNil() { + subv.Set(reflect.New(subv.Type().Elem())) + } + subv = subv.Elem() + } + subv = subv.Field(i) + } + } + } + + // Read value. + if destring { + switch qv := d.valueQuoted().(type) { + case nil: + d.literalStore(nullLiteral, subv, false) + case string: + d.literalStore([]byte(qv), subv, true) + default: + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type())) + } + } else { + d.value(subv) + } + + // Write value back to map; + // if using struct, subv points into struct already. + if v.Kind() == reflect.Map { + kt := v.Type().Key() + var kv reflect.Value + switch { + case kt.Kind() == reflect.String: + kv = reflect.ValueOf(key).Convert(v.Type().Key()) + case reflect.PtrTo(kt).Implements(textUnmarshalerType): + kv = reflect.New(v.Type().Key()) + d.literalStore(key, kv, true) + kv = kv.Elem() + default: + panic("json: Unexpected key type") // should never occur + } + v.SetMapIndex(kv, subv) + } + + // Next token must be , or ). + op = d.scanWhile(scanSkipSpace) + if op == scanEndParams { + break + } + if op != scanParam { + d.error(errPhase) + } + } +} + +// keyed attempts to decode an object or function using a keyed doc extension, +// and returns the value and true on success, or nil and false otherwise. +func (d *decodeState) keyed() (interface{}, bool) { + if len(d.ext.keyed) == 0 { + return nil, false + } + + unquote := false + + // Look-ahead first key to check for a keyed document extension. + d.nextscan.reset() + var start, end int + for i, c := range d.data[d.off-1:] { + switch op := d.nextscan.step(&d.nextscan, c); op { + case scanSkipSpace, scanContinue, scanBeginObject: + continue + case scanBeginLiteral, scanBeginName: + unquote = op == scanBeginLiteral + start = i + continue + } + end = i + break + } + + name := bytes.Trim(d.data[d.off-1+start:d.off-1+end], " \n\t") + + var key []byte + var ok bool + if unquote { + key, ok = unquoteBytes(name) + if !ok { + d.error(errPhase) + } + } else { + funcData, ok := d.ext.funcs[string(name)] + if !ok { + return nil, false + } + key = []byte(funcData.key) + } + + decode, ok := d.ext.keyed[string(key)] + if !ok { + return nil, false + } + + d.off-- + out, err := decode(d.next()) + if err != nil { + d.error(err) + } + return out, true +} + +func (d *decodeState) storeKeyed(v reflect.Value) bool { + keyed, ok := d.keyed() + if !ok { + return false + } + d.storeValue(v, keyed) + return true +} + +var ( + trueBytes = []byte("true") + falseBytes = []byte("false") + nullBytes = []byte("null") +) + +func (d *decodeState) storeValue(v reflect.Value, from interface{}) { + switch from { + case nil: + d.literalStore(nullBytes, v, false) + return + case true: + d.literalStore(trueBytes, v, false) + return + case false: + d.literalStore(falseBytes, v, false) + return + } + fromv := reflect.ValueOf(from) + for fromv.Kind() == reflect.Ptr && !fromv.IsNil() { + fromv = fromv.Elem() + } + fromt := fromv.Type() + for v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + vt := v.Type() + if fromt.AssignableTo(vt) { + v.Set(fromv) + } else if fromt.ConvertibleTo(vt) { + v.Set(fromv.Convert(vt)) + } else { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + } +} + +func (d *decodeState) convertLiteral(name []byte) (interface{}, bool) { + if len(name) == 0 { + return nil, false + } + switch name[0] { + case 't': + if bytes.Equal(name, trueBytes) { + return true, true + } + case 'f': + if bytes.Equal(name, falseBytes) { + return false, true + } + case 'n': + if bytes.Equal(name, nullBytes) { + return nil, true + } + } + if l, ok := d.ext.consts[string(name)]; ok { + return l, true + } + return nil, false +} + +// literal consumes a literal from d.data[d.off-1:], decoding into the value v. +// The first byte of the literal has been read already +// (that's how the caller knows it's a literal). +func (d *decodeState) literal(v reflect.Value) { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + + d.literalStore(d.data[start:d.off], v, false) +} + +// convertNumber converts the number literal s to a float64 or a Number +// depending on the setting of d.useNumber. +func (d *decodeState) convertNumber(s string) (interface{}, error) { + if d.useNumber { + return Number(s), nil + } + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} + } + return f, nil +} + +var numberType = reflect.TypeOf(Number("")) + +// literalStore decodes a literal stored in item into v. +// +// fromQuoted indicates whether this literal came from unwrapping a +// string from the ",string" struct tag option. this is used only to +// produce more helpful error messages. +func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) { + // Check for unmarshaler. + if len(item) == 0 { + //Empty string given + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + return + } + wantptr := item[0] == 'n' // null + u, ut, pv := d.indirect(v, wantptr) + if u != nil { + err := u.UnmarshalJSON(item) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + if item[0] != '"' { + if fromQuoted { + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + } + return + } + s, ok := unquoteBytes(item) + if !ok { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + err := ut.UnmarshalText(s) + if err != nil { + d.error(err) + } + return + } + + v = pv + + switch c := item[0]; c { + case 'n': // null + switch v.Kind() { + case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: + v.Set(reflect.Zero(v.Type())) + // otherwise, ignore null for primitives/string + } + case 't', 'f': // true, false + value := c == 't' + switch v.Kind() { + default: + if fromQuoted { + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) + } + case reflect.Bool: + v.SetBool(value) + case reflect.Interface: + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(value)) + } else { + d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) + } + } + + case '"': // string + s, ok := unquoteBytes(item) + if !ok { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + case reflect.Slice: + if v.Type().Elem().Kind() != reflect.Uint8 { + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + break + } + b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) + n, err := base64.StdEncoding.Decode(b, s) + if err != nil { + d.saveError(err) + break + } + v.SetBytes(b[:n]) + case reflect.String: + v.SetString(string(s)) + case reflect.Interface: + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(string(s))) + } else { + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + } + } + + default: // number + if c != '-' && (c < '0' || c > '9') { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + s := string(item) + switch v.Kind() { + default: + if v.Kind() == reflect.String && v.Type() == numberType { + v.SetString(s) + if !isValidNumber(s) { + d.error(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item)) + } + break + } + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) + } + case reflect.Interface: + n, err := d.convertNumber(s) + if err != nil { + d.saveError(err) + break + } + if v.NumMethod() != 0 { + d.saveError(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) + break + } + v.Set(reflect.ValueOf(n)) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, err := strconv.ParseInt(s, 10, 64) + if err != nil || v.OverflowInt(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetInt(n) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n, err := strconv.ParseUint(s, 10, 64) + if err != nil || v.OverflowUint(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetUint(n) + + case reflect.Float32, reflect.Float64: + n, err := strconv.ParseFloat(s, v.Type().Bits()) + if err != nil || v.OverflowFloat(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetFloat(n) + } + } +} + +// The xxxInterface routines build up a value to be stored +// in an empty interface. They are not strictly necessary, +// but they avoid the weight of reflection in this common case. + +// valueInterface is like value but returns interface{} +func (d *decodeState) valueInterface() interface{} { + switch d.scanWhile(scanSkipSpace) { + default: + d.error(errPhase) + panic("unreachable") + case scanBeginArray: + return d.arrayInterface() + case scanBeginObject: + return d.objectInterface() + case scanBeginLiteral: + return d.literalInterface() + case scanBeginName: + return d.nameInterface() + } +} + +func (d *decodeState) syntaxError(expected string) { + msg := fmt.Sprintf("invalid character '%c' looking for %s", d.data[d.off-1], expected) + d.error(&SyntaxError{msg, int64(d.off)}) +} + +// arrayInterface is like array but returns []interface{}. +func (d *decodeState) arrayInterface() []interface{} { + var v = make([]interface{}, 0) + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + if len(v) > 0 && !d.ext.trailingCommas { + d.syntaxError("beginning of value") + } + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + v = append(v, d.valueInterface()) + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + return v +} + +// objectInterface is like object but returns map[string]interface{}. +func (d *decodeState) objectInterface() interface{} { + v, ok := d.keyed() + if ok { + return v + } + + m := make(map[string]interface{}) + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + if len(m) > 0 && !d.ext.trailingCommas { + d.syntaxError("beginning of object key string") + } + break + } + if op == scanBeginName { + if !d.ext.unquotedKeys { + d.syntaxError("beginning of object key string") + } + } else if op != scanBeginLiteral { + d.error(errPhase) + } + unquotedKey := op == scanBeginName + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + var key string + if unquotedKey { + key = string(item) + } else { + var ok bool + key, ok = unquote(item) + if !ok { + d.error(errPhase) + } + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + m[key] = d.valueInterface() + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } + return m +} + +// literalInterface is like literal but returns an interface value. +func (d *decodeState) literalInterface() interface{} { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + switch c := item[0]; c { + case 'n': // null + return nil + + case 't', 'f': // true, false + return c == 't' + + case '"': // string + s, ok := unquote(item) + if !ok { + d.error(errPhase) + } + return s + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + n, err := d.convertNumber(string(item)) + if err != nil { + d.saveError(err) + } + return n + } +} + +// nameInterface is like function but returns map[string]interface{}. +func (d *decodeState) nameInterface() interface{} { + v, ok := d.keyed() + if ok { + return v + } + + nameStart := d.off - 1 + + op := d.scanWhile(scanContinue) + + name := d.data[nameStart : d.off-1] + if op != scanParam { + // Back up so the byte just read is consumed next. + d.off-- + d.scan.undo(op) + if l, ok := d.convertLiteral(name); ok { + return l + } + d.error(&SyntaxError{fmt.Sprintf("json: unknown constant %q", name), int64(d.off)}) + } + + funcName := string(name) + funcData := d.ext.funcs[funcName] + if funcData.key == "" { + d.error(fmt.Errorf("json: unknown function %q", funcName)) + } + + m := make(map[string]interface{}) + for i := 0; ; i++ { + // Look ahead for ) - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndParams { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + if i >= len(funcData.args) { + d.error(fmt.Errorf("json: too many arguments for function %s", funcName)) + } + m[funcData.args[i]] = d.valueInterface() + + // Next token must be , or ). + op = d.scanWhile(scanSkipSpace) + if op == scanEndParams { + break + } + if op != scanParam { + d.error(errPhase) + } + } + return map[string]interface{}{funcData.key: m} +} + +// getu4 decodes \uXXXX from the beginning of s, returning the hex value, +// or it returns -1. +func getu4(s []byte) rune { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + r, err := strconv.ParseUint(string(s[2:6]), 16, 64) + if err != nil { + return -1 + } + return rune(r) +} + +// unquote converts a quoted JSON string literal s into an actual string t. +// The rules are different than for Go, so cannot use strconv.Unquote. +func unquote(s []byte) (t string, ok bool) { + s, ok = unquoteBytes(s) + t = string(s) + return +} + +func unquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + + // Check for unusual characters. If there are none, + // then no unquoting is needed, so return a slice of the + // original bytes. + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rr, size := utf8.DecodeRune(s[r:]) + if rr == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { + return s, true + } + + b := make([]byte, len(s)+2*utf8.UTFMax) + w := copy(b, s[0:r]) + for r < len(s) { + // Out of room? Can only happen if s is full of + // malformed UTF-8 and we're replacing each + // byte with RuneError. + if w >= len(b)-2*utf8.UTFMax { + nb := make([]byte, (len(b)+utf8.UTFMax)*2) + copy(nb, b[0:w]) + b = nb + } + switch c := s[r]; { + case c == '\\': + r++ + if r >= len(s) { + return + } + switch s[r] { + default: + return + case '"', '\\', '/', '\'': + b[w] = s[r] + r++ + w++ + case 'b': + b[w] = '\b' + r++ + w++ + case 'f': + b[w] = '\f' + r++ + w++ + case 'n': + b[w] = '\n' + r++ + w++ + case 'r': + b[w] = '\r' + r++ + w++ + case 't': + b[w] = '\t' + r++ + w++ + case 'u': + r-- + rr := getu4(s[r:]) + if rr < 0 { + return + } + r += 6 + if utf16.IsSurrogate(rr) { + rr1 := getu4(s[r:]) + if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { + // A valid pair; consume. + r += 6 + w += utf8.EncodeRune(b[w:], dec) + break + } + // Invalid surrogate; fall back to replacement rune. + rr = unicode.ReplacementChar + } + w += utf8.EncodeRune(b[w:], rr) + } + + // Quote, control characters are invalid. + case c == '"', c < ' ': + return + + // ASCII + case c < utf8.RuneSelf: + b[w] = c + r++ + w++ + + // Coerce to well-formed UTF-8. + default: + rr, size := utf8.DecodeRune(s[r:]) + r += size + w += utf8.EncodeRune(b[w:], rr) + } + } + return b[0:w], true +} diff --git a/src/runtime/vendor/github.com/globalsign/mgo/internal/json/encode.go b/src/runtime/vendor/github.com/globalsign/mgo/internal/json/encode.go new file mode 100644 index 000000000..e4b8f8648 --- /dev/null +++ b/src/runtime/vendor/github.com/globalsign/mgo/internal/json/encode.go @@ -0,0 +1,1260 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package json implements encoding and decoding of JSON as defined in +// RFC 4627. The mapping between JSON and Go values is described +// in the documentation for the Marshal and Unmarshal functions. +// +// See "JSON and Go" for an introduction to this package: +// https://golang.org/doc/articles/json_and_go.html +package json + +import ( + "bytes" + "encoding" + "encoding/base64" + "fmt" + "math" + "reflect" + "runtime" + "sort" + "strconv" + "strings" + "sync" + "unicode" + "unicode/utf8" +) + +// Marshal returns the JSON encoding of v. +// +// Marshal traverses the value v recursively. +// If an encountered value implements the Marshaler interface +// and is not a nil pointer, Marshal calls its MarshalJSON method +// to produce JSON. If no MarshalJSON method is present but the +// value implements encoding.TextMarshaler instead, Marshal calls +// its MarshalText method. +// The nil pointer exception is not strictly necessary +// but mimics a similar, necessary exception in the behavior of +// UnmarshalJSON. +// +// Otherwise, Marshal uses the following type-dependent default encodings: +// +// Boolean values encode as JSON booleans. +// +// Floating point, integer, and Number values encode as JSON numbers. +// +// String values encode as JSON strings coerced to valid UTF-8, +// replacing invalid bytes with the Unicode replacement rune. +// The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e" +// to keep some browsers from misinterpreting JSON output as HTML. +// Ampersand "&" is also escaped to "\u0026" for the same reason. +// This escaping can be disabled using an Encoder with DisableHTMLEscaping. +// +// Array and slice values encode as JSON arrays, except that +// []byte encodes as a base64-encoded string, and a nil slice +// encodes as the null JSON value. +// +// Struct values encode as JSON objects. Each exported struct field +// becomes a member of the object unless +// - the field's tag is "-", or +// - the field is empty and its tag specifies the "omitempty" option. +// The empty values are false, 0, any +// nil pointer or interface value, and any array, slice, map, or string of +// length zero. The object's default key string is the struct field name +// but can be specified in the struct field's tag value. The "json" key in +// the struct field's tag value is the key name, followed by an optional comma +// and options. Examples: +// +// // Field is ignored by this package. +// Field int `json:"-"` +// +// // Field appears in JSON as key "myName". +// Field int `json:"myName"` +// +// // Field appears in JSON as key "myName" and +// // the field is omitted from the object if its value is empty, +// // as defined above. +// Field int `json:"myName,omitempty"` +// +// // Field appears in JSON as key "Field" (the default), but +// // the field is skipped if empty. +// // Note the leading comma. +// Field int `json:",omitempty"` +// +// The "string" option signals that a field is stored as JSON inside a +// JSON-encoded string. It applies only to fields of string, floating point, +// integer, or boolean types. This extra level of encoding is sometimes used +// when communicating with JavaScript programs: +// +// Int64String int64 `json:",string"` +// +// The key name will be used if it's a non-empty string consisting of +// only Unicode letters, digits, dollar signs, percent signs, hyphens, +// underscores and slashes. +// +// Anonymous struct fields are usually marshaled as if their inner exported fields +// were fields in the outer struct, subject to the usual Go visibility rules amended +// as described in the next paragraph. +// An anonymous struct field with a name given in its JSON tag is treated as +// having that name, rather than being anonymous. +// An anonymous struct field of interface type is treated the same as having +// that type as its name, rather than being anonymous. +// +// The Go visibility rules for struct fields are amended for JSON when +// deciding which field to marshal or unmarshal. If there are +// multiple fields at the same level, and that level is the least +// nested (and would therefore be the nesting level selected by the +// usual Go rules), the following extra rules apply: +// +// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered, +// even if there are multiple untagged fields that would otherwise conflict. +// 2) If there is exactly one field (tagged or not according to the first rule), that is selected. +// 3) Otherwise there are multiple fields, and all are ignored; no error occurs. +// +// Handling of anonymous struct fields is new in Go 1.1. +// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of +// an anonymous struct field in both current and earlier versions, give the field +// a JSON tag of "-". +// +// Map values encode as JSON objects. The map's key type must either be a string +// or implement encoding.TextMarshaler. The map keys are used as JSON object +// keys, subject to the UTF-8 coercion described for string values above. +// +// Pointer values encode as the value pointed to. +// A nil pointer encodes as the null JSON value. +// +// Interface values encode as the value contained in the interface. +// A nil interface value encodes as the null JSON value. +// +// Channel, complex, and function values cannot be encoded in JSON. +// Attempting to encode such a value causes Marshal to return +// an UnsupportedTypeError. +// +// JSON cannot represent cyclic data structures and Marshal does not +// handle them. Passing cyclic structures to Marshal will result in +// an infinite recursion. +// +func Marshal(v interface{}) ([]byte, error) { + e := &encodeState{} + err := e.marshal(v, encOpts{escapeHTML: true}) + if err != nil { + return nil, err + } + return e.Bytes(), nil +} + +// MarshalIndent is like Marshal but applies Indent to format the output. +func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { + b, err := Marshal(v) + if err != nil { + return nil, err + } + var buf bytes.Buffer + err = Indent(&buf, b, prefix, indent) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 +// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 +// so that the JSON will be safe to embed inside HTML + + +` +) diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/middleware/request.go b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/request.go new file mode 100644 index 000000000..ee725f587 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/request.go @@ -0,0 +1,104 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 middleware + +import ( + "net/http" + "reflect" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +// RequestBinder binds and validates the data from a http request +type untypedRequestBinder struct { + Spec *spec.Swagger + Parameters map[string]spec.Parameter + Formats strfmt.Registry + paramBinders map[string]*untypedParamBinder +} + +// NewRequestBinder creates a new binder for reading a request. +func newUntypedRequestBinder(parameters map[string]spec.Parameter, spec *spec.Swagger, formats strfmt.Registry) *untypedRequestBinder { + binders := make(map[string]*untypedParamBinder) + for fieldName, param := range parameters { + binders[fieldName] = newUntypedParamBinder(param, spec, formats) + } + return &untypedRequestBinder{ + Parameters: parameters, + paramBinders: binders, + Spec: spec, + Formats: formats, + } +} + +// Bind perform the databinding and validation +func (o *untypedRequestBinder) Bind(request *http.Request, routeParams RouteParams, consumer runtime.Consumer, data interface{}) error { + val := reflect.Indirect(reflect.ValueOf(data)) + isMap := val.Kind() == reflect.Map + var result []error + debugLog("binding %d parameters for %s %s", len(o.Parameters), request.Method, request.URL.EscapedPath()) + for fieldName, param := range o.Parameters { + binder := o.paramBinders[fieldName] + debugLog("binding parameter %s for %s %s", fieldName, request.Method, request.URL.EscapedPath()) + var target reflect.Value + if !isMap { + binder.Name = fieldName + target = val.FieldByName(fieldName) + } + + if isMap { + tpe := binder.Type() + if tpe == nil { + if param.Schema.Type.Contains("array") { + tpe = reflect.TypeOf([]interface{}{}) + } else { + tpe = reflect.TypeOf(map[string]interface{}{}) + } + } + target = reflect.Indirect(reflect.New(tpe)) + + } + + if !target.IsValid() { + result = append(result, errors.New(500, "parameter name %q is an unknown field", binder.Name)) + continue + } + + if err := binder.Bind(request, routeParams, consumer, target); err != nil { + result = append(result, err) + continue + } + + if binder.validator != nil { + rr := binder.validator.Validate(target.Interface()) + if rr != nil && rr.HasErrors() { + result = append(result, rr.AsError()) + } + } + + if isMap { + val.SetMapIndex(reflect.ValueOf(param.Name), target) + } + } + + if len(result) > 0 { + return errors.CompositeValidationError(result...) + } + + return nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/middleware/router.go b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/router.go new file mode 100644 index 000000000..73c8eebb3 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/router.go @@ -0,0 +1,478 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 middleware + +import ( + "fmt" + "net/http" + fpath "path" + "regexp" + "strings" + + "github.com/go-openapi/runtime/security" + + "github.com/go-openapi/analysis" + "github.com/go-openapi/errors" + "github.com/go-openapi/loads" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware/denco" + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +// RouteParam is a object to capture route params in a framework agnostic way. +// implementations of the muxer should use these route params to communicate with the +// swagger framework +type RouteParam struct { + Name string + Value string +} + +// RouteParams the collection of route params +type RouteParams []RouteParam + +// Get gets the value for the route param for the specified key +func (r RouteParams) Get(name string) string { + vv, _, _ := r.GetOK(name) + if len(vv) > 0 { + return vv[len(vv)-1] + } + return "" +} + +// GetOK gets the value but also returns booleans to indicate if a key or value +// is present. This aids in validation and satisfies an interface in use there +// +// The returned values are: data, has key, has value +func (r RouteParams) GetOK(name string) ([]string, bool, bool) { + for _, p := range r { + if p.Name == name { + return []string{p.Value}, true, p.Value != "" + } + } + return nil, false, false +} + +// NewRouter creates a new context aware router middleware +func NewRouter(ctx *Context, next http.Handler) http.Handler { + if ctx.router == nil { + ctx.router = DefaultRouter(ctx.spec, ctx.api) + } + + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if _, rCtx, ok := ctx.RouteInfo(r); ok { + next.ServeHTTP(rw, rCtx) + return + } + + // Not found, check if it exists in the other methods first + if others := ctx.AllowedMethods(r); len(others) > 0 { + ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.MethodNotAllowed(r.Method, others)) + return + } + + ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.NotFound("path %s was not found", r.URL.EscapedPath())) + }) +} + +// RoutableAPI represents an interface for things that can serve +// as a provider of implementations for the swagger router +type RoutableAPI interface { + HandlerFor(string, string) (http.Handler, bool) + ServeErrorFor(string) func(http.ResponseWriter, *http.Request, error) + ConsumersFor([]string) map[string]runtime.Consumer + ProducersFor([]string) map[string]runtime.Producer + AuthenticatorsFor(map[string]spec.SecurityScheme) map[string]runtime.Authenticator + Authorizer() runtime.Authorizer + Formats() strfmt.Registry + DefaultProduces() string + DefaultConsumes() string +} + +// Router represents a swagger aware router +type Router interface { + Lookup(method, path string) (*MatchedRoute, bool) + OtherMethods(method, path string) []string +} + +type defaultRouteBuilder struct { + spec *loads.Document + analyzer *analysis.Spec + api RoutableAPI + records map[string][]denco.Record +} + +type defaultRouter struct { + spec *loads.Document + routers map[string]*denco.Router +} + +func newDefaultRouteBuilder(spec *loads.Document, api RoutableAPI) *defaultRouteBuilder { + return &defaultRouteBuilder{ + spec: spec, + analyzer: analysis.New(spec.Spec()), + api: api, + records: make(map[string][]denco.Record), + } +} + +// DefaultRouter creates a default implemenation of the router +func DefaultRouter(spec *loads.Document, api RoutableAPI) Router { + builder := newDefaultRouteBuilder(spec, api) + if spec != nil { + for method, paths := range builder.analyzer.Operations() { + for path, operation := range paths { + fp := fpath.Join(spec.BasePath(), path) + debugLog("adding route %s %s %q", method, fp, operation.ID) + builder.AddRoute(method, fp, operation) + } + } + } + return builder.Build() +} + +// RouteAuthenticator is an authenticator that can compose several authenticators together. +// It also knows when it contains an authenticator that allows for anonymous pass through. +// Contains a group of 1 or more authenticators that have a logical AND relationship +type RouteAuthenticator struct { + Authenticator map[string]runtime.Authenticator + Schemes []string + Scopes map[string][]string + allScopes []string + commonScopes []string + allowAnonymous bool +} + +func (ra *RouteAuthenticator) AllowsAnonymous() bool { + return ra.allowAnonymous +} + +// AllScopes returns a list of unique scopes that is the combination +// of all the scopes in the requirements +func (ra *RouteAuthenticator) AllScopes() []string { + return ra.allScopes +} + +// CommonScopes returns a list of unique scopes that are common in all the +// scopes in the requirements +func (ra *RouteAuthenticator) CommonScopes() []string { + return ra.commonScopes +} + +// Authenticate Authenticator interface implementation +func (ra *RouteAuthenticator) Authenticate(req *http.Request, route *MatchedRoute) (bool, interface{}, error) { + if ra.allowAnonymous { + route.Authenticator = ra + return true, nil, nil + } + // iterate in proper order + var lastResult interface{} + for _, scheme := range ra.Schemes { + if authenticator, ok := ra.Authenticator[scheme]; ok { + applies, princ, err := authenticator.Authenticate(&security.ScopedAuthRequest{ + Request: req, + RequiredScopes: ra.Scopes[scheme], + }) + if !applies { + return false, nil, nil + } + + if err != nil { + route.Authenticator = ra + return true, nil, err + } + lastResult = princ + } + } + route.Authenticator = ra + return true, lastResult, nil +} + +func stringSliceUnion(slices ...[]string) []string { + unique := make(map[string]struct{}) + var result []string + for _, slice := range slices { + for _, entry := range slice { + if _, ok := unique[entry]; ok { + continue + } + unique[entry] = struct{}{} + result = append(result, entry) + } + } + return result +} + +func stringSliceIntersection(slices ...[]string) []string { + unique := make(map[string]int) + var intersection []string + + total := len(slices) + var emptyCnt int + for _, slice := range slices { + if len(slice) == 0 { + emptyCnt++ + continue + } + + for _, entry := range slice { + unique[entry]++ + if unique[entry] == total-emptyCnt { // this entry appeared in all the non-empty slices + intersection = append(intersection, entry) + } + } + } + + return intersection +} + +// RouteAuthenticators represents a group of authenticators that represent a logical OR +type RouteAuthenticators []RouteAuthenticator + +// AllowsAnonymous returns true when there is an authenticator that means optional auth +func (ras RouteAuthenticators) AllowsAnonymous() bool { + for _, ra := range ras { + if ra.AllowsAnonymous() { + return true + } + } + return false +} + +// Authenticate method implemention so this collection can be used as authenticator +func (ras RouteAuthenticators) Authenticate(req *http.Request, route *MatchedRoute) (bool, interface{}, error) { + var lastError error + var allowsAnon bool + var anonAuth RouteAuthenticator + + for _, ra := range ras { + if ra.AllowsAnonymous() { + anonAuth = ra + allowsAnon = true + continue + } + applies, usr, err := ra.Authenticate(req, route) + if !applies || err != nil || usr == nil { + if err != nil { + lastError = err + } + continue + } + return applies, usr, nil + } + + if allowsAnon && lastError == nil { + route.Authenticator = &anonAuth + return true, nil, lastError + } + return lastError != nil, nil, lastError +} + +type routeEntry struct { + PathPattern string + BasePath string + Operation *spec.Operation + Consumes []string + Consumers map[string]runtime.Consumer + Produces []string + Producers map[string]runtime.Producer + Parameters map[string]spec.Parameter + Handler http.Handler + Formats strfmt.Registry + Binder *untypedRequestBinder + Authenticators RouteAuthenticators + Authorizer runtime.Authorizer +} + +// MatchedRoute represents the route that was matched in this request +type MatchedRoute struct { + routeEntry + Params RouteParams + Consumer runtime.Consumer + Producer runtime.Producer + Authenticator *RouteAuthenticator +} + +// HasAuth returns true when the route has a security requirement defined +func (m *MatchedRoute) HasAuth() bool { + return len(m.Authenticators) > 0 +} + +// NeedsAuth returns true when the request still +// needs to perform authentication +func (m *MatchedRoute) NeedsAuth() bool { + return m.HasAuth() && m.Authenticator == nil +} + +func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) { + mth := strings.ToUpper(method) + debugLog("looking up route for %s %s", method, path) + if Debug { + if len(d.routers) == 0 { + debugLog("there are no known routers") + } + for meth := range d.routers { + debugLog("got a router for %s", meth) + } + } + if router, ok := d.routers[mth]; ok { + if m, rp, ok := router.Lookup(fpath.Clean(path)); ok && m != nil { + if entry, ok := m.(*routeEntry); ok { + debugLog("found a route for %s %s with %d parameters", method, path, len(entry.Parameters)) + var params RouteParams + for _, p := range rp { + v, err := pathUnescape(p.Value) + if err != nil { + debugLog("failed to escape %q: %v", p.Value, err) + v = p.Value + } + // a workaround to handle fragment/composing parameters until they are supported in denco router + // check if this parameter is a fragment within a path segment + if xpos := strings.Index(entry.PathPattern, fmt.Sprintf("{%s}", p.Name)) + len(p.Name) + 2; xpos < len(entry.PathPattern) && entry.PathPattern[xpos] != '/' { + // extract fragment parameters + ep := strings.Split(entry.PathPattern[xpos:], "/")[0] + pnames, pvalues := decodeCompositParams(p.Name, v, ep, nil, nil) + for i, pname := range pnames { + params = append(params, RouteParam{Name: pname, Value: pvalues[i]}) + } + } else { + // use the parameter directly + params = append(params, RouteParam{Name: p.Name, Value: v}) + } + } + return &MatchedRoute{routeEntry: *entry, Params: params}, true + } + } else { + debugLog("couldn't find a route by path for %s %s", method, path) + } + } else { + debugLog("couldn't find a route by method for %s %s", method, path) + } + return nil, false +} + +func (d *defaultRouter) OtherMethods(method, path string) []string { + mn := strings.ToUpper(method) + var methods []string + for k, v := range d.routers { + if k != mn { + if _, _, ok := v.Lookup(fpath.Clean(path)); ok { + methods = append(methods, k) + continue + } + } + } + return methods +} + +// convert swagger parameters per path segment into a denco parameter as multiple parameters per segment are not supported in denco +var pathConverter = regexp.MustCompile(`{(.+?)}([^/]*)`) + +func decodeCompositParams(name string, value string, pattern string, names []string, values []string) ([]string, []string) { + pleft := strings.Index(pattern, "{") + names = append(names, name) + if pleft < 0 { + if strings.HasSuffix(value, pattern) { + values = append(values, value[:len(value)-len(pattern)]) + } else { + values = append(values, "") + } + } else { + toskip := pattern[:pleft] + pright := strings.Index(pattern, "}") + vright := strings.Index(value, toskip) + if vright >= 0 { + values = append(values, value[:vright]) + } else { + values = append(values, "") + value = "" + } + return decodeCompositParams(pattern[pleft+1:pright], value[vright+len(toskip):], pattern[pright+1:], names, values) + } + return names, values +} + +func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Operation) { + mn := strings.ToUpper(method) + + bp := fpath.Clean(d.spec.BasePath()) + if len(bp) > 0 && bp[len(bp)-1] == '/' { + bp = bp[:len(bp)-1] + } + + debugLog("operation: %#v", *operation) + if handler, ok := d.api.HandlerFor(method, strings.TrimPrefix(path, bp)); ok { + consumes := d.analyzer.ConsumesFor(operation) + produces := d.analyzer.ProducesFor(operation) + parameters := d.analyzer.ParamsFor(method, strings.TrimPrefix(path, bp)) + + record := denco.NewRecord(pathConverter.ReplaceAllString(path, ":$1"), &routeEntry{ + BasePath: bp, + PathPattern: path, + Operation: operation, + Handler: handler, + Consumes: consumes, + Produces: produces, + Consumers: d.api.ConsumersFor(normalizeOffers(consumes)), + Producers: d.api.ProducersFor(normalizeOffers(produces)), + Parameters: parameters, + Formats: d.api.Formats(), + Binder: newUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()), + Authenticators: d.buildAuthenticators(operation), + Authorizer: d.api.Authorizer(), + }) + d.records[mn] = append(d.records[mn], record) + } +} + +func (d *defaultRouteBuilder) buildAuthenticators(operation *spec.Operation) RouteAuthenticators { + requirements := d.analyzer.SecurityRequirementsFor(operation) + var auths []RouteAuthenticator + for _, reqs := range requirements { + var schemes []string + scopes := make(map[string][]string, len(reqs)) + var scopeSlices [][]string + for _, req := range reqs { + schemes = append(schemes, req.Name) + scopes[req.Name] = req.Scopes + scopeSlices = append(scopeSlices, req.Scopes) + } + + definitions := d.analyzer.SecurityDefinitionsForRequirements(reqs) + authenticators := d.api.AuthenticatorsFor(definitions) + auths = append(auths, RouteAuthenticator{ + Authenticator: authenticators, + Schemes: schemes, + Scopes: scopes, + allScopes: stringSliceUnion(scopeSlices...), + commonScopes: stringSliceIntersection(scopeSlices...), + allowAnonymous: len(reqs) == 1 && reqs[0].Name == "", + }) + } + return auths +} + +func (d *defaultRouteBuilder) Build() *defaultRouter { + routers := make(map[string]*denco.Router) + for method, records := range d.records { + router := denco.New() + _ = router.Build(records) + routers[method] = router + } + return &defaultRouter{ + spec: d.spec, + routers: routers, + } +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/middleware/security.go b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/security.go new file mode 100644 index 000000000..2b061caef --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/security.go @@ -0,0 +1,39 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 middleware + +import "net/http" + +func newSecureAPI(ctx *Context, next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := ctx.RouteInfo(r) + if rCtx != nil { + r = rCtx + } + if route != nil && !route.NeedsAuth() { + next.ServeHTTP(rw, r) + return + } + + _, rCtx, err := ctx.Authorize(r, route) + if err != nil { + ctx.Respond(rw, r, route.Produces, route, err) + return + } + r = rCtx + + next.ServeHTTP(rw, r) + }) +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/middleware/spec.go b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/spec.go new file mode 100644 index 000000000..6e02b8350 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/spec.go @@ -0,0 +1,48 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 middleware + +import ( + "net/http" + "path" +) + +// Spec creates a middleware to serve a swagger spec. +// This allows for altering the spec before starting the http listener. +// This can be useful if you want to serve the swagger spec from another path than /swagger.json +// +func Spec(basePath string, b []byte, next http.Handler) http.Handler { + if basePath == "" { + basePath = "/" + } + pth := path.Join(basePath, "swagger.json") + + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if r.URL.Path == pth { + rw.Header().Set("Content-Type", "application/json") + rw.WriteHeader(http.StatusOK) + //#nosec + rw.Write(b) + return + } + + if next == nil { + rw.Header().Set("Content-Type", "application/json") + rw.WriteHeader(http.StatusNotFound) + return + } + next.ServeHTTP(rw, r) + }) +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go new file mode 100644 index 000000000..3b0cd4e28 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go @@ -0,0 +1,286 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 untyped + +import ( + "fmt" + "net/http" + "sort" + "strings" + + "github.com/go-openapi/analysis" + "github.com/go-openapi/errors" + "github.com/go-openapi/loads" + "github.com/go-openapi/runtime" + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +// NewAPI creates the default untyped API +func NewAPI(spec *loads.Document) *API { + var an *analysis.Spec + if spec != nil && spec.Spec() != nil { + an = analysis.New(spec.Spec()) + } + api := &API{ + spec: spec, + analyzer: an, + consumers: make(map[string]runtime.Consumer, 10), + producers: make(map[string]runtime.Producer, 10), + authenticators: make(map[string]runtime.Authenticator), + operations: make(map[string]map[string]runtime.OperationHandler), + ServeError: errors.ServeError, + Models: make(map[string]func() interface{}), + formats: strfmt.NewFormats(), + } + return api.WithJSONDefaults() +} + +// API represents an untyped mux for a swagger spec +type API struct { + spec *loads.Document + analyzer *analysis.Spec + DefaultProduces string + DefaultConsumes string + consumers map[string]runtime.Consumer + producers map[string]runtime.Producer + authenticators map[string]runtime.Authenticator + authorizer runtime.Authorizer + operations map[string]map[string]runtime.OperationHandler + ServeError func(http.ResponseWriter, *http.Request, error) + Models map[string]func() interface{} + formats strfmt.Registry +} + +// WithJSONDefaults loads the json defaults for this api +func (d *API) WithJSONDefaults() *API { + d.DefaultConsumes = runtime.JSONMime + d.DefaultProduces = runtime.JSONMime + d.consumers[runtime.JSONMime] = runtime.JSONConsumer() + d.producers[runtime.JSONMime] = runtime.JSONProducer() + return d +} + +// WithoutJSONDefaults clears the json defaults for this api +func (d *API) WithoutJSONDefaults() *API { + d.DefaultConsumes = "" + d.DefaultProduces = "" + delete(d.consumers, runtime.JSONMime) + delete(d.producers, runtime.JSONMime) + return d +} + +// Formats returns the registered string formats +func (d *API) Formats() strfmt.Registry { + if d.formats == nil { + d.formats = strfmt.NewFormats() + } + return d.formats +} + +// RegisterFormat registers a custom format validator +func (d *API) RegisterFormat(name string, format strfmt.Format, validator strfmt.Validator) { + if d.formats == nil { + d.formats = strfmt.NewFormats() + } + d.formats.Add(name, format, validator) +} + +// RegisterAuth registers an auth handler in this api +func (d *API) RegisterAuth(scheme string, handler runtime.Authenticator) { + if d.authenticators == nil { + d.authenticators = make(map[string]runtime.Authenticator) + } + d.authenticators[scheme] = handler +} + +// RegisterAuthorizer registers an authorizer handler in this api +func (d *API) RegisterAuthorizer(handler runtime.Authorizer) { + d.authorizer = handler +} + +// RegisterConsumer registers a consumer for a media type. +func (d *API) RegisterConsumer(mediaType string, handler runtime.Consumer) { + if d.consumers == nil { + d.consumers = make(map[string]runtime.Consumer, 10) + } + d.consumers[strings.ToLower(mediaType)] = handler +} + +// RegisterProducer registers a producer for a media type +func (d *API) RegisterProducer(mediaType string, handler runtime.Producer) { + if d.producers == nil { + d.producers = make(map[string]runtime.Producer, 10) + } + d.producers[strings.ToLower(mediaType)] = handler +} + +// RegisterOperation registers an operation handler for an operation name +func (d *API) RegisterOperation(method, path string, handler runtime.OperationHandler) { + if d.operations == nil { + d.operations = make(map[string]map[string]runtime.OperationHandler, 30) + } + um := strings.ToUpper(method) + if b, ok := d.operations[um]; !ok || b == nil { + d.operations[um] = make(map[string]runtime.OperationHandler) + } + d.operations[um][path] = handler +} + +// OperationHandlerFor returns the operation handler for the specified id if it can be found +func (d *API) OperationHandlerFor(method, path string) (runtime.OperationHandler, bool) { + if d.operations == nil { + return nil, false + } + if pi, ok := d.operations[strings.ToUpper(method)]; ok { + h, ok := pi[path] + return h, ok + } + return nil, false +} + +// ConsumersFor gets the consumers for the specified media types +func (d *API) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer { + result := make(map[string]runtime.Consumer) + for _, mt := range mediaTypes { + if consumer, ok := d.consumers[mt]; ok { + result[mt] = consumer + } + } + return result +} + +// ProducersFor gets the producers for the specified media types +func (d *API) ProducersFor(mediaTypes []string) map[string]runtime.Producer { + result := make(map[string]runtime.Producer) + for _, mt := range mediaTypes { + if producer, ok := d.producers[mt]; ok { + result[mt] = producer + } + } + return result +} + +// AuthenticatorsFor gets the authenticators for the specified security schemes +func (d *API) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator { + result := make(map[string]runtime.Authenticator) + for k := range schemes { + if a, ok := d.authenticators[k]; ok { + result[k] = a + } + } + return result +} + +// AuthorizersFor returns the registered authorizer +func (d *API) Authorizer() runtime.Authorizer { + return d.authorizer +} + +// Validate validates this API for any missing items +func (d *API) Validate() error { + return d.validate() +} + +// validateWith validates the registrations in this API against the provided spec analyzer +func (d *API) validate() error { + var consumes []string + for k := range d.consumers { + consumes = append(consumes, k) + } + + var produces []string + for k := range d.producers { + produces = append(produces, k) + } + + var authenticators []string + for k := range d.authenticators { + authenticators = append(authenticators, k) + } + + var operations []string + for m, v := range d.operations { + for p := range v { + operations = append(operations, fmt.Sprintf("%s %s", strings.ToUpper(m), p)) + } + } + + var definedAuths []string + for k := range d.spec.Spec().SecurityDefinitions { + definedAuths = append(definedAuths, k) + } + + if err := d.verify("consumes", consumes, d.analyzer.RequiredConsumes()); err != nil { + return err + } + if err := d.verify("produces", produces, d.analyzer.RequiredProduces()); err != nil { + return err + } + if err := d.verify("operation", operations, d.analyzer.OperationMethodPaths()); err != nil { + return err + } + + requiredAuths := d.analyzer.RequiredSecuritySchemes() + if err := d.verify("auth scheme", authenticators, requiredAuths); err != nil { + return err + } + if err := d.verify("security definitions", definedAuths, requiredAuths); err != nil { + return err + } + return nil +} + +func (d *API) verify(name string, registrations []string, expectations []string) error { + + sort.Sort(sort.StringSlice(registrations)) + sort.Sort(sort.StringSlice(expectations)) + + expected := map[string]struct{}{} + seen := map[string]struct{}{} + + for _, v := range expectations { + expected[v] = struct{}{} + } + + var unspecified []string + for _, v := range registrations { + seen[v] = struct{}{} + if _, ok := expected[v]; !ok { + unspecified = append(unspecified, v) + } + } + + for k := range seen { + delete(expected, k) + } + + var unregistered []string + for k := range expected { + unregistered = append(unregistered, k) + } + sort.Sort(sort.StringSlice(unspecified)) + sort.Sort(sort.StringSlice(unregistered)) + + if len(unregistered) > 0 || len(unspecified) > 0 { + return &errors.APIVerificationFailed{ + Section: name, + MissingSpecification: unspecified, + MissingRegistration: unregistered, + } + } + + return nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/middleware/validation.go b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/validation.go new file mode 100644 index 000000000..bb8df3cb3 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/middleware/validation.go @@ -0,0 +1,122 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 middleware + +import ( + "mime" + "net/http" + "strings" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/swag" +) + +type validation struct { + context *Context + result []error + request *http.Request + route *MatchedRoute + bound map[string]interface{} +} + +// ContentType validates the content type of a request +func validateContentType(allowed []string, actual string) error { + debugLog("validating content type for %q against [%s]", actual, strings.Join(allowed, ", ")) + if len(allowed) == 0 { + return nil + } + mt, _, err := mime.ParseMediaType(actual) + if err != nil { + return errors.InvalidContentType(actual, allowed) + } + if swag.ContainsStringsCI(allowed, mt) { + return nil + } + if swag.ContainsStringsCI(allowed, "*/*") { + return nil + } + parts := strings.Split(actual, "/") + if len(parts) == 2 && swag.ContainsStringsCI(allowed, parts[0]+"/*") { + return nil + } + return errors.InvalidContentType(actual, allowed) +} + +func validateRequest(ctx *Context, request *http.Request, route *MatchedRoute) *validation { + debugLog("validating request %s %s", request.Method, request.URL.EscapedPath()) + validate := &validation{ + context: ctx, + request: request, + route: route, + bound: make(map[string]interface{}), + } + + validate.contentType() + if len(validate.result) == 0 { + validate.responseFormat() + } + if len(validate.result) == 0 { + validate.parameters() + } + + return validate +} + +func (v *validation) parameters() { + debugLog("validating request parameters for %s %s", v.request.Method, v.request.URL.EscapedPath()) + if result := v.route.Binder.Bind(v.request, v.route.Params, v.route.Consumer, v.bound); result != nil { + if result.Error() == "validation failure list" { + for _, e := range result.(*errors.Validation).Value.([]interface{}) { + v.result = append(v.result, e.(error)) + } + return + } + v.result = append(v.result, result) + } +} + +func (v *validation) contentType() { + if len(v.result) == 0 && runtime.HasBody(v.request) { + debugLog("validating body content type for %s %s", v.request.Method, v.request.URL.EscapedPath()) + ct, _, req, err := v.context.ContentType(v.request) + if err != nil { + v.result = append(v.result, err) + } else { + v.request = req + } + + if len(v.result) == 0 { + if err := validateContentType(v.route.Consumes, ct); err != nil { + v.result = append(v.result, err) + } + } + if ct != "" && v.route.Consumer == nil { + cons, ok := v.route.Consumers[ct] + if !ok { + v.result = append(v.result, errors.New(500, "no consumer registered for %s", ct)) + } else { + v.route.Consumer = cons + } + } + } +} + +func (v *validation) responseFormat() { + if str, rCtx := v.context.ResponseFormat(v.request, v.route.Produces); str == "" && runtime.HasBody(v.request) { + v.request = rCtx + v.result = append(v.result, errors.InvalidResponseFormat(v.request.Header.Get(runtime.HeaderAccept), v.route.Produces)) + } +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/request.go b/src/runtime/vendor/github.com/go-openapi/runtime/request.go new file mode 100644 index 000000000..87b73da45 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/request.go @@ -0,0 +1,77 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "io" + "net/http" + "strings" + + "github.com/go-openapi/swag" +) + +// CanHaveBody returns true if this method can have a body +func CanHaveBody(method string) bool { + mn := strings.ToUpper(method) + return mn == "POST" || mn == "PUT" || mn == "PATCH" || mn == "DELETE" +} + +// IsSafe returns true if this is a request with a safe method +func IsSafe(r *http.Request) bool { + mn := strings.ToUpper(r.Method) + return mn == "GET" || mn == "HEAD" +} + +// AllowsBody returns true if the request allows for a body +func AllowsBody(r *http.Request) bool { + mn := strings.ToUpper(r.Method) + return mn != "HEAD" +} + +// HasBody returns true if this method needs a content-type +func HasBody(r *http.Request) bool { + return len(r.TransferEncoding) > 0 || r.ContentLength > 0 +} + +// JSONRequest creates a new http request with json headers set +func JSONRequest(method, urlStr string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequest(method, urlStr, body) + if err != nil { + return nil, err + } + req.Header.Add(HeaderContentType, JSONMime) + req.Header.Add(HeaderAccept, JSONMime) + return req, nil +} + +// Gettable for things with a method GetOK(string) (data string, hasKey bool, hasValue bool) +type Gettable interface { + GetOK(string) ([]string, bool, bool) +} + +// ReadSingleValue reads a single value from the source +func ReadSingleValue(values Gettable, name string) string { + vv, _, hv := values.GetOK(name) + if hv { + return vv[len(vv)-1] + } + return "" +} + +// ReadCollectionValue reads a collection value from a string data source +func ReadCollectionValue(values Gettable, name, collectionFormat string) []string { + v := ReadSingleValue(values, name) + return swag.SplitByFormat(v, collectionFormat) +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/security/authenticator.go b/src/runtime/vendor/github.com/go-openapi/runtime/security/authenticator.go new file mode 100644 index 000000000..3c624d755 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/security/authenticator.go @@ -0,0 +1,215 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 security + +import ( + "context" + "net/http" + "strings" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" +) + +const ( + query = "query" + header = "header" +) + +// HttpAuthenticator is a function that authenticates a HTTP request +func HttpAuthenticator(handler func(*http.Request) (bool, interface{}, error)) runtime.Authenticator { + return runtime.AuthenticatorFunc(func(params interface{}) (bool, interface{}, error) { + if request, ok := params.(*http.Request); ok { + return handler(request) + } + if scoped, ok := params.(*ScopedAuthRequest); ok { + return handler(scoped.Request) + } + return false, nil, nil + }) +} + +// ScopedAuthenticator is a function that authenticates a HTTP request against a list of valid scopes +func ScopedAuthenticator(handler func(*ScopedAuthRequest) (bool, interface{}, error)) runtime.Authenticator { + return runtime.AuthenticatorFunc(func(params interface{}) (bool, interface{}, error) { + if request, ok := params.(*ScopedAuthRequest); ok { + return handler(request) + } + return false, nil, nil + }) +} + +// UserPassAuthentication authentication function +type UserPassAuthentication func(string, string) (interface{}, error) + +// UserPassAuthenticationCtx authentication function with context.Context +type UserPassAuthenticationCtx func(context.Context, string, string) (context.Context, interface{}, error) + +// TokenAuthentication authentication function +type TokenAuthentication func(string) (interface{}, error) + +// TokenAuthenticationCtx authentication function with context.Context +type TokenAuthenticationCtx func(context.Context, string) (context.Context, interface{}, error) + +// ScopedTokenAuthentication authentication function +type ScopedTokenAuthentication func(string, []string) (interface{}, error) + +// ScopedTokenAuthenticationCtx authentication function with context.Context +type ScopedTokenAuthenticationCtx func(context.Context, string, []string) (context.Context, interface{}, error) + +// BasicAuth creates a basic auth authenticator with the provided authentication function +func BasicAuth(authenticate UserPassAuthentication) runtime.Authenticator { + return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) { + if usr, pass, ok := r.BasicAuth(); ok { + p, err := authenticate(usr, pass) + return true, p, err + } + + return false, nil, nil + }) +} + +// BasicAuthCtx creates a basic auth authenticator with the provided authentication function with support for context.Context +func BasicAuthCtx(authenticate UserPassAuthenticationCtx) runtime.Authenticator { + return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) { + if usr, pass, ok := r.BasicAuth(); ok { + ctx, p, err := authenticate(r.Context(), usr, pass) + *r = *r.WithContext(ctx) + return true, p, err + } + + return false, nil, nil + }) +} + +// APIKeyAuth creates an authenticator that uses a token for authorization. +// This token can be obtained from either a header or a query string +func APIKeyAuth(name, in string, authenticate TokenAuthentication) runtime.Authenticator { + inl := strings.ToLower(in) + if inl != query && inl != header { + // panic because this is most likely a typo + panic(errors.New(500, "api key auth: in value needs to be either \"query\" or \"header\".")) + } + + var getToken func(*http.Request) string + switch inl { + case header: + getToken = func(r *http.Request) string { return r.Header.Get(name) } + case query: + getToken = func(r *http.Request) string { return r.URL.Query().Get(name) } + } + + return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) { + token := getToken(r) + if token == "" { + return false, nil, nil + } + + p, err := authenticate(token) + return true, p, err + }) +} + +// APIKeyAuthCtx creates an authenticator that uses a token for authorization with support for context.Context. +// This token can be obtained from either a header or a query string +func APIKeyAuthCtx(name, in string, authenticate TokenAuthenticationCtx) runtime.Authenticator { + inl := strings.ToLower(in) + if inl != query && inl != header { + // panic because this is most likely a typo + panic(errors.New(500, "api key auth: in value needs to be either \"query\" or \"header\".")) + } + + var getToken func(*http.Request) string + switch inl { + case header: + getToken = func(r *http.Request) string { return r.Header.Get(name) } + case query: + getToken = func(r *http.Request) string { return r.URL.Query().Get(name) } + } + + return HttpAuthenticator(func(r *http.Request) (bool, interface{}, error) { + token := getToken(r) + if token == "" { + return false, nil, nil + } + + ctx, p, err := authenticate(r.Context(), token) + *r = *r.WithContext(ctx) + return true, p, err + }) +} + +// ScopedAuthRequest contains both a http request and the required scopes for a particular operation +type ScopedAuthRequest struct { + Request *http.Request + RequiredScopes []string +} + +// BearerAuth for use with oauth2 flows +func BearerAuth(name string, authenticate ScopedTokenAuthentication) runtime.Authenticator { + const prefix = "Bearer " + return ScopedAuthenticator(func(r *ScopedAuthRequest) (bool, interface{}, error) { + var token string + hdr := r.Request.Header.Get("Authorization") + if strings.HasPrefix(hdr, prefix) { + token = strings.TrimPrefix(hdr, prefix) + } + if token == "" { + qs := r.Request.URL.Query() + token = qs.Get("access_token") + } + //#nosec + ct, _, _ := runtime.ContentType(r.Request.Header) + if token == "" && (ct == "application/x-www-form-urlencoded" || ct == "multipart/form-data") { + token = r.Request.FormValue("access_token") + } + + if token == "" { + return false, nil, nil + } + + p, err := authenticate(token, r.RequiredScopes) + return true, p, err + }) +} + +// BearerAuthCtx for use with oauth2 flows with support for context.Context. +func BearerAuthCtx(name string, authenticate ScopedTokenAuthenticationCtx) runtime.Authenticator { + const prefix = "Bearer " + return ScopedAuthenticator(func(r *ScopedAuthRequest) (bool, interface{}, error) { + var token string + hdr := r.Request.Header.Get("Authorization") + if strings.HasPrefix(hdr, prefix) { + token = strings.TrimPrefix(hdr, prefix) + } + if token == "" { + qs := r.Request.URL.Query() + token = qs.Get("access_token") + } + //#nosec + ct, _, _ := runtime.ContentType(r.Request.Header) + if token == "" && (ct == "application/x-www-form-urlencoded" || ct == "multipart/form-data") { + token = r.Request.FormValue("access_token") + } + + if token == "" { + return false, nil, nil + } + + ctx, p, err := authenticate(r.Request.Context(), token, r.RequiredScopes) + *r.Request = *r.Request.WithContext(ctx) + return true, p, err + }) +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/security/authorizer.go b/src/runtime/vendor/github.com/go-openapi/runtime/security/authorizer.go new file mode 100644 index 000000000..00c1a4d6a --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/security/authorizer.go @@ -0,0 +1,27 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 security + +import ( + "net/http" + + "github.com/go-openapi/runtime" +) + +// Authorized provides a default implementation of the Authorizer interface where all +// requests are authorized (successful) +func Authorized() runtime.Authorizer { + return runtime.AuthorizerFunc(func(_ *http.Request, _ interface{}) error { return nil }) +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/statuses.go b/src/runtime/vendor/github.com/go-openapi/runtime/statuses.go new file mode 100644 index 000000000..3b011a0bf --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/statuses.go @@ -0,0 +1,90 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +// Statuses lists the most common HTTP status codes to default message +// taken from https://httpstatuses.com/ +var Statuses = map[int]string{ + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 103: "Checkpoint", + 122: "URI too long", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Request Processed", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 208: "Already Reported", + 226: "IM Used", + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 306: "Switch Proxy", + 307: "Temporary Redirect", + 308: "Permanent Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Request Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot", + 420: "Enhance Your Calm", + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 426: "Upgrade Required", + 428: "Precondition Required", + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 444: "No Response", + 449: "Retry With", + 450: "Blocked by Windows Parental Controls", + 451: "Wrong Exchange Server", + 499: "Client Closed Request", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", + 507: "Insufficient Storage", + 508: "Loop Detected", + 509: "Bandwidth Limit Exceeded", + 510: "Not Extended", + 511: "Network Authentication Required", + 598: "Network read timeout error", + 599: "Network connect timeout error", +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/text.go b/src/runtime/vendor/github.com/go-openapi/runtime/text.go new file mode 100644 index 000000000..77099feda --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/text.go @@ -0,0 +1,111 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "bytes" + "encoding" + "errors" + "fmt" + "io" + "reflect" + + "github.com/go-openapi/swag" +) + +// TextConsumer creates a new text consumer +func TextConsumer() Consumer { + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + if reader == nil { + return errors.New("TextConsumer requires a reader") // early exit + } + + buf := new(bytes.Buffer) + _, err := buf.ReadFrom(reader) + if err != nil { + return err + } + b := buf.Bytes() + + if tu, ok := data.(encoding.TextUnmarshaler); ok { + err := tu.UnmarshalText(b) + if err != nil { + return fmt.Errorf("text consumer: %v", err) + } + + return nil + } + + t := reflect.TypeOf(data) + if data != nil && t.Kind() == reflect.Ptr { + v := reflect.Indirect(reflect.ValueOf(data)) + if t.Elem().Kind() == reflect.String { + v.SetString(string(b)) + return nil + } + } + + return fmt.Errorf("%v (%T) is not supported by the TextConsumer, %s", + data, data, "can be resolved by supporting TextUnmarshaler interface") + }) +} + +// TextProducer creates a new text producer +func TextProducer() Producer { + return ProducerFunc(func(writer io.Writer, data interface{}) error { + if writer == nil { + return errors.New("TextProducer requires a writer") // early exit + } + + if data == nil { + return errors.New("no data given to produce text from") + } + + if tm, ok := data.(encoding.TextMarshaler); ok { + txt, err := tm.MarshalText() + if err != nil { + return fmt.Errorf("text producer: %v", err) + } + _, err = writer.Write(txt) + return err + } + + if str, ok := data.(error); ok { + _, err := writer.Write([]byte(str.Error())) + return err + } + + if str, ok := data.(fmt.Stringer); ok { + _, err := writer.Write([]byte(str.String())) + return err + } + + v := reflect.Indirect(reflect.ValueOf(data)) + if t := v.Type(); t.Kind() == reflect.Struct || t.Kind() == reflect.Slice { + b, err := swag.WriteJSON(data) + if err != nil { + return err + } + _, err = writer.Write(b) + return err + } + if v.Kind() != reflect.String { + return fmt.Errorf("%T is not a supported type by the TextProducer", data) + } + + _, err := writer.Write([]byte(v.String())) + return err + }) +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/values.go b/src/runtime/vendor/github.com/go-openapi/runtime/values.go new file mode 100644 index 000000000..11f5732af --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/values.go @@ -0,0 +1,19 @@ +package runtime + +// Values typically represent parameters on a http request. +type Values map[string][]string + +// GetOK returns the values collection for the given key. +// When the key is present in the map it will return true for hasKey. +// When the value is not empty it will return true for hasValue. +func (v Values) GetOK(key string) (value []string, hasKey bool, hasValue bool) { + value, hasKey = v[key] + if !hasKey { + return + } + if len(value) == 0 { + return + } + hasValue = true + return +} diff --git a/src/runtime/vendor/github.com/go-openapi/runtime/xml.go b/src/runtime/vendor/github.com/go-openapi/runtime/xml.go new file mode 100644 index 000000000..821c7393d --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/runtime/xml.go @@ -0,0 +1,36 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "encoding/xml" + "io" +) + +// XMLConsumer creates a new XML consumer +func XMLConsumer() Consumer { + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + dec := xml.NewDecoder(reader) + return dec.Decode(data) + }) +} + +// XMLProducer creates a new XML producer +func XMLProducer() Producer { + return ProducerFunc(func(writer io.Writer, data interface{}) error { + enc := xml.NewEncoder(writer) + return enc.Encode(data) + }) +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/.editorconfig b/src/runtime/vendor/github.com/go-openapi/spec/.editorconfig new file mode 100644 index 000000000..3152da69a --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/src/runtime/vendor/github.com/go-openapi/spec/.gitignore b/src/runtime/vendor/github.com/go-openapi/spec/.gitignore new file mode 100644 index 000000000..dd91ed6a0 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/.gitignore @@ -0,0 +1,2 @@ +secrets.yml +coverage.out diff --git a/src/runtime/vendor/github.com/go-openapi/spec/.golangci.yml b/src/runtime/vendor/github.com/go-openapi/spec/.golangci.yml new file mode 100644 index 000000000..ed53e5cd7 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/.golangci.yml @@ -0,0 +1,21 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 25 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 2 + +linters: + enable-all: true + disable: + - maligned + - unparam + - lll diff --git a/src/runtime/vendor/github.com/go-openapi/spec/.travis.yml b/src/runtime/vendor/github.com/go-openapi/spec/.travis.yml new file mode 100644 index 000000000..a4f03484b --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/.travis.yml @@ -0,0 +1,18 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- '1.9' +- 1.10.x +- 1.11.x +install: +- go get -u github.com/stretchr/testify +- go get -u github.com/go-openapi/swag +- go get -u gopkg.in/yaml.v2 +- go get -u github.com/go-openapi/jsonpointer +- go get -u github.com/go-openapi/jsonreference +language: go +notifications: + slack: + secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E= +script: +- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/src/runtime/vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md b/src/runtime/vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9322b065e --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/src/runtime/vendor/github.com/go-openapi/spec/LICENSE b/src/runtime/vendor/github.com/go-openapi/spec/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/src/runtime/vendor/github.com/go-openapi/spec/README.md b/src/runtime/vendor/github.com/go-openapi/spec/README.md new file mode 100644 index 000000000..6354742cb --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/README.md @@ -0,0 +1,10 @@ +# OAI object model [![Build Status](https://travis-ci.org/go-openapi/spec.svg?branch=master)](https://travis-ci.org/go-openapi/spec) [![codecov](https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE) +[![GoDoc](https://godoc.org/github.com/go-openapi/spec?status.svg)](http://godoc.org/github.com/go-openapi/spec) +[![GolangCI](https://golangci.com/badges/github.com/go-openapi/spec.svg)](https://golangci.com) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/spec)](https://goreportcard.com/report/github.com/go-openapi/spec) + +The object model for OpenAPI specification documents. + +Currently supports Swagger 2.0. diff --git a/src/runtime/vendor/github.com/go-openapi/spec/bindata.go b/src/runtime/vendor/github.com/go-openapi/spec/bindata.go new file mode 100644 index 000000000..1717ea105 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/bindata.go @@ -0,0 +1,260 @@ +// Code generated by go-bindata. +// sources: +// schemas/jsonschema-draft-04.json +// schemas/v2/schema.json +// DO NOT EDIT! + +package spec + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _jsonschemaDraft04JSON = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x57\x3d\x6f\xdb\x3c\x10\xde\xf3\x2b\x08\x26\x63\xf2\x2a\x2f\xd0\xc9\x5b\xd1\x2e\x01\x5a\x34\x43\x37\x23\x03\x6d\x9d\x6c\x06\x14\xa9\x50\x54\x60\xc3\xd0\x7f\x2f\x28\x4a\x14\x29\x91\x92\x2d\xa7\x8d\x97\x28\xbc\xaf\xe7\x8e\xf7\xc5\xd3\x0d\x42\x08\x61\x9a\xe2\x15\xc2\x7b\xa5\x8a\x55\x92\xbc\x96\x82\x3f\x94\xdb\x3d\xe4\xe4\x3f\x21\x77\x49\x2a\x49\xa6\x1e\x1e\xbf\x24\xe6\xec\x16\xdf\x1b\xa1\x3b\xf3\xff\x02\xc9\x14\xca\xad\xa4\x85\xa2\x82\x6b\xe9\x6f\x42\x02\x32\x2c\x28\x07\x45\x5a\x15\x3d\x77\x46\x39\xd5\xcc\x25\x5e\x21\x83\xb8\x21\x18\xb6\xaf\x52\x92\xa3\x47\x68\x88\xea\x58\x80\x56\x4e\x1a\xf2\xbd\x4f\xcc\x29\x7f\x52\x90\x6b\x7d\xff\x0f\x48\xb4\x3d\x3f\x21\x7c\x27\x21\xd3\x2a\x6e\x31\xaa\x2d\x53\xdd\xf3\xe3\x42\x94\x54\xd1\x77\x78\xe2\x0a\x76\x20\xe3\x20\x68\xcb\x30\x86\x41\xf3\x2a\xc7\x2b\xf4\x78\x8e\xfe\xef\x90\x91\x8a\xa9\xc7\xb1\x1d\xc2\xd8\x2f\x0d\x75\xed\xc1\x4e\x9c\xc8\x25\x43\xac\xa8\xbe\xd7\xcc\xa9\xd1\xa9\x21\xa0\x1a\xbd\x04\x61\x94\x34\x2f\x18\xfc\x3e\x16\x50\x8e\x4d\x03\x6f\x1c\x58\xdb\x48\x23\xbc\x11\x82\x01\xe1\xfa\xd3\x3a\x8e\x30\xaf\x18\x33\x7f\xf3\x8d\x39\x11\x9b\x57\xd8\x2a\xfd\x55\x2a\x49\xf9\x0e\xc7\xec\x37\xd4\x25\xf7\xec\x5c\x66\xc7\xd7\x99\xaa\xcf\x4f\x89\x8a\xd3\xb7\x0a\x3a\xaa\x92\x15\xf4\x30\x6f\x1c\xb0\xd6\x46\xe7\x98\x39\x2d\xa4\x28\x40\x2a\x3a\x88\x9e\x29\xba\x88\x37\x2d\xca\x60\x38\xfa\xba\x5b\x20\xac\xa8\x62\xb0\x4c\xd4\xaf\xda\x45\x0a\xba\x5c\x3b\xb9\xc7\x79\xc5\x14\x2d\x18\x34\x19\x1c\x51\xdb\x25\x4d\xb4\x7e\x06\x14\x38\x6c\x59\x55\xd2\x77\xf8\x69\x59\xfc\x7b\x73\xed\x93\x43\xcb\x32\x6d\x3c\x28\xdc\x1b\x9a\xd3\x62\xab\xc2\x27\xf7\x41\xc9\x08\x2b\x23\x08\xad\x13\x57\x21\x9c\xd3\x72\x0d\x42\x72\xf8\x01\x7c\xa7\xf6\x83\xce\x39\xd7\x82\x3c\x1f\x2f\xd6\x60\x1b\xa2\xdf\x35\x89\x52\x20\xe7\x73\x74\xe0\x66\x26\x64\x4e\xb4\x97\x58\xc2\x0e\x0e\xe1\x60\x92\x34\x6d\xa0\x10\xd6\xb5\x83\x61\x27\xe6\x47\xd3\x89\xbd\x63\xfd\x3b\x8d\x03\x3d\x6c\x42\x2d\x5b\x70\xee\xe8\xdf\x4b\xf4\x66\x4e\xe1\x01\x45\x17\x80\x74\xad\x4f\xc3\xf3\xae\xc6\x1d\xc6\xd7\xc2\xce\xc9\xe1\x29\x30\x86\x2f\x4a\xa6\x4b\x15\x84\x73\xc9\x6f\xfd\x7f\xa5\x6e\x9e\xbd\xf1\xb0\xd4\xdd\x45\x5a\xc2\x3e\x4b\x78\xab\xa8\x84\x74\x4a\x91\x3b\x92\x23\x05\xf2\x1c\x1e\x7b\xf3\x09\xf8\xcf\xab\x24\xb6\x60\xa2\xe8\x4c\x9f\x75\x77\xaa\x8c\xe6\x01\x45\x36\x86\xcf\xc3\x63\x3a\xea\xd4\x8d\x7e\x06\xac\x14\x0a\xe0\x29\xf0\xed\x07\x22\x1a\x65\xda\x44\xae\xa2\x73\x1a\xe6\x90\x69\xa2\x8c\x46\xb2\x2f\xde\x49\x38\x08\xed\xfe\xfd\x41\xaf\x9f\xa9\x55\xd7\xdd\x22\x8d\xfa\x45\x63\xc5\x0f\x80\xf3\xb4\x08\xd6\x79\x30\x9e\x93\xee\x59\xa6\xd0\x4b\xee\x22\xe3\x33\xc1\x3a\x27\x68\x36\x78\x7e\x87\x0a\x06\xd5\x2e\x20\xd3\xaf\x15\xfb\xd8\x3b\x73\x14\xbb\x92\xed\x05\x5d\x2e\x29\x38\x2c\x94\xe4\x42\x45\x5e\xd3\xb5\x7d\xdf\x47\xca\x38\xb4\x5c\xaf\xfb\x7d\xdd\x6d\xf4\xa1\x2d\x77\xdd\x2f\xce\x6d\xc4\x7b\x8b\x4e\x67\xa9\x6f\xfe\x04\x00\x00\xff\xff\xb1\xd1\x27\x78\x05\x11\x00\x00") + +func jsonschemaDraft04JSONBytes() ([]byte, error) { + return bindataRead( + _jsonschemaDraft04JSON, + "jsonschema-draft-04.json", + ) +} + +func jsonschemaDraft04JSON() (*asset, error) { + bytes, err := jsonschemaDraft04JSONBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "jsonschema-draft-04.json", size: 4357, mode: os.FileMode(420), modTime: time.Unix(1523760398, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _v2SchemaJSON = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5d\x4f\x93\xdb\x36\xb2\xbf\xfb\x53\xa0\x14\x57\xd9\xae\xd8\x92\xe3\xf7\x2e\xcf\x97\xd4\xbc\xd8\x49\x66\x37\x5e\x4f\x79\x26\xbb\x87\x78\x5c\x05\x91\x2d\x09\x09\x09\x30\x00\x38\x33\x5a\xef\x7c\xf7\x2d\xf0\x9f\x08\x02\x20\x41\x8a\xd2\xc8\x0e\x0f\xa9\x78\x28\xa0\xd1\xdd\x68\x34\x7e\xdd\xf8\xf7\xf9\x11\x42\x33\x49\x64\x04\xb3\xd7\x68\x76\x86\xfe\x76\xf9\xfe\x1f\xe8\x32\xd8\x40\x8c\xd1\x8a\x71\x74\x79\x8b\xd7\x6b\xe0\xe8\xd5\xfc\x25\x3a\xbb\x38\x9f\xcf\x9e\xab\x0a\x24\x54\xa5\x37\x52\x26\xaf\x17\x0b\x91\x17\x99\x13\xb6\xb8\x79\xb5\x10\x59\xdd\xf9\xef\x82\xd1\x6f\xf2\xc2\x8f\xf3\x4f\xb5\x1a\xea\xc7\x17\x45\x41\xc6\xd7\x8b\x90\xe3\x95\x7c\xf1\xf2\x7f\x8b\xca\x45\x3d\xb9\x4d\x32\xa6\xd8\xf2\x77\x08\x64\xfe\x8d\xc3\x9f\x29\xe1\xa0\x9a\xff\xed\x11\x42\x08\xcd\x8a\xd6\xb3\x9f\x15\x67\x74\xc5\xca\x7f\x27\x58\x6e\xc4\xec\x11\x42\xd7\x59\x5d\x1c\x86\x44\x12\x46\x71\x74\xc1\x59\x02\x5c\x12\x10\xb3\xd7\x68\x85\x23\x01\x59\x81\x04\x4b\x09\x9c\x6a\xbf\x7e\xce\x49\x7d\xba\x7b\x51\xfd\xa1\x44\xe2\xb0\x52\xac\x7d\xb3\x08\x61\x45\x68\x46\x56\x2c\x6e\x80\x86\x8c\xbf\xbd\x93\x40\x05\x61\x74\x96\x95\xbe\x7f\x84\xd0\x7d\x4e\xde\x42\xb7\xe4\xbe\x46\xbb\x14\x5b\x48\x4e\xe8\xba\x90\x05\xa1\x19\xd0\x34\xae\xc4\xce\xbe\xbc\x9a\xbf\x9c\x15\x7f\x5d\x57\xc5\x42\x10\x01\x27\x89\xe2\x48\x51\xb9\xda\x40\xd5\x87\x37\xc0\x15\x5f\x88\xad\x90\xdc\x10\x81\x42\x16\xa4\x31\x50\x39\x2f\x38\xad\xab\xb0\x53\xd8\xac\x94\x56\x6f\xc3\x84\xf4\x11\xa4\x50\xb3\xfa\xe9\xd3\x6f\x9f\x3e\xdf\x2f\xd0\xeb\x8f\x1f\x3f\x7e\xbc\xfe\xf6\xe9\xf7\xaf\x5f\x7f\xfc\x18\x7e\xfb\xec\xfb\xc7\xb3\x36\x79\x54\x43\xe8\x29\xc5\x31\x20\xc6\x11\x49\x9e\xe5\x12\x41\x66\xa0\xe8\xed\x1d\x8e\x93\x08\x5e\xa3\x27\x3b\xc3\x7c\xa2\x73\xba\xc4\x02\x2e\xb0\xdc\xf4\xe5\x76\xd1\xca\x96\xa2\x8a\x94\xcd\x21\xc9\x6c\xec\x2c\x70\x42\x9e\x34\x74\x9d\x19\x7c\xcd\x20\x9c\xea\x2e\x0a\xfe\x42\x84\xd4\x29\x04\x8c\x8a\xb4\x41\xa2\xc1\xdc\x19\x8a\x88\x90\x4a\x49\xef\xce\xdf\xbd\x45\x4a\x52\x81\x70\x10\x40\x22\x21\x44\xcb\x6d\xc5\xec\x4e\x3c\x1c\x45\xef\x57\x9a\xb5\x7d\xae\xfe\xe5\xe4\x31\x86\x90\xe0\xab\x6d\x02\x3b\x2e\xcb\x11\x90\xd9\xa8\xc6\x77\xc2\x59\x98\x06\xfd\xf9\x2e\x78\x45\x01\xa6\xa8\xa0\x71\x5c\xbe\x33\xa7\xd2\xd9\x5f\x95\xef\xd9\xd5\xac\xfd\xdc\x5d\xbf\x5e\xb8\xd1\x3e\xc7\x31\x48\xe0\x5e\x4c\x14\x65\xdf\xb8\xa8\x71\x10\x09\xa3\xc2\xc7\x02\xcb\xa2\x4e\x5a\x02\x82\x94\x13\xb9\xf5\x30\xe6\xb2\xa4\xb5\xfe\x9b\x3e\x7a\xb2\x55\xd2\xa8\x4a\xbc\x16\xb6\x71\x8e\x39\xc7\xdb\x9d\xe1\x10\x09\x71\xbd\x9c\xb3\x41\x89\xd7\xa5\x89\xdc\x57\xb5\x53\x4a\xfe\x4c\xe1\xbc\xa0\x21\x79\x0a\x1a\x0f\x70\xa7\x5c\x08\x8e\xde\xb0\xc0\x43\x24\xad\x74\x63\x0e\xb1\xd9\x90\xe1\xb0\x2d\x13\xa7\x6d\x78\xfd\x04\x14\x38\x8e\x90\xaa\xce\x63\xac\x3e\x23\xbc\x64\xa9\xb4\xf8\x03\x63\xde\xcd\xbe\x16\x13\x4a\x55\xac\x82\x12\xc6\xac\xd4\x35\xf7\x22\xd4\x3a\xff\x22\x73\x0e\x6e\x51\xa0\x75\x1e\xae\x8f\xe8\x5d\xc7\x59\xe6\xe4\x9a\x18\x8d\xd6\x1c\x53\x84\x4d\xb7\x67\x28\x37\x09\x84\x69\x88\x12\x0e\x01\x11\x80\x32\xa2\xf5\xb9\xaa\xc6\xd9\x73\x53\xab\xfb\xb4\x2e\x20\xc6\x54\x92\xa0\x9a\xf3\x69\x1a\x2f\x81\x77\x37\xae\x53\x1a\xce\x40\xc4\xa8\x82\x1c\xb5\xef\xda\x24\x7d\xb9\x61\x69\x14\xa2\x25\xa0\x90\xac\x56\xc0\x81\x4a\xb4\xe2\x2c\xce\x4a\x64\x7a\x9a\x23\xf4\x13\x91\x3f\xa7\x4b\xf4\x63\x84\x6f\x18\x87\x10\xbd\xc3\xfc\x8f\x90\xdd\x52\x44\x04\xc2\x51\xc4\x6e\x21\x74\x48\x21\x81\xc7\xe2\xfd\xea\x12\xf8\x0d\x09\xf6\xe9\x47\x35\xaf\x67\xc4\x14\xf7\x22\x27\x97\xe1\xe2\x76\x2d\x06\x8c\x4a\x1c\x48\x3f\x73\x2d\x0b\x5b\x29\x45\x24\x00\x2a\x0c\x11\xec\x94\xca\xc2\xa6\xc1\x37\x21\x43\x83\x3b\x5f\x97\xf1\x43\x5e\x53\x73\x19\xa5\x36\xd8\x2d\x05\x2e\x34\x0b\xeb\x39\xfc\x1d\x63\x51\x01\xbd\x3d\xbb\x90\x84\x40\x25\x59\x6d\x09\x5d\xa3\x1c\x37\xe6\x5c\x16\x9a\x40\x09\x70\xc1\xe8\x82\xf1\x35\xa6\xe4\xdf\x99\x5c\x8e\x9e\x4d\x79\xb4\x27\x2f\xbf\x7e\xf8\x05\x25\x8c\x50\xa9\x98\x29\x90\x62\x60\xea\x75\xae\x13\xca\xbf\x2b\x1a\x29\x27\x76\xd6\x20\xc6\x64\x5f\xe6\x32\x1a\x08\x87\x21\x07\x21\xbc\xb4\xe4\xe0\x32\x67\xa6\xcd\xf3\x1e\xcd\xd9\x6b\xb6\x6f\x8e\x27\xa7\xed\xdb\xe7\xbc\xcc\x1a\x07\xce\x6f\x87\x33\xf0\xba\x51\x17\x22\x66\x78\x79\x8e\xce\xe5\x13\x81\x80\x06\x2c\xe5\x78\x0d\xa1\xb2\xb8\x54\xa8\x79\x09\xbd\xbf\x3c\x47\x01\x8b\x13\x2c\xc9\x32\xaa\xaa\x1d\xd5\xee\xab\x36\xbd\x6c\xfd\x54\x6c\xc8\x08\x01\x3c\xbd\xe7\x07\x88\xb0\x24\x37\x79\x90\x28\x4a\x1d\x10\x1a\x92\x1b\x12\xa6\x38\x42\x40\xc3\x4c\x43\x62\x8e\xae\x36\xb0\x45\x71\x2a\xa4\x9a\x23\x79\x59\xb1\xa8\xf2\xa4\x0c\x60\x9f\xcc\x8d\x40\xf5\x80\xca\xa8\x99\xc3\xa7\x85\x1f\x31\x25\xa9\x82\xc5\x6d\xbd\xd8\x36\x76\x7c\x02\x28\x97\xf6\x1d\x74\x3b\x11\x7e\x91\xae\x32\xf8\x6c\xf4\xe6\x7b\x9a\xa5\x1f\x62\xc6\x21\xcf\x9a\xe5\xed\x8b\x02\xf3\x2c\x33\x33\xdf\x00\xca\xc9\x09\xb4\x04\xf5\xa5\x08\xd7\xc3\x02\x18\x66\xf1\xab\x1e\x83\x37\x4c\xcd\x12\xc1\x1d\x50\xf6\xaa\xbd\xfe\xe2\x73\x48\x38\x08\xa0\x32\x9b\x18\x44\x86\x0b\x6a\xc1\xaa\x26\x96\x2d\x96\x3c\xa0\x54\x65\x73\x87\x15\xca\x15\xe5\xf5\x94\x46\x9f\x33\x1a\x0c\x9a\xb1\x5a\xd9\x6a\x95\xcd\xcb\x7e\xec\x9a\xc5\x94\x3b\x37\x26\x31\xd7\xfc\xe4\x1f\x13\x8c\x31\x75\x9c\xba\xf7\x87\x3c\xa1\xb7\x4f\x17\x1b\x09\x82\x98\xc4\x70\x95\xd3\xe8\x4c\x48\x5a\xa6\xd6\x2a\x3d\x56\x42\x80\x9f\xaf\xae\x2e\x50\x0c\x42\xe0\x35\x34\x3c\x8a\x62\x03\x37\xba\xb2\x27\x04\xda\x25\x8d\x06\xe2\xa0\x13\x8a\xf3\xf5\xec\x10\x72\x67\x88\x90\x3d\x4b\x64\xeb\xaa\xda\x8f\xf7\x5a\x75\x47\x9a\xa8\x51\x70\x26\xd2\x38\xc6\x7c\xbb\x57\xfc\xbd\xe4\x04\x56\xa8\xa0\x54\x9a\x45\xd5\xf7\x0f\x16\xfc\x57\x1c\x3c\xdf\x23\xba\x77\x38\xda\x16\x4b\x31\x53\x6a\x4d\x9a\x15\x63\xe7\xe1\x18\x69\x9f\x22\xe0\x24\xbb\x94\x4b\x97\xee\x2d\xf9\x70\x87\x72\x7b\xe6\xc4\x33\x2a\x66\x5e\x1c\x35\x72\xe3\x2d\xda\x73\xe4\xc7\x51\x6d\xa4\xa1\x2a\x4f\xde\x94\xcb\xb2\x3e\x31\x48\xae\x82\xce\xc9\xc8\x65\xcd\xc3\xb7\x34\xb6\x2b\xdf\x58\x65\x78\x6e\x73\xac\x5e\x24\x0d\x3f\xdc\x70\x23\xc6\xda\x52\x0b\x2d\x63\x7d\xa9\x49\x2d\x54\x48\x28\xc0\x12\x9c\xe3\x63\xc9\x58\x04\x98\x36\x07\xc8\x0a\xa7\x91\xd4\xf0\xbc\xc1\xa8\xb9\x70\xd0\xc6\xa9\xb6\x78\x80\x5a\xa3\xb4\x2c\xf4\x18\x0b\x8a\x9d\xd0\xb4\x55\x10\xee\x0d\xc5\xd6\xe0\x99\x93\xdc\xa1\x04\xbb\xf1\xa7\x23\xd1\xd1\x97\x8c\x87\x13\x0a\x21\x02\xe9\x99\x25\xed\x20\xc5\x92\x66\x3c\x32\x9c\xd6\x06\xb0\x31\x5c\x86\x29\x0a\xcb\x60\x33\x12\xa5\x91\xfc\x96\x75\xd0\x59\xd7\x13\xbd\xd3\x23\x79\xdd\x2a\x90\xa6\x38\x06\x91\x39\x7f\x20\x72\x03\x1c\x2d\x01\x61\xba\x45\x37\x38\x22\x61\x8e\x71\x85\xc4\x32\x15\x28\x60\x61\x16\xb8\x3d\x29\xdc\x4d\x3d\x2f\x12\x13\x7d\xc8\x7e\x37\xee\xa8\x7f\xfa\xdb\xcb\x17\xff\x77\xfd\xf9\x7f\xee\x9f\x3d\xfe\xcf\xa7\xa7\x45\xfb\xcf\x1e\xf7\xf3\xe0\xff\xc4\x51\x0a\x8e\x4c\xcb\x01\xdc\x0a\x65\xb2\x01\x83\xed\x3d\xe4\xa9\xa3\x4e\x2d\x59\xc5\xe8\x2f\x48\x7d\x5a\x6e\x37\xbf\x5c\x9f\x35\x13\x64\x14\xfa\xef\x0b\x68\xa6\x0d\xb4\x8e\xf1\xa8\xff\xbb\x60\xf4\x03\x64\xab\x5b\x81\x65\x51\xe6\xda\xca\xfa\xf0\xb0\xac\x3e\x9c\xca\x26\x0e\x1d\xdb\x57\x5b\xbb\xb4\x9a\xa6\xb6\x9b\x1a\x6b\xd1\x9a\x9e\x7e\x33\x9a\xec\x41\x69\x45\x22\xb8\xb4\x51\xeb\x04\x77\xca\x6f\x7b\x7b\xc8\xb2\xb0\x95\x92\x25\x5b\xd0\x42\xaa\x2a\xdd\x32\x78\x4f\x0c\xab\x68\x46\x6c\xea\x6d\xf4\x5c\x5e\xde\xc4\xac\xa5\xf9\xd1\x00\x9f\x7d\x98\x65\x24\xbd\xc7\x97\xd4\xb3\x3a\xa8\x2b\xa0\x34\x76\xf9\x65\x5f\x2d\x25\x95\x1b\xcf\xd6\xf4\x9b\x5f\x09\x95\xb0\x36\x3f\xdb\xd0\x39\x2a\x93\x1c\x9d\x03\xa2\x4a\xca\xf5\xf6\x10\xb6\x94\x89\x0b\x6a\x70\x12\x13\x49\x6e\x40\xe4\x29\x12\x2b\xbd\x80\x45\x11\x04\xaa\xc2\x8f\x56\x9e\x5c\x6b\xec\x8d\x5a\x0e\x14\x59\x06\x2b\x1e\x24\xcb\xc2\x56\x4a\x31\xbe\x23\x71\x1a\xfb\x51\x2a\x0b\x3b\x1c\x48\x10\xa5\x82\xdc\xc0\xbb\x3e\x24\x8d\x5a\x76\x2e\x09\xed\xc1\x65\x51\xb8\x83\xcb\x3e\x24\x8d\x5a\x2e\x5d\xfe\x02\x74\x2d\x3d\xf1\xef\xae\xb8\x4b\xe6\x5e\xd4\xaa\xe2\x2e\x5c\x5e\xec\x0e\xf5\x5b\x0c\xcb\x0a\xbb\xa4\x3c\xf7\x1f\x2a\x55\x69\x97\x8c\x7d\x68\x95\xa5\xad\xb4\xf4\x9c\xa5\x07\xb9\x7a\x05\xbb\xad\x50\x6f\xfb\xa0\x4e\x9b\x48\x23\x49\x92\x28\x87\x19\x3e\x32\xee\xca\x3b\x46\x7e\x7f\x18\x64\xcc\xcc\x0f\x34\xe9\x36\x8b\xb7\x6c\xa8\xa5\x5b\x54\x4c\x54\x5b\x15\x3a\xf1\x6c\x2d\xfe\x96\xc8\x0d\xba\x7b\x81\x88\xc8\x23\xab\xee\x7d\x3b\x92\xa7\x60\x29\xe3\xdc\xff\xb8\x64\xe1\xf6\xa2\x5a\x59\xdc\x6f\xeb\x45\x7d\x6a\xd1\x76\x1e\xea\xb8\xf1\xfa\x14\xd3\x36\x63\xe5\xd7\xf3\xe4\xbe\x25\xbd\x5e\x05\xeb\x73\x74\xb5\x21\x2a\x2e\x4e\xa3\x30\xdf\xbf\x43\x28\x2a\xd1\xa5\x2a\x9d\x8a\xfd\x76\xd8\x8d\xbc\x67\x65\xc7\xb8\x03\x45\xec\xa3\xb0\x37\x8a\x70\x4c\x68\x91\x51\x8e\x58\x80\xed\x4a\xf3\x81\x62\xca\x96\xbb\xf1\x52\xcd\x80\xfb\xe4\x4a\x5d\x6c\xdf\x6e\x20\x4b\x80\x30\x8e\x28\x93\xf9\xe9\x8d\x8a\x6d\xd5\x59\x65\x7b\xaa\x44\x9e\xc0\xc2\xd1\x7c\x40\x26\xd6\x1a\xce\xf9\xc5\x69\x7b\x6c\xec\xc8\x71\x7b\xe5\x21\x2e\xd3\xe5\x65\x93\x91\x53\x0b\x7b\x3a\xc7\xfa\x17\x6a\x01\xa7\x33\xd0\xf4\x40\x0f\x39\x87\xda\xe4\x54\x87\x3a\xd5\xe3\xc7\xa6\x8e\x20\xd4\x11\xb2\x4e\xb1\xe9\x14\x9b\x4e\xb1\xe9\x14\x9b\xfe\x15\x63\xd3\x47\xf5\xff\x97\x38\xe9\xcf\x14\xf8\x76\x82\x49\x13\x4c\xaa\x7d\xcd\x6c\x62\x42\x49\x87\x43\x49\x19\x33\x6f\xe3\x44\x6e\x9b\xab\x8a\x3e\x86\xaa\x99\x52\x1b\x5b\x59\x33\x02\x09\xa0\x21\xa1\x6b\x84\x6b\x66\xbb\xdc\x16\x0c\xd3\x68\xab\xec\x36\x4b\xd8\x60\x8a\x40\x31\x85\x6e\x14\x57\x13\xc2\xfb\x92\x10\xde\xbf\x88\xdc\xbc\x53\x5e\x7f\x82\x7a\x13\xd4\x9b\xa0\xde\x04\xf5\x90\x01\xf5\x94\xcb\x7b\x83\x25\x9e\xd0\xde\x84\xf6\x6a\x5f\x4b\xb3\x98\x00\xdf\x04\xf8\x6c\xbc\x7f\x19\x80\xaf\xf1\x71\x45\x22\x98\x40\xe0\x04\x02\x27\x10\xd8\x29\xf5\x04\x02\xff\x4a\x20\x30\xc1\x72\xf3\x65\x02\x40\xd7\xc1\xd1\xe2\x6b\xf1\xa9\x7b\xfb\xe4\x20\xc0\x68\x9d\xd4\xb4\xd3\x96\xb5\xa6\xd1\x41\x20\xe6\x89\xc3\x48\x65\x58\x13\x84\x9c\x56\x56\x3b\x0c\xe0\x6b\x83\x5c\x13\xd2\x9a\x90\xd6\x84\xb4\x26\xa4\x85\x0c\xa4\x45\x19\xfd\xff\x63\x6c\x52\xb5\x1f\x1e\x19\x74\x3a\xcd\xb9\x69\xce\xa6\x3a\x0f\x7a\x2d\x19\xc7\x81\x14\x5d\xcb\xd5\x03\xc9\x39\xd0\xb0\xd1\xb3\xcd\xfb\x7a\x2d\x5d\x3a\x48\xe1\xfa\x2e\xe6\x81\x42\x18\x86\xd6\xc1\xbe\xb1\x23\xd3\xf7\x34\xed\x19\x0a\x0b\xc4\x48\x44\xfd\x22\x50\xb6\x42\x58\xbb\xe5\x3d\xa7\x73\xd4\x8b\xc4\x8c\x70\x61\xec\x73\xee\xc3\x81\x8b\xf5\xe2\xd7\x52\x3e\xcf\xeb\xeb\x17\x3b\x71\x16\xda\x7d\xb8\xde\xf0\x7a\x8f\x06\x2d\xa7\x40\x7b\xc1\x9d\x41\x4d\xb6\x61\xa2\x4e\x9f\x3d\xa0\xc5\xae\xe3\x1c\x1d\x40\x6c\x48\x8b\x63\xa0\xb5\x01\xed\x8e\x02\xe9\x86\xc8\x3b\x06\xee\xdb\x4b\xde\xbd\xc0\xa1\x6f\xcb\xda\xfc\xc2\x44\x16\x87\x9c\x17\x31\xd3\x30\x20\x39\x42\xcb\x6f\xf2\xf1\xf4\x72\x10\xf8\x1c\xa0\xf3\xbd\x10\xea\x21\x35\x7d\xe8\x86\xdb\x15\xed\x81\x81\x07\x28\xbb\x13\x28\xc7\xf8\xce\x7d\x8d\xc2\x31\xb4\x7e\x94\xd6\xdb\x55\xef\x4a\xfb\xed\xc3\x40\x3e\xeb\x9f\xe9\x99\x0f\xdf\x08\x65\x88\x27\x73\x86\x31\x9d\x47\xdf\x55\x19\xba\x3d\xee\x15\x0a\xcd\x8c\xaa\x5e\xb9\xf6\x57\x33\x73\x5a\xa1\x89\x7b\x3b\xa0\xb2\xa4\xc2\xf6\xc1\x53\xb5\x00\xca\x23\xe5\xf4\x60\x6a\xb4\x2d\x74\xea\x4e\xed\x3b\xe3\x47\xfb\xed\x82\x3d\x19\xd4\x3b\x6b\xaf\xae\x2b\x2f\x57\xb3\x82\x68\xcb\xed\x88\x2e\xe1\x5c\xd7\x26\xfa\x0a\x65\xe7\xce\x11\x33\xb4\xdd\x66\xe3\x37\xf6\xfa\x70\xd6\x4f\xa1\x21\x51\xd8\x3c\x26\x14\x4b\xc6\x87\x44\x27\x1c\x70\xf8\x9e\x46\xce\xab\x21\x07\x5f\xc1\x76\x17\x1b\x77\xb4\xda\x75\xa0\x0a\x3a\x30\xe1\xf8\x97\x32\x16\x2b\x00\x75\x85\xee\x62\x46\xef\xd3\x85\xb5\x6b\x60\xbe\xf2\x30\x7a\x8c\x0b\x4b\xa6\xd0\xf9\x64\x42\xe7\x07\x41\x41\xe3\x2c\x5d\xf9\x6d\xe9\x39\x98\x3b\x3b\x5d\x67\xd4\x5c\xed\xf2\xf0\x48\x7b\xbd\x2d\x31\xdd\x3f\x34\xad\x44\x76\x51\x9a\x56\x22\xa7\x95\xc8\x69\x25\xf2\xe1\x56\x22\x1f\x00\x32\x6a\x73\x92\xed\xe1\xc6\x7d\x9f\x49\x2c\x69\x7e\xc8\x31\x4c\x0c\xb4\xf2\x54\x3b\x79\x3b\x9e\x4d\xb4\xd1\x18\x3e\x5f\x9a\x93\xa2\x11\xc3\xda\x27\x0b\xaf\x37\x2e\x5c\x37\xfb\xeb\x9a\xd6\xc3\xac\xc3\xcc\xf8\x1e\x5b\x9d\xac\x22\x64\xb7\xed\x26\xb8\xf3\xb9\x3c\xbb\x1f\xe2\xb0\x22\x77\x43\x6a\x62\x29\x39\x59\xa6\xe6\xe5\xcd\x7b\x83\xc0\x5b\x8e\x93\x64\xac\xeb\xca\x4f\x65\xac\x4a\xbc\x1e\xcd\x82\xfa\x3c\x70\x36\xb6\xb5\xed\x79\xef\xec\x68\x00\xff\x54\xfa\xb5\xe3\xf1\xdb\xe1\xbe\xce\x76\x17\xaf\x57\xb6\x6b\x89\x05\x09\xce\x52\xb9\x01\x2a\x49\xbe\xd9\xf4\xd2\xb8\x7a\xbf\x91\x02\xf3\x22\x8c\x13\xf2\x77\xd8\x8e\x43\x8b\xe1\x54\x6e\x5e\x9d\xc7\x49\x44\x02\x22\xc7\xa4\x79\x81\x85\xb8\x65\x3c\x1c\x93\xe6\x59\xa2\xf8\x1c\x51\x95\x05\xd9\x20\x00\x21\x7e\x60\x21\x58\xa9\x56\xff\xbe\xb6\x5a\x5e\x5b\x3f\x1f\xd6\xd3\x3c\xc4\x4d\xba\x99\xb4\x63\x6e\x7d\x3e\x3d\x57\xd2\x18\x5f\x47\xe8\xc3\x06\x8a\x68\x6c\x7f\x3b\x72\x0f\xe7\xe2\x77\x77\xf1\xd0\x99\xab\xdf\x2e\xfe\xd6\xbb\xcd\x1a\xb9\x90\xd1\xaf\xf2\x38\x3d\xdb\x74\xf8\xeb\xe3\xda\xe8\x2a\x62\xb7\xda\x1b\x07\xa9\xdc\x30\x5e\xbc\x68\xfb\x6b\x9f\x97\xf1\xc6\xb1\xd8\x5c\x29\x1e\x49\x30\xc5\xf7\xde\xad\x91\x42\xf9\xdd\xed\x89\x80\x25\xbe\x37\xd7\xe7\x32\x5c\xe6\x35\xac\xd4\x0c\x2d\xf7\x90\xc4\xe3\xf5\xe3\x2f\x7f\x54\x18\x88\xe3\x61\x47\x85\x64\x7f\xc0\xd7\x3f\x1a\x92\x42\xe9\xc7\x1e\x0d\x95\x76\xa7\x51\xa0\x8f\x02\x1b\x46\x9e\x06\x42\xd1\xf2\x01\x07\x02\xde\xe9\x7d\x1a\x0b\xa7\x32\x16\xcc\xc0\xee\xc4\x90\xd2\x5f\x6f\x98\x54\x5d\xf2\x95\xe1\xa7\x69\x10\x3a\x06\xe1\x65\xb3\x17\x47\x58\x78\xd0\x45\xd6\x5b\xd5\x5f\x25\x1d\x71\x49\xa6\x7a\x64\xda\xd0\x6f\xc7\x3a\x4c\xe3\x09\xc0\x6e\x96\x2c\xa7\xa7\x77\x34\x10\x05\x08\x21\x44\x92\x65\x77\xdf\x20\x5c\xbc\xe7\x97\x3f\xf4\x1a\x45\xd6\xe7\x27\x4a\xde\x74\x27\x66\x11\x7d\x70\xba\xd3\x78\xf9\x1e\x0d\xca\xc8\x39\xde\x7c\xb3\xa6\xe1\xbc\xd7\xc1\x6a\x6f\xb3\x0e\x52\xbe\xe4\x98\x8a\x15\x70\x94\x70\x26\x59\xc0\xa2\xf2\x1c\xfb\xd9\xc5\xf9\xbc\xd5\x92\x9c\xa3\xdf\xe6\x1e\xb3\x0d\x49\xba\x87\x50\x5f\x84\xfe\xe9\xd6\xf8\xbb\xe6\xf0\x7a\xeb\xa6\x65\x3b\x86\x8b\x79\x93\xf5\x59\x20\x6e\xb4\xa7\x44\xf4\x3f\xa5\xfe\x67\x42\x12\xdb\xd3\xe7\xbb\xa5\xa3\x8c\x5c\x2b\x97\xbb\xbb\x7f\x8e\xc5\x6e\xed\x43\x5c\xbf\x74\xc8\x8f\xff\xe6\xd6\xbe\x91\xb6\xf5\x95\xe4\xed\x93\xc4\xa8\x5b\xf9\x76\x4d\x35\xb7\xd8\x8c\xb6\x7d\xaf\x72\xe0\xb6\xbd\x01\x63\x9e\x76\xab\x1a\x32\x76\xe4\x8c\x76\xc2\xad\x6c\xa2\x65\xf7\xcf\xf8\xa7\xda\x2a\xb9\x8c\x3d\x3c\xa3\x9d\x64\x33\xe5\x1a\xb5\x2d\xfb\x86\xa2\x5a\x7f\x19\x5b\x7f\xc6\x3f\xd1\x53\xd3\xe2\x41\x5b\xd3\x4f\xf0\xec\xb0\x42\x73\x43\xd2\x68\x27\xd3\x6a\x6a\x34\xf6\x4e\x1e\x52\x8b\x87\x6c\xcc\xae\x44\xfb\x9e\xa7\x51\x4f\x9d\x55\x03\x81\x8e\x67\xfc\xb4\x69\xf0\x3a\x18\xf2\x40\xd0\xf6\xa8\x34\xe3\xc9\x98\xaf\xf6\xda\x24\xd3\xeb\x60\xb9\x0e\xd3\x1f\xa9\xff\xee\x1f\xfd\x37\x00\x00\xff\xff\x69\x5d\x0a\x6a\x39\x9d\x00\x00") + +func v2SchemaJSONBytes() ([]byte, error) { + return bindataRead( + _v2SchemaJSON, + "v2/schema.json", + ) +} + +func v2SchemaJSON() (*asset, error) { + bytes, err := v2SchemaJSONBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "v2/schema.json", size: 40249, mode: os.FileMode(420), modTime: time.Unix(1523760397, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "jsonschema-draft-04.json": jsonschemaDraft04JSON, + "v2/schema.json": v2SchemaJSON, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "jsonschema-draft-04.json": &bintree{jsonschemaDraft04JSON, map[string]*bintree{}}, + "v2": &bintree{nil, map[string]*bintree{ + "schema.json": &bintree{v2SchemaJSON, map[string]*bintree{}}, + }}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/contact_info.go b/src/runtime/vendor/github.com/go-openapi/spec/contact_info.go new file mode 100644 index 000000000..f285970aa --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/contact_info.go @@ -0,0 +1,24 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +// ContactInfo contact information for the exposed API. +// +// For more information: http://goo.gl/8us55a#contactObject +type ContactInfo struct { + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` + Email string `json:"email,omitempty"` +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/debug.go b/src/runtime/vendor/github.com/go-openapi/spec/debug.go new file mode 100644 index 000000000..7edb95a61 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/debug.go @@ -0,0 +1,47 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "fmt" + "log" + "os" + "path/filepath" + "runtime" +) + +var ( + // Debug is true when the SWAGGER_DEBUG env var is not empty. + // It enables a more verbose logging of validators. + Debug = os.Getenv("SWAGGER_DEBUG") != "" + // validateLogger is a debug logger for this package + specLogger *log.Logger +) + +func init() { + debugOptions() +} + +func debugOptions() { + specLogger = log.New(os.Stdout, "spec:", log.LstdFlags) +} + +func debugLog(msg string, args ...interface{}) { + // A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog() + if Debug { + _, file1, pos1, _ := runtime.Caller(1) + specLogger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...)) + } +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/expander.go b/src/runtime/vendor/github.com/go-openapi/spec/expander.go new file mode 100644 index 000000000..456a9dd7e --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/expander.go @@ -0,0 +1,1227 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "fmt" + "log" + "net/url" + "os" + "path" + "path/filepath" + "reflect" + "strings" + "sync" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// ExpandOptions provides options for expand. +type ExpandOptions struct { + RelativeBase string + SkipSchemas bool + ContinueOnError bool + AbsoluteCircularRef bool +} + +// ResolutionCache a cache for resolving urls +type ResolutionCache interface { + Get(string) (interface{}, bool) + Set(string, interface{}) +} + +type simpleCache struct { + lock sync.RWMutex + store map[string]interface{} +} + +var resCache ResolutionCache + +func init() { + resCache = initResolutionCache() +} + +// initResolutionCache initializes the URI resolution cache +func initResolutionCache() ResolutionCache { + return &simpleCache{store: map[string]interface{}{ + "http://swagger.io/v2/schema.json": MustLoadSwagger20Schema(), + "http://json-schema.org/draft-04/schema": MustLoadJSONSchemaDraft04(), + }} +} + +// resolverContext allows to share a context during spec processing. +// At the moment, it just holds the index of circular references found. +type resolverContext struct { + // circulars holds all visited circular references, which allows shortcuts. + // NOTE: this is not just a performance improvement: it is required to figure out + // circular references which participate several cycles. + // This structure is privately instantiated and needs not be locked against + // concurrent access, unless we chose to implement a parallel spec walking. + circulars map[string]bool + basePath string +} + +func newResolverContext(originalBasePath string) *resolverContext { + return &resolverContext{ + circulars: make(map[string]bool), + basePath: originalBasePath, // keep the root base path in context + } +} + +// Get retrieves a cached URI +func (s *simpleCache) Get(uri string) (interface{}, bool) { + debugLog("getting %q from resolution cache", uri) + s.lock.RLock() + v, ok := s.store[uri] + debugLog("got %q from resolution cache: %t", uri, ok) + + s.lock.RUnlock() + return v, ok +} + +// Set caches a URI +func (s *simpleCache) Set(uri string, data interface{}) { + s.lock.Lock() + s.store[uri] = data + s.lock.Unlock() +} + +// ResolveRefWithBase resolves a reference against a context root with preservation of base path +func ResolveRefWithBase(root interface{}, ref *Ref, opts *ExpandOptions) (*Schema, error) { + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return nil, err + } + specBasePath := "" + if opts != nil && opts.RelativeBase != "" { + specBasePath, _ = absPath(opts.RelativeBase) + } + + result := new(Schema) + if err := resolver.Resolve(ref, result, specBasePath); err != nil { + return nil, err + } + return result, nil +} + +// ResolveRef resolves a reference against a context root +// ref is guaranteed to be in root (no need to go to external files) +// ResolveRef is ONLY called from the code generation module +func ResolveRef(root interface{}, ref *Ref) (*Schema, error) { + res, _, err := ref.GetPointer().Get(root) + if err != nil { + panic(err) + } + switch sch := res.(type) { + case Schema: + return &sch, nil + case *Schema: + return sch, nil + case map[string]interface{}: + b, _ := json.Marshal(sch) + newSch := new(Schema) + _ = json.Unmarshal(b, newSch) + return newSch, nil + default: + return nil, fmt.Errorf("unknown type for the resolved reference") + } +} + +// ResolveParameter resolves a parameter reference against a context root +func ResolveParameter(root interface{}, ref Ref) (*Parameter, error) { + return ResolveParameterWithBase(root, ref, nil) +} + +// ResolveParameterWithBase resolves a parameter reference against a context root and base path +func ResolveParameterWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Parameter, error) { + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return nil, err + } + + result := new(Parameter) + if err := resolver.Resolve(&ref, result, ""); err != nil { + return nil, err + } + return result, nil +} + +// ResolveResponse resolves response a reference against a context root +func ResolveResponse(root interface{}, ref Ref) (*Response, error) { + return ResolveResponseWithBase(root, ref, nil) +} + +// ResolveResponseWithBase resolves response a reference against a context root and base path +func ResolveResponseWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Response, error) { + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return nil, err + } + + result := new(Response) + if err := resolver.Resolve(&ref, result, ""); err != nil { + return nil, err + } + return result, nil +} + +// ResolveItems resolves header and parameter items reference against a context root and base path +func ResolveItems(root interface{}, ref Ref, opts *ExpandOptions) (*Items, error) { + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return nil, err + } + basePath := "" + if opts.RelativeBase != "" { + basePath = opts.RelativeBase + } + result := new(Items) + if err := resolver.Resolve(&ref, result, basePath); err != nil { + return nil, err + } + return result, nil +} + +// ResolvePathItem resolves response a path item against a context root and base path +func ResolvePathItem(root interface{}, ref Ref, opts *ExpandOptions) (*PathItem, error) { + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return nil, err + } + basePath := "" + if opts.RelativeBase != "" { + basePath = opts.RelativeBase + } + result := new(PathItem) + if err := resolver.Resolve(&ref, result, basePath); err != nil { + return nil, err + } + return result, nil +} + +type schemaLoader struct { + root interface{} + options *ExpandOptions + cache ResolutionCache + context *resolverContext + loadDoc func(string) (json.RawMessage, error) +} + +var idPtr, _ = jsonpointer.New("/id") +var refPtr, _ = jsonpointer.New("/$ref") + +// PathLoader function to use when loading remote refs +var PathLoader func(string) (json.RawMessage, error) + +func init() { + PathLoader = func(path string) (json.RawMessage, error) { + data, err := swag.LoadFromFileOrHTTP(path) + if err != nil { + return nil, err + } + return json.RawMessage(data), nil + } +} + +func defaultSchemaLoader( + root interface{}, + expandOptions *ExpandOptions, + cache ResolutionCache, + context *resolverContext) (*schemaLoader, error) { + + if cache == nil { + cache = resCache + } + if expandOptions == nil { + expandOptions = &ExpandOptions{} + } + absBase, _ := absPath(expandOptions.RelativeBase) + if context == nil { + context = newResolverContext(absBase) + } + return &schemaLoader{ + root: root, + options: expandOptions, + cache: cache, + context: context, + loadDoc: func(path string) (json.RawMessage, error) { + debugLog("fetching document at %q", path) + return PathLoader(path) + }, + }, nil +} + +func idFromNode(node interface{}) (*Ref, error) { + if idValue, _, err := idPtr.Get(node); err == nil { + if refStr, ok := idValue.(string); ok && refStr != "" { + idRef, err := NewRef(refStr) + if err != nil { + return nil, err + } + return &idRef, nil + } + } + return nil, nil +} + +func nextRef(startingNode interface{}, startingRef *Ref, ptr *jsonpointer.Pointer) *Ref { + if startingRef == nil { + return nil + } + + if ptr == nil { + return startingRef + } + + ret := startingRef + var idRef *Ref + node := startingNode + + for _, tok := range ptr.DecodedTokens() { + node, _, _ = jsonpointer.GetForToken(node, tok) + if node == nil { + break + } + + idRef, _ = idFromNode(node) + if idRef != nil { + nw, err := ret.Inherits(*idRef) + if err != nil { + break + } + ret = nw + } + + refRef, _, _ := refPtr.Get(node) + if refRef != nil { + var rf Ref + switch value := refRef.(type) { + case string: + rf, _ = NewRef(value) + } + nw, err := ret.Inherits(rf) + if err != nil { + break + } + nwURL := nw.GetURL() + if nwURL.Scheme == "file" || (nwURL.Scheme == "" && nwURL.Host == "") { + nwpt := filepath.ToSlash(nwURL.Path) + if filepath.IsAbs(nwpt) { + _, err := os.Stat(nwpt) + if err != nil { + nwURL.Path = filepath.Join(".", nwpt) + } + } + } + + ret = nw + } + + } + + return ret +} + +// normalize absolute path for cache. +// on Windows, drive letters should be converted to lower as scheme in net/url.URL +func normalizeAbsPath(path string) string { + u, err := url.Parse(path) + if err != nil { + debugLog("normalize absolute path failed: %s", err) + return path + } + return u.String() +} + +// base or refPath could be a file path or a URL +// given a base absolute path and a ref path, return the absolute path of refPath +// 1) if refPath is absolute, return it +// 2) if refPath is relative, join it with basePath keeping the scheme, hosts, and ports if exists +// base could be a directory or a full file path +func normalizePaths(refPath, base string) string { + refURL, _ := url.Parse(refPath) + if path.IsAbs(refURL.Path) || filepath.IsAbs(refPath) { + // refPath is actually absolute + if refURL.Host != "" { + return refPath + } + parts := strings.Split(refPath, "#") + result := filepath.FromSlash(parts[0]) + if len(parts) == 2 { + result += "#" + parts[1] + } + return result + } + + // relative refPath + baseURL, _ := url.Parse(base) + if !strings.HasPrefix(refPath, "#") { + // combining paths + if baseURL.Host != "" { + baseURL.Path = path.Join(path.Dir(baseURL.Path), refURL.Path) + } else { // base is a file + newBase := fmt.Sprintf("%s#%s", filepath.Join(filepath.Dir(base), filepath.FromSlash(refURL.Path)), refURL.Fragment) + return newBase + } + + } + // copying fragment from ref to base + baseURL.Fragment = refURL.Fragment + return baseURL.String() +} + +// denormalizePaths returns to simplest notation on file $ref, +// i.e. strips the absolute path and sets a path relative to the base path. +// +// This is currently used when we rewrite ref after a circular ref has been detected +func denormalizeFileRef(ref *Ref, relativeBase, originalRelativeBase string) *Ref { + debugLog("denormalizeFileRef for: %s", ref.String()) + + if ref.String() == "" || ref.IsRoot() || ref.HasFragmentOnly { + return ref + } + // strip relativeBase from URI + relativeBaseURL, _ := url.Parse(relativeBase) + relativeBaseURL.Fragment = "" + + if relativeBaseURL.IsAbs() && strings.HasPrefix(ref.String(), relativeBase) { + // this should work for absolute URI (e.g. http://...): we have an exact match, just trim prefix + r, _ := NewRef(strings.TrimPrefix(ref.String(), relativeBase)) + return &r + } + + if relativeBaseURL.IsAbs() { + // other absolute URL get unchanged (i.e. with a non-empty scheme) + return ref + } + + // for relative file URIs: + originalRelativeBaseURL, _ := url.Parse(originalRelativeBase) + originalRelativeBaseURL.Fragment = "" + if strings.HasPrefix(ref.String(), originalRelativeBaseURL.String()) { + // the resulting ref is in the expanded spec: return a local ref + r, _ := NewRef(strings.TrimPrefix(ref.String(), originalRelativeBaseURL.String())) + return &r + } + + // check if we may set a relative path, considering the original base path for this spec. + // Example: + // spec is located at /mypath/spec.json + // my normalized ref points to: /mypath/item.json#/target + // expected result: item.json#/target + parts := strings.Split(ref.String(), "#") + relativePath, err := filepath.Rel(path.Dir(originalRelativeBaseURL.String()), parts[0]) + if err != nil { + // there is no common ancestor (e.g. different drives on windows) + // leaves the ref unchanged + return ref + } + if len(parts) == 2 { + relativePath += "#" + parts[1] + } + r, _ := NewRef(relativePath) + return &r +} + +// relativeBase could be an ABSOLUTE file path or an ABSOLUTE URL +func normalizeFileRef(ref *Ref, relativeBase string) *Ref { + // This is important for when the reference is pointing to the root schema + if ref.String() == "" { + r, _ := NewRef(relativeBase) + return &r + } + + debugLog("normalizing %s against %s", ref.String(), relativeBase) + + s := normalizePaths(ref.String(), relativeBase) + r, _ := NewRef(s) + return &r +} + +func (r *schemaLoader) resolveRef(ref *Ref, target interface{}, basePath string) error { + tgt := reflect.ValueOf(target) + if tgt.Kind() != reflect.Ptr { + return fmt.Errorf("resolve ref: target needs to be a pointer") + } + + refURL := ref.GetURL() + if refURL == nil { + return nil + } + + var res interface{} + var data interface{} + var err error + // Resolve against the root if it isn't nil, and if ref is pointing at the root, or has a fragment only which means + // it is pointing somewhere in the root. + root := r.root + if (ref.IsRoot() || ref.HasFragmentOnly) && root == nil && basePath != "" { + if baseRef, erb := NewRef(basePath); erb == nil { + root, _, _, _ = r.load(baseRef.GetURL()) + } + } + if (ref.IsRoot() || ref.HasFragmentOnly) && root != nil { + data = root + } else { + baseRef := normalizeFileRef(ref, basePath) + debugLog("current ref is: %s", ref.String()) + debugLog("current ref normalized file: %s", baseRef.String()) + data, _, _, err = r.load(baseRef.GetURL()) + if err != nil { + return err + } + } + + res = data + if ref.String() != "" { + res, _, err = ref.GetPointer().Get(data) + if err != nil { + return err + } + } + if err := swag.DynamicJSONToStruct(res, target); err != nil { + return err + } + + return nil +} + +func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error) { + debugLog("loading schema from url: %s", refURL) + toFetch := *refURL + toFetch.Fragment = "" + + normalized := normalizeAbsPath(toFetch.String()) + + data, fromCache := r.cache.Get(normalized) + if !fromCache { + b, err := r.loadDoc(normalized) + if err != nil { + return nil, url.URL{}, false, err + } + + if err := json.Unmarshal(b, &data); err != nil { + return nil, url.URL{}, false, err + } + r.cache.Set(normalized, data) + } + + return data, toFetch, fromCache, nil +} + +// Resolve resolves a reference against basePath and stores the result in target +// Resolve is not in charge of following references, it only resolves ref by following its URL +// if the schema that ref is referring to has more refs in it. Resolve doesn't resolve them +// if basePath is an empty string, ref is resolved against the root schema stored in the schemaLoader struct +func (r *schemaLoader) Resolve(ref *Ref, target interface{}, basePath string) error { + return r.resolveRef(ref, target, basePath) +} + +// absPath returns the absolute path of a file +func absPath(fname string) (string, error) { + if strings.HasPrefix(fname, "http") { + return fname, nil + } + if filepath.IsAbs(fname) { + return fname, nil + } + wd, err := os.Getwd() + return filepath.Join(wd, fname), err +} + +// ExpandSpec expands the references in a swagger spec +func ExpandSpec(spec *Swagger, options *ExpandOptions) error { + resolver, err := defaultSchemaLoader(spec, options, nil, nil) + // Just in case this ever returns an error. + if shouldStopOnError(err, resolver.options) { + return err + } + + // getting the base path of the spec to adjust all subsequent reference resolutions + specBasePath := "" + if options != nil && options.RelativeBase != "" { + specBasePath, _ = absPath(options.RelativeBase) + } + + if options == nil || !options.SkipSchemas { + for key, definition := range spec.Definitions { + var def *Schema + var err error + if def, err = expandSchema(definition, []string{fmt.Sprintf("#/definitions/%s", key)}, resolver, specBasePath); shouldStopOnError(err, resolver.options) { + return err + } + if def != nil { + spec.Definitions[key] = *def + } + } + } + + for key, parameter := range spec.Parameters { + if err := expandParameter(¶meter, resolver, specBasePath); shouldStopOnError(err, resolver.options) { + return err + } + spec.Parameters[key] = parameter + } + + for key, response := range spec.Responses { + if err := expandResponse(&response, resolver, specBasePath); shouldStopOnError(err, resolver.options) { + return err + } + spec.Responses[key] = response + } + + if spec.Paths != nil { + for key, path := range spec.Paths.Paths { + if err := expandPathItem(&path, resolver, specBasePath); shouldStopOnError(err, resolver.options) { + return err + } + spec.Paths.Paths[key] = path + } + } + + return nil +} + +func shouldStopOnError(err error, opts *ExpandOptions) bool { + if err != nil && !opts.ContinueOnError { + return true + } + + if err != nil { + log.Println(err) + } + + return false +} + +// baseForRoot loads in the cache the root document and produces a fake "root" base path entry +// for further $ref resolution +func baseForRoot(root interface{}, cache ResolutionCache) string { + // cache the root document to resolve $ref's + const rootBase = "root" + if root != nil { + base, _ := absPath(rootBase) + normalizedBase := normalizeAbsPath(base) + debugLog("setting root doc in cache at: %s", normalizedBase) + if cache == nil { + cache = resCache + } + cache.Set(normalizedBase, root) + return rootBase + } + return "" +} + +// ExpandSchema expands the refs in the schema object with reference to the root object +// go-openapi/validate uses this function +// notice that it is impossible to reference a json schema in a different file other than root +func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error { + opts := &ExpandOptions{ + // when a root is specified, cache the root as an in-memory document for $ref retrieval + RelativeBase: baseForRoot(root, cache), + SkipSchemas: false, + ContinueOnError: false, + // when no base path is specified, remaining $ref (circular) are rendered with an absolute path + AbsoluteCircularRef: true, + } + return ExpandSchemaWithBasePath(schema, cache, opts) +} + +// ExpandSchemaWithBasePath expands the refs in the schema object, base path configured through expand options +func ExpandSchemaWithBasePath(schema *Schema, cache ResolutionCache, opts *ExpandOptions) error { + if schema == nil { + return nil + } + + var basePath string + if opts.RelativeBase != "" { + basePath, _ = absPath(opts.RelativeBase) + } + + resolver, err := defaultSchemaLoader(nil, opts, cache, nil) + if err != nil { + return err + } + + refs := []string{""} + var s *Schema + if s, err = expandSchema(*schema, refs, resolver, basePath); err != nil { + return err + } + *schema = *s + return nil +} + +func expandItems(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) { + if target.Items != nil { + if target.Items.Schema != nil { + t, err := expandSchema(*target.Items.Schema, parentRefs, resolver, basePath) + if err != nil { + return nil, err + } + *target.Items.Schema = *t + } + for i := range target.Items.Schemas { + t, err := expandSchema(target.Items.Schemas[i], parentRefs, resolver, basePath) + if err != nil { + return nil, err + } + target.Items.Schemas[i] = *t + } + } + return &target, nil +} + +// basePathFromSchemaID returns a new basePath based on an existing basePath and a schema ID +func basePathFromSchemaID(oldBasePath, id string) string { + u, err := url.Parse(oldBasePath) + if err != nil { + panic(err) + } + uid, err := url.Parse(id) + if err != nil { + panic(err) + } + + if path.IsAbs(uid.Path) { + return id + } + u.Path = path.Join(path.Dir(u.Path), uid.Path) + return u.String() +} + +// isCircular detects cycles in sequences of $ref. +// It relies on a private context (which needs not be locked). +func (r *schemaLoader) isCircular(ref *Ref, basePath string, parentRefs ...string) (foundCycle bool) { + normalizedRef := normalizePaths(ref.String(), basePath) + if _, ok := r.context.circulars[normalizedRef]; ok { + // circular $ref has been already detected in another explored cycle + foundCycle = true + return + } + foundCycle = swag.ContainsStringsCI(parentRefs, normalizedRef) + if foundCycle { + r.context.circulars[normalizedRef] = true + } + return +} + +func updateBasePath(transitive *schemaLoader, resolver *schemaLoader, basePath string) string { + if transitive != resolver { + debugLog("got a new resolver") + if transitive.options != nil && transitive.options.RelativeBase != "" { + basePath, _ = absPath(transitive.options.RelativeBase) + debugLog("new basePath = %s", basePath) + } + } + + return basePath +} + +func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) { + if target.Ref.String() == "" && target.Ref.IsRoot() { + // normalizing is important + newRef := normalizeFileRef(&target.Ref, basePath) + target.Ref = *newRef + return &target, nil + + } + + /* change the base path of resolution when an ID is encountered + otherwise the basePath should inherit the parent's */ + // important: ID can be relative path + if target.ID != "" { + debugLog("schema has ID: %s", target.ID) + // handling the case when id is a folder + // remember that basePath has to be a file + refPath := target.ID + if strings.HasSuffix(target.ID, "/") { + // path.Clean here would not work correctly if basepath is http + refPath = fmt.Sprintf("%s%s", refPath, "placeholder.json") + } + basePath = normalizePaths(refPath, basePath) + } + + /* Explain here what this function does */ + var t *Schema + /* if Ref is found, everything else doesn't matter */ + /* Ref also changes the resolution scope of children expandSchema */ + if target.Ref.String() != "" { + /* Here the resolution scope is changed because a $ref was encountered */ + normalizedRef := normalizeFileRef(&target.Ref, basePath) + normalizedBasePath := normalizedRef.RemoteURI() + + if resolver.isCircular(normalizedRef, basePath, parentRefs...) { + // this means there is a cycle in the recursion tree: return the Ref + // - circular refs cannot be expanded. We leave them as ref. + // - denormalization means that a new local file ref is set relative to the original basePath + debugLog("shortcut circular ref: basePath: %s, normalizedPath: %s, normalized ref: %s", + basePath, normalizedBasePath, normalizedRef.String()) + if !resolver.options.AbsoluteCircularRef { + target.Ref = *denormalizeFileRef(normalizedRef, normalizedBasePath, resolver.context.basePath) + } else { + target.Ref = *normalizedRef + } + return &target, nil + } + + debugLog("basePath: %s", basePath) + if Debug { + b, _ := json.Marshal(target) + debugLog("calling Resolve with target: %s", string(b)) + } + if err := resolver.Resolve(&target.Ref, &t, basePath); shouldStopOnError(err, resolver.options) { + return nil, err + } + + if t != nil { + parentRefs = append(parentRefs, normalizedRef.String()) + var err error + transitiveResolver, err := transitiveResolver(basePath, target.Ref, resolver) + if shouldStopOnError(err, resolver.options) { + return nil, err + } + + basePath = updateBasePath(transitiveResolver, resolver, normalizedBasePath) + + return expandSchema(*t, parentRefs, transitiveResolver, basePath) + } + } + + t, err := expandItems(target, parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return &target, err + } + if t != nil { + target = *t + } + + for i := range target.AllOf { + t, err := expandSchema(target.AllOf[i], parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return &target, err + } + target.AllOf[i] = *t + } + for i := range target.AnyOf { + t, err := expandSchema(target.AnyOf[i], parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return &target, err + } + target.AnyOf[i] = *t + } + for i := range target.OneOf { + t, err := expandSchema(target.OneOf[i], parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return &target, err + } + if t != nil { + target.OneOf[i] = *t + } + } + if target.Not != nil { + t, err := expandSchema(*target.Not, parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return &target, err + } + if t != nil { + *target.Not = *t + } + } + for k := range target.Properties { + t, err := expandSchema(target.Properties[k], parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return &target, err + } + if t != nil { + target.Properties[k] = *t + } + } + if target.AdditionalProperties != nil && target.AdditionalProperties.Schema != nil { + t, err := expandSchema(*target.AdditionalProperties.Schema, parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return &target, err + } + if t != nil { + *target.AdditionalProperties.Schema = *t + } + } + for k := range target.PatternProperties { + t, err := expandSchema(target.PatternProperties[k], parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return &target, err + } + if t != nil { + target.PatternProperties[k] = *t + } + } + for k := range target.Dependencies { + if target.Dependencies[k].Schema != nil { + t, err := expandSchema(*target.Dependencies[k].Schema, parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return &target, err + } + if t != nil { + *target.Dependencies[k].Schema = *t + } + } + } + if target.AdditionalItems != nil && target.AdditionalItems.Schema != nil { + t, err := expandSchema(*target.AdditionalItems.Schema, parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return &target, err + } + if t != nil { + *target.AdditionalItems.Schema = *t + } + } + for k := range target.Definitions { + t, err := expandSchema(target.Definitions[k], parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return &target, err + } + if t != nil { + target.Definitions[k] = *t + } + } + return &target, nil +} + +func derefPathItem(pathItem *PathItem, parentRefs []string, resolver *schemaLoader, basePath string) error { + curRef := pathItem.Ref.String() + if curRef != "" { + normalizedRef := normalizeFileRef(&pathItem.Ref, basePath) + normalizedBasePath := normalizedRef.RemoteURI() + + if resolver.isCircular(normalizedRef, basePath, parentRefs...) { + return nil + } + + if err := resolver.Resolve(&pathItem.Ref, pathItem, basePath); shouldStopOnError(err, resolver.options) { + return err + } + + if pathItem.Ref.String() != "" && pathItem.Ref.String() != curRef && basePath != normalizedBasePath { + parentRefs = append(parentRefs, normalizedRef.String()) + return derefPathItem(pathItem, parentRefs, resolver, normalizedBasePath) + } + } + + return nil +} + +func expandPathItem(pathItem *PathItem, resolver *schemaLoader, basePath string) error { + if pathItem == nil { + return nil + } + + parentRefs := []string{} + if err := derefPathItem(pathItem, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + if pathItem.Ref.String() != "" { + var err error + resolver, err = transitiveResolver(basePath, pathItem.Ref, resolver) + if shouldStopOnError(err, resolver.options) { + return err + } + } + pathItem.Ref = Ref{} + + // Currently unused: + //parentRefs = parentRefs[0:] + + for idx := range pathItem.Parameters { + if err := expandParameter(&(pathItem.Parameters[idx]), resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + } + if err := expandOperation(pathItem.Get, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + if err := expandOperation(pathItem.Head, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + if err := expandOperation(pathItem.Options, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + if err := expandOperation(pathItem.Put, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + if err := expandOperation(pathItem.Post, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + if err := expandOperation(pathItem.Patch, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + if err := expandOperation(pathItem.Delete, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + return nil +} + +func expandOperation(op *Operation, resolver *schemaLoader, basePath string) error { + if op == nil { + return nil + } + + for i, param := range op.Parameters { + if err := expandParameter(¶m, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + op.Parameters[i] = param + } + + if op.Responses != nil { + responses := op.Responses + if err := expandResponse(responses.Default, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + for code, response := range responses.StatusCodeResponses { + if err := expandResponse(&response, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + responses.StatusCodeResponses[code] = response + } + } + return nil +} + +func transitiveResolver(basePath string, ref Ref, resolver *schemaLoader) (*schemaLoader, error) { + if ref.IsRoot() || ref.HasFragmentOnly { + return resolver, nil + } + + baseRef, _ := NewRef(basePath) + currentRef := normalizeFileRef(&ref, basePath) + // Set a new root to resolve against + if !strings.HasPrefix(currentRef.String(), baseRef.String()) { + rootURL := currentRef.GetURL() + rootURL.Fragment = "" + root, _ := resolver.cache.Get(rootURL.String()) + var err error + + // shallow copy of resolver options to set a new RelativeBase when + // traversing multiple documents + newOptions := resolver.options + newOptions.RelativeBase = rootURL.String() + debugLog("setting new root: %s", newOptions.RelativeBase) + resolver, err = defaultSchemaLoader(root, newOptions, resolver.cache, resolver.context) + if err != nil { + return nil, err + } + } + + return resolver, nil +} + +// ExpandResponseWithRoot expands a response based on a root document, not a fetchable document +func ExpandResponseWithRoot(response *Response, root interface{}, cache ResolutionCache) error { + opts := &ExpandOptions{ + RelativeBase: baseForRoot(root, cache), + SkipSchemas: false, + ContinueOnError: false, + // when no base path is specified, remaining $ref (circular) are rendered with an absolute path + AbsoluteCircularRef: true, + } + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return err + } + + return expandResponse(response, resolver, opts.RelativeBase) +} + +// ExpandResponse expands a response based on a basepath +// This is the exported version of expandResponse +// all refs inside response will be resolved relative to basePath +func ExpandResponse(response *Response, basePath string) error { + var specBasePath string + if basePath != "" { + specBasePath, _ = absPath(basePath) + } + opts := &ExpandOptions{ + RelativeBase: specBasePath, + } + resolver, err := defaultSchemaLoader(nil, opts, nil, nil) + if err != nil { + return err + } + + return expandResponse(response, resolver, opts.RelativeBase) +} + +func derefResponse(response *Response, parentRefs []string, resolver *schemaLoader, basePath string) error { + curRef := response.Ref.String() + if curRef != "" { + /* Here the resolution scope is changed because a $ref was encountered */ + normalizedRef := normalizeFileRef(&response.Ref, basePath) + normalizedBasePath := normalizedRef.RemoteURI() + + if resolver.isCircular(normalizedRef, basePath, parentRefs...) { + return nil + } + + if err := resolver.Resolve(&response.Ref, response, basePath); shouldStopOnError(err, resolver.options) { + return err + } + + if response.Ref.String() != "" && response.Ref.String() != curRef && basePath != normalizedBasePath { + parentRefs = append(parentRefs, normalizedRef.String()) + return derefResponse(response, parentRefs, resolver, normalizedBasePath) + } + } + + return nil +} + +func expandResponse(response *Response, resolver *schemaLoader, basePath string) error { + if response == nil { + return nil + } + parentRefs := []string{} + if err := derefResponse(response, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + if response.Ref.String() != "" { + transitiveResolver, err := transitiveResolver(basePath, response.Ref, resolver) + if shouldStopOnError(err, transitiveResolver.options) { + return err + } + basePath = updateBasePath(transitiveResolver, resolver, basePath) + resolver = transitiveResolver + } + if response.Schema != nil && response.Schema.Ref.String() != "" { + // schema expanded to a $ref in another root + var ern error + response.Schema.Ref, ern = NewRef(normalizePaths(response.Schema.Ref.String(), response.Ref.RemoteURI())) + if ern != nil { + return ern + } + } + response.Ref = Ref{} + + parentRefs = parentRefs[0:] + if !resolver.options.SkipSchemas && response.Schema != nil { + // parentRefs = append(parentRefs, response.Schema.Ref.String()) + s, err := expandSchema(*response.Schema, parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return err + } + *response.Schema = *s + } + + return nil +} + +// ExpandParameterWithRoot expands a parameter based on a root document, not a fetchable document +func ExpandParameterWithRoot(parameter *Parameter, root interface{}, cache ResolutionCache) error { + opts := &ExpandOptions{ + RelativeBase: baseForRoot(root, cache), + SkipSchemas: false, + ContinueOnError: false, + // when no base path is specified, remaining $ref (circular) are rendered with an absolute path + AbsoluteCircularRef: true, + } + resolver, err := defaultSchemaLoader(root, opts, nil, nil) + if err != nil { + return err + } + + return expandParameter(parameter, resolver, opts.RelativeBase) +} + +// ExpandParameter expands a parameter based on a basepath +// This is the exported version of expandParameter +// all refs inside parameter will be resolved relative to basePath +func ExpandParameter(parameter *Parameter, basePath string) error { + var specBasePath string + if basePath != "" { + specBasePath, _ = absPath(basePath) + } + opts := &ExpandOptions{ + RelativeBase: specBasePath, + } + resolver, err := defaultSchemaLoader(nil, opts, nil, nil) + if err != nil { + return err + } + + return expandParameter(parameter, resolver, opts.RelativeBase) +} + +func derefParameter(parameter *Parameter, parentRefs []string, resolver *schemaLoader, basePath string) error { + curRef := parameter.Ref.String() + if curRef != "" { + normalizedRef := normalizeFileRef(¶meter.Ref, basePath) + normalizedBasePath := normalizedRef.RemoteURI() + + if resolver.isCircular(normalizedRef, basePath, parentRefs...) { + return nil + } + + if err := resolver.Resolve(¶meter.Ref, parameter, basePath); shouldStopOnError(err, resolver.options) { + return err + } + + if parameter.Ref.String() != "" && parameter.Ref.String() != curRef && basePath != normalizedBasePath { + parentRefs = append(parentRefs, normalizedRef.String()) + return derefParameter(parameter, parentRefs, resolver, normalizedBasePath) + } + } + + return nil +} + +func expandParameter(parameter *Parameter, resolver *schemaLoader, basePath string) error { + if parameter == nil { + return nil + } + + parentRefs := []string{} + if err := derefParameter(parameter, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) { + return err + } + if parameter.Ref.String() != "" { + transitiveResolver, err := transitiveResolver(basePath, parameter.Ref, resolver) + if shouldStopOnError(err, transitiveResolver.options) { + return err + } + basePath = updateBasePath(transitiveResolver, resolver, basePath) + resolver = transitiveResolver + } + + if parameter.Schema != nil && parameter.Schema.Ref.String() != "" { + // schema expanded to a $ref in another root + var ern error + parameter.Schema.Ref, ern = NewRef(normalizePaths(parameter.Schema.Ref.String(), parameter.Ref.RemoteURI())) + if ern != nil { + return ern + } + } + parameter.Ref = Ref{} + + parentRefs = parentRefs[0:] + if !resolver.options.SkipSchemas && parameter.Schema != nil { + s, err := expandSchema(*parameter.Schema, parentRefs, resolver, basePath) + if shouldStopOnError(err, resolver.options) { + return err + } + *parameter.Schema = *s + } + return nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/external_docs.go b/src/runtime/vendor/github.com/go-openapi/spec/external_docs.go new file mode 100644 index 000000000..88add91b2 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/external_docs.go @@ -0,0 +1,24 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +// ExternalDocumentation allows referencing an external resource for +// extended documentation. +// +// For more information: http://goo.gl/8us55a#externalDocumentationObject +type ExternalDocumentation struct { + Description string `json:"description,omitempty"` + URL string `json:"url,omitempty"` +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/go.mod b/src/runtime/vendor/github.com/go-openapi/spec/go.mod new file mode 100644 index 000000000..5af64c10b --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/go.mod @@ -0,0 +1,16 @@ +module github.com/go-openapi/spec + +require ( + github.com/PuerkitoBio/purell v1.1.0 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-openapi/jsonpointer v0.17.0 + github.com/go-openapi/jsonreference v0.17.0 + github.com/go-openapi/swag v0.17.0 + github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.2.2 + golang.org/x/net v0.0.0-20181005035420-146acd28ed58 // indirect + golang.org/x/text v0.3.0 // indirect + gopkg.in/yaml.v2 v2.2.1 +) diff --git a/src/runtime/vendor/github.com/go-openapi/spec/go.sum b/src/runtime/vendor/github.com/go-openapi/spec/go.sum new file mode 100644 index 000000000..ab6bfb608 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/go.sum @@ -0,0 +1,22 @@ +github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-openapi/jsonpointer v0.17.0 h1:Bpl2DtZ6k7wKqfFs7e+4P08+M9I3FQgn09a1UsRUQbk= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonreference v0.17.0 h1:d/o7/fsLWWQZACbihvZxcyLQ59jfUVs7WOJv/ak7T7A= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/swag v0.17.0 h1:7wu+dZ5k83kvUWeAb+WUkFiUhDzwGqzTR/NhWzeo1JU= +github.com/go-openapi/swag v0.17.0/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/src/runtime/vendor/github.com/go-openapi/spec/header.go b/src/runtime/vendor/github.com/go-openapi/spec/header.go new file mode 100644 index 000000000..82f77f770 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/header.go @@ -0,0 +1,193 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// HeaderProps describes a response header +type HeaderProps struct { + Description string `json:"description,omitempty"` +} + +// Header describes a header for a response of the API +// +// For more information: http://goo.gl/8us55a#headerObject +type Header struct { + CommonValidations + SimpleSchema + VendorExtensible + HeaderProps +} + +// ResponseHeader creates a new header instance for use in a response +func ResponseHeader() *Header { + return new(Header) +} + +// WithDescription sets the description on this response, allows for chaining +func (h *Header) WithDescription(description string) *Header { + h.Description = description + return h +} + +// Typed a fluent builder method for the type of parameter +func (h *Header) Typed(tpe, format string) *Header { + h.Type = tpe + h.Format = format + return h +} + +// CollectionOf a fluent builder method for an array item +func (h *Header) CollectionOf(items *Items, format string) *Header { + h.Type = "array" + h.Items = items + h.CollectionFormat = format + return h +} + +// WithDefault sets the default value on this item +func (h *Header) WithDefault(defaultValue interface{}) *Header { + h.Default = defaultValue + return h +} + +// WithMaxLength sets a max length value +func (h *Header) WithMaxLength(max int64) *Header { + h.MaxLength = &max + return h +} + +// WithMinLength sets a min length value +func (h *Header) WithMinLength(min int64) *Header { + h.MinLength = &min + return h +} + +// WithPattern sets a pattern value +func (h *Header) WithPattern(pattern string) *Header { + h.Pattern = pattern + return h +} + +// WithMultipleOf sets a multiple of value +func (h *Header) WithMultipleOf(number float64) *Header { + h.MultipleOf = &number + return h +} + +// WithMaximum sets a maximum number value +func (h *Header) WithMaximum(max float64, exclusive bool) *Header { + h.Maximum = &max + h.ExclusiveMaximum = exclusive + return h +} + +// WithMinimum sets a minimum number value +func (h *Header) WithMinimum(min float64, exclusive bool) *Header { + h.Minimum = &min + h.ExclusiveMinimum = exclusive + return h +} + +// WithEnum sets a the enum values (replace) +func (h *Header) WithEnum(values ...interface{}) *Header { + h.Enum = append([]interface{}{}, values...) + return h +} + +// WithMaxItems sets the max items +func (h *Header) WithMaxItems(size int64) *Header { + h.MaxItems = &size + return h +} + +// WithMinItems sets the min items +func (h *Header) WithMinItems(size int64) *Header { + h.MinItems = &size + return h +} + +// UniqueValues dictates that this array can only have unique items +func (h *Header) UniqueValues() *Header { + h.UniqueItems = true + return h +} + +// AllowDuplicates this array can have duplicates +func (h *Header) AllowDuplicates() *Header { + h.UniqueItems = false + return h +} + +// MarshalJSON marshal this to JSON +func (h Header) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(h.CommonValidations) + if err != nil { + return nil, err + } + b2, err := json.Marshal(h.SimpleSchema) + if err != nil { + return nil, err + } + b3, err := json.Marshal(h.HeaderProps) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2, b3), nil +} + +// UnmarshalJSON unmarshals this header from JSON +func (h *Header) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &h.CommonValidations); err != nil { + return err + } + if err := json.Unmarshal(data, &h.SimpleSchema); err != nil { + return err + } + if err := json.Unmarshal(data, &h.VendorExtensible); err != nil { + return err + } + return json.Unmarshal(data, &h.HeaderProps) +} + +// JSONLookup look up a value by the json property name +func (h Header) JSONLookup(token string) (interface{}, error) { + if ex, ok := h.Extensions[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(h.CommonValidations, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(h.SimpleSchema, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(h.HeaderProps, token) + return r, err +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/info.go b/src/runtime/vendor/github.com/go-openapi/spec/info.go new file mode 100644 index 000000000..cfb37ec12 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/info.go @@ -0,0 +1,168 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// Extensions vendor specific extensions +type Extensions map[string]interface{} + +// Add adds a value to these extensions +func (e Extensions) Add(key string, value interface{}) { + realKey := strings.ToLower(key) + e[realKey] = value +} + +// GetString gets a string value from the extensions +func (e Extensions) GetString(key string) (string, bool) { + if v, ok := e[strings.ToLower(key)]; ok { + str, ok := v.(string) + return str, ok + } + return "", false +} + +// GetBool gets a string value from the extensions +func (e Extensions) GetBool(key string) (bool, bool) { + if v, ok := e[strings.ToLower(key)]; ok { + str, ok := v.(bool) + return str, ok + } + return false, false +} + +// GetStringSlice gets a string value from the extensions +func (e Extensions) GetStringSlice(key string) ([]string, bool) { + if v, ok := e[strings.ToLower(key)]; ok { + arr, isSlice := v.([]interface{}) + if !isSlice { + return nil, false + } + var strs []string + for _, iface := range arr { + str, isString := iface.(string) + if !isString { + return nil, false + } + strs = append(strs, str) + } + return strs, ok + } + return nil, false +} + +// VendorExtensible composition block. +type VendorExtensible struct { + Extensions Extensions +} + +// AddExtension adds an extension to this extensible object +func (v *VendorExtensible) AddExtension(key string, value interface{}) { + if value == nil { + return + } + if v.Extensions == nil { + v.Extensions = make(map[string]interface{}) + } + v.Extensions.Add(key, value) +} + +// MarshalJSON marshals the extensions to json +func (v VendorExtensible) MarshalJSON() ([]byte, error) { + toser := make(map[string]interface{}) + for k, v := range v.Extensions { + lk := strings.ToLower(k) + if strings.HasPrefix(lk, "x-") { + toser[k] = v + } + } + return json.Marshal(toser) +} + +// UnmarshalJSON for this extensible object +func (v *VendorExtensible) UnmarshalJSON(data []byte) error { + var d map[string]interface{} + if err := json.Unmarshal(data, &d); err != nil { + return err + } + for k, vv := range d { + lk := strings.ToLower(k) + if strings.HasPrefix(lk, "x-") { + if v.Extensions == nil { + v.Extensions = map[string]interface{}{} + } + v.Extensions[k] = vv + } + } + return nil +} + +// InfoProps the properties for an info definition +type InfoProps struct { + Description string `json:"description,omitempty"` + Title string `json:"title,omitempty"` + TermsOfService string `json:"termsOfService,omitempty"` + Contact *ContactInfo `json:"contact,omitempty"` + License *License `json:"license,omitempty"` + Version string `json:"version,omitempty"` +} + +// Info object provides metadata about the API. +// The metadata can be used by the clients if needed, and can be presented in the Swagger-UI for convenience. +// +// For more information: http://goo.gl/8us55a#infoObject +type Info struct { + VendorExtensible + InfoProps +} + +// JSONLookup look up a value by the json property name +func (i Info) JSONLookup(token string) (interface{}, error) { + if ex, ok := i.Extensions[token]; ok { + return &ex, nil + } + r, _, err := jsonpointer.GetForToken(i.InfoProps, token) + return r, err +} + +// MarshalJSON marshal this to JSON +func (i Info) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(i.InfoProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(i.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON marshal this from JSON +func (i *Info) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &i.InfoProps); err != nil { + return err + } + if err := json.Unmarshal(data, &i.VendorExtensible); err != nil { + return err + } + return nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/items.go b/src/runtime/vendor/github.com/go-openapi/spec/items.go new file mode 100644 index 000000000..cf4298971 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/items.go @@ -0,0 +1,233 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// SimpleSchema describe swagger simple schemas for parameters and headers +type SimpleSchema struct { + Type string `json:"type,omitempty"` + Format string `json:"format,omitempty"` + Items *Items `json:"items,omitempty"` + CollectionFormat string `json:"collectionFormat,omitempty"` + Default interface{} `json:"default,omitempty"` + Example interface{} `json:"example,omitempty"` +} + +// TypeName return the type (or format) of a simple schema +func (s *SimpleSchema) TypeName() string { + if s.Format != "" { + return s.Format + } + return s.Type +} + +// ItemsTypeName yields the type of items in a simple schema array +func (s *SimpleSchema) ItemsTypeName() string { + if s.Items == nil { + return "" + } + return s.Items.TypeName() +} + +// CommonValidations describe common JSON-schema validations +type CommonValidations struct { + Maximum *float64 `json:"maximum,omitempty"` + ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` + Minimum *float64 `json:"minimum,omitempty"` + ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` + MaxLength *int64 `json:"maxLength,omitempty"` + MinLength *int64 `json:"minLength,omitempty"` + Pattern string `json:"pattern,omitempty"` + MaxItems *int64 `json:"maxItems,omitempty"` + MinItems *int64 `json:"minItems,omitempty"` + UniqueItems bool `json:"uniqueItems,omitempty"` + MultipleOf *float64 `json:"multipleOf,omitempty"` + Enum []interface{} `json:"enum,omitempty"` +} + +// Items a limited subset of JSON-Schema's items object. +// It is used by parameter definitions that are not located in "body". +// +// For more information: http://goo.gl/8us55a#items-object +type Items struct { + Refable + CommonValidations + SimpleSchema + VendorExtensible +} + +// NewItems creates a new instance of items +func NewItems() *Items { + return &Items{} +} + +// Typed a fluent builder method for the type of item +func (i *Items) Typed(tpe, format string) *Items { + i.Type = tpe + i.Format = format + return i +} + +// CollectionOf a fluent builder method for an array item +func (i *Items) CollectionOf(items *Items, format string) *Items { + i.Type = "array" + i.Items = items + i.CollectionFormat = format + return i +} + +// WithDefault sets the default value on this item +func (i *Items) WithDefault(defaultValue interface{}) *Items { + i.Default = defaultValue + return i +} + +// WithMaxLength sets a max length value +func (i *Items) WithMaxLength(max int64) *Items { + i.MaxLength = &max + return i +} + +// WithMinLength sets a min length value +func (i *Items) WithMinLength(min int64) *Items { + i.MinLength = &min + return i +} + +// WithPattern sets a pattern value +func (i *Items) WithPattern(pattern string) *Items { + i.Pattern = pattern + return i +} + +// WithMultipleOf sets a multiple of value +func (i *Items) WithMultipleOf(number float64) *Items { + i.MultipleOf = &number + return i +} + +// WithMaximum sets a maximum number value +func (i *Items) WithMaximum(max float64, exclusive bool) *Items { + i.Maximum = &max + i.ExclusiveMaximum = exclusive + return i +} + +// WithMinimum sets a minimum number value +func (i *Items) WithMinimum(min float64, exclusive bool) *Items { + i.Minimum = &min + i.ExclusiveMinimum = exclusive + return i +} + +// WithEnum sets a the enum values (replace) +func (i *Items) WithEnum(values ...interface{}) *Items { + i.Enum = append([]interface{}{}, values...) + return i +} + +// WithMaxItems sets the max items +func (i *Items) WithMaxItems(size int64) *Items { + i.MaxItems = &size + return i +} + +// WithMinItems sets the min items +func (i *Items) WithMinItems(size int64) *Items { + i.MinItems = &size + return i +} + +// UniqueValues dictates that this array can only have unique items +func (i *Items) UniqueValues() *Items { + i.UniqueItems = true + return i +} + +// AllowDuplicates this array can have duplicates +func (i *Items) AllowDuplicates() *Items { + i.UniqueItems = false + return i +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (i *Items) UnmarshalJSON(data []byte) error { + var validations CommonValidations + if err := json.Unmarshal(data, &validations); err != nil { + return err + } + var ref Refable + if err := json.Unmarshal(data, &ref); err != nil { + return err + } + var simpleSchema SimpleSchema + if err := json.Unmarshal(data, &simpleSchema); err != nil { + return err + } + var vendorExtensible VendorExtensible + if err := json.Unmarshal(data, &vendorExtensible); err != nil { + return err + } + i.Refable = ref + i.CommonValidations = validations + i.SimpleSchema = simpleSchema + i.VendorExtensible = vendorExtensible + return nil +} + +// MarshalJSON converts this items object to JSON +func (i Items) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(i.CommonValidations) + if err != nil { + return nil, err + } + b2, err := json.Marshal(i.SimpleSchema) + if err != nil { + return nil, err + } + b3, err := json.Marshal(i.Refable) + if err != nil { + return nil, err + } + b4, err := json.Marshal(i.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b4, b3, b1, b2), nil +} + +// JSONLookup look up a value by the json property name +func (i Items) JSONLookup(token string) (interface{}, error) { + if token == "$ref" { + return &i.Ref, nil + } + + r, _, err := jsonpointer.GetForToken(i.CommonValidations, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(i.SimpleSchema, token) + return r, err +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/license.go b/src/runtime/vendor/github.com/go-openapi/spec/license.go new file mode 100644 index 000000000..f20961b4f --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/license.go @@ -0,0 +1,23 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +// License information for the exposed API. +// +// For more information: http://goo.gl/8us55a#licenseObject +type License struct { + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/operation.go b/src/runtime/vendor/github.com/go-openapi/spec/operation.go new file mode 100644 index 000000000..32f7d8fe7 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/operation.go @@ -0,0 +1,259 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// OperationProps describes an operation +type OperationProps struct { + Description string `json:"description,omitempty"` + Consumes []string `json:"consumes,omitempty"` + Produces []string `json:"produces,omitempty"` + Schemes []string `json:"schemes,omitempty"` // the scheme, when present must be from [http, https, ws, wss] + Tags []string `json:"tags,omitempty"` + Summary string `json:"summary,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` + ID string `json:"operationId,omitempty"` + Deprecated bool `json:"deprecated,omitempty"` + Security []map[string][]string `json:"security,omitempty"` //Special case, see MarshalJSON function + Parameters []Parameter `json:"parameters,omitempty"` + Responses *Responses `json:"responses,omitempty"` +} + +// MarshalJSON takes care of serializing operation properties to JSON +// +// We use a custom marhaller here to handle a special cases related to +// the Security field. We need to preserve zero length slice +// while omitting the field when the value is nil/unset. +func (op OperationProps) MarshalJSON() ([]byte, error) { + type Alias OperationProps + if op.Security == nil { + return json.Marshal(&struct { + Security []map[string][]string `json:"security,omitempty"` + *Alias + }{ + Security: op.Security, + Alias: (*Alias)(&op), + }) + } + return json.Marshal(&struct { + Security []map[string][]string `json:"security"` + *Alias + }{ + Security: op.Security, + Alias: (*Alias)(&op), + }) +} + +// Operation describes a single API operation on a path. +// +// For more information: http://goo.gl/8us55a#operationObject +type Operation struct { + VendorExtensible + OperationProps +} + +// SuccessResponse gets a success response model +func (o *Operation) SuccessResponse() (*Response, int, bool) { + if o.Responses == nil { + return nil, 0, false + } + + for k, v := range o.Responses.StatusCodeResponses { + if k/100 == 2 { + return &v, k, true + } + } + + return o.Responses.Default, 0, false +} + +// JSONLookup look up a value by the json property name +func (o Operation) JSONLookup(token string) (interface{}, error) { + if ex, ok := o.Extensions[token]; ok { + return &ex, nil + } + r, _, err := jsonpointer.GetForToken(o.OperationProps, token) + return r, err +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (o *Operation) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &o.OperationProps); err != nil { + return err + } + if err := json.Unmarshal(data, &o.VendorExtensible); err != nil { + return err + } + return nil +} + +// MarshalJSON converts this items object to JSON +func (o Operation) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(o.OperationProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(o.VendorExtensible) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b1, b2) + return concated, nil +} + +// NewOperation creates a new operation instance. +// It expects an ID as parameter but not passing an ID is also valid. +func NewOperation(id string) *Operation { + op := new(Operation) + op.ID = id + return op +} + +// WithID sets the ID property on this operation, allows for chaining. +func (o *Operation) WithID(id string) *Operation { + o.ID = id + return o +} + +// WithDescription sets the description on this operation, allows for chaining +func (o *Operation) WithDescription(description string) *Operation { + o.Description = description + return o +} + +// WithSummary sets the summary on this operation, allows for chaining +func (o *Operation) WithSummary(summary string) *Operation { + o.Summary = summary + return o +} + +// WithExternalDocs sets/removes the external docs for/from this operation. +// When you pass empty strings as params the external documents will be removed. +// When you pass non-empty string as one value then those values will be used on the external docs object. +// So when you pass a non-empty description, you should also pass the url and vice versa. +func (o *Operation) WithExternalDocs(description, url string) *Operation { + if description == "" && url == "" { + o.ExternalDocs = nil + return o + } + + if o.ExternalDocs == nil { + o.ExternalDocs = &ExternalDocumentation{} + } + o.ExternalDocs.Description = description + o.ExternalDocs.URL = url + return o +} + +// Deprecate marks the operation as deprecated +func (o *Operation) Deprecate() *Operation { + o.Deprecated = true + return o +} + +// Undeprecate marks the operation as not deprected +func (o *Operation) Undeprecate() *Operation { + o.Deprecated = false + return o +} + +// WithConsumes adds media types for incoming body values +func (o *Operation) WithConsumes(mediaTypes ...string) *Operation { + o.Consumes = append(o.Consumes, mediaTypes...) + return o +} + +// WithProduces adds media types for outgoing body values +func (o *Operation) WithProduces(mediaTypes ...string) *Operation { + o.Produces = append(o.Produces, mediaTypes...) + return o +} + +// WithTags adds tags for this operation +func (o *Operation) WithTags(tags ...string) *Operation { + o.Tags = append(o.Tags, tags...) + return o +} + +// AddParam adds a parameter to this operation, when a parameter for that location +// and with that name already exists it will be replaced +func (o *Operation) AddParam(param *Parameter) *Operation { + if param == nil { + return o + } + + for i, p := range o.Parameters { + if p.Name == param.Name && p.In == param.In { + params := append(o.Parameters[:i], *param) + params = append(params, o.Parameters[i+1:]...) + o.Parameters = params + return o + } + } + + o.Parameters = append(o.Parameters, *param) + return o +} + +// RemoveParam removes a parameter from the operation +func (o *Operation) RemoveParam(name, in string) *Operation { + for i, p := range o.Parameters { + if p.Name == name && p.In == name { + o.Parameters = append(o.Parameters[:i], o.Parameters[i+1:]...) + return o + } + } + return o +} + +// SecuredWith adds a security scope to this operation. +func (o *Operation) SecuredWith(name string, scopes ...string) *Operation { + o.Security = append(o.Security, map[string][]string{name: scopes}) + return o +} + +// WithDefaultResponse adds a default response to the operation. +// Passing a nil value will remove the response +func (o *Operation) WithDefaultResponse(response *Response) *Operation { + return o.RespondsWith(0, response) +} + +// RespondsWith adds a status code response to the operation. +// When the code is 0 the value of the response will be used as default response value. +// When the value of the response is nil it will be removed from the operation +func (o *Operation) RespondsWith(code int, response *Response) *Operation { + if o.Responses == nil { + o.Responses = new(Responses) + } + if code == 0 { + o.Responses.Default = response + return o + } + if response == nil { + delete(o.Responses.StatusCodeResponses, code) + return o + } + if o.Responses.StatusCodeResponses == nil { + o.Responses.StatusCodeResponses = make(map[int]Response) + } + o.Responses.StatusCodeResponses[code] = *response + return o +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/parameter.go b/src/runtime/vendor/github.com/go-openapi/spec/parameter.go new file mode 100644 index 000000000..cb1a88d25 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/parameter.go @@ -0,0 +1,302 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// QueryParam creates a query parameter +func QueryParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "query"}} +} + +// HeaderParam creates a header parameter, this is always required by default +func HeaderParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "header", Required: true}} +} + +// PathParam creates a path parameter, this is always required +func PathParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "path", Required: true}} +} + +// BodyParam creates a body parameter +func BodyParam(name string, schema *Schema) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "body", Schema: schema}, SimpleSchema: SimpleSchema{Type: "object"}} +} + +// FormDataParam creates a body parameter +func FormDataParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "formData"}} +} + +// FileParam creates a body parameter +func FileParam(name string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name, In: "formData"}, SimpleSchema: SimpleSchema{Type: "file"}} +} + +// SimpleArrayParam creates a param for a simple array (string, int, date etc) +func SimpleArrayParam(name, tpe, fmt string) *Parameter { + return &Parameter{ParamProps: ParamProps{Name: name}, SimpleSchema: SimpleSchema{Type: "array", CollectionFormat: "csv", Items: &Items{SimpleSchema: SimpleSchema{Type: "string", Format: fmt}}}} +} + +// ParamRef creates a parameter that's a json reference +func ParamRef(uri string) *Parameter { + p := new(Parameter) + p.Ref = MustCreateRef(uri) + return p +} + +// ParamProps describes the specific attributes of an operation parameter +type ParamProps struct { + Description string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + In string `json:"in,omitempty"` + Required bool `json:"required,omitempty"` + Schema *Schema `json:"schema,omitempty"` // when in == "body" + AllowEmptyValue bool `json:"allowEmptyValue,omitempty"` // when in == "query" || "formData" +} + +// Parameter a unique parameter is defined by a combination of a [name](#parameterName) and [location](#parameterIn). +// +// There are five possible parameter types. +// * Path - Used together with [Path Templating](#pathTemplating), where the parameter value is actually part of the operation's URL. This does not include the host or base path of the API. For example, in `/items/{itemId}`, the path parameter is `itemId`. +// * Query - Parameters that are appended to the URL. For example, in `/items?id=###`, the query parameter is `id`. +// * Header - Custom headers that are expected as part of the request. +// * Body - The payload that's appended to the HTTP request. Since there can only be one payload, there can only be *one* body parameter. The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only. Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation. +// * Form - Used to describe the payload of an HTTP request when either `application/x-www-form-urlencoded` or `multipart/form-data` are used as the content type of the request (in Swagger's definition, the [`consumes`](#operationConsumes) property of an operation). This is the only parameter type that can be used to send files, thus supporting the `file` type. Since form parameters are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form parameters have a different format based on the content-type used (for further details, consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4): +// * `application/x-www-form-urlencoded` - Similar to the format of Query parameters but as a payload. For example, `foo=1&bar=swagger` - both `foo` and `bar` are form parameters. This is normally used for simple parameters that are being transferred. +// * `multipart/form-data` - each parameter takes a section in the payload with an internal header. For example, for the header `Content-Disposition: form-data; name="submit-name"` the name of the parameter is `submit-name`. This type of form parameters is more commonly used for file transfers. +// +// For more information: http://goo.gl/8us55a#parameterObject +type Parameter struct { + Refable + CommonValidations + SimpleSchema + VendorExtensible + ParamProps +} + +// JSONLookup look up a value by the json property name +func (p Parameter) JSONLookup(token string) (interface{}, error) { + if ex, ok := p.Extensions[token]; ok { + return &ex, nil + } + if token == "$ref" { + return &p.Ref, nil + } + + r, _, err := jsonpointer.GetForToken(p.CommonValidations, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(p.SimpleSchema, token) + if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { + return nil, err + } + if r != nil { + return r, nil + } + r, _, err = jsonpointer.GetForToken(p.ParamProps, token) + return r, err +} + +// WithDescription a fluent builder method for the description of the parameter +func (p *Parameter) WithDescription(description string) *Parameter { + p.Description = description + return p +} + +// Named a fluent builder method to override the name of the parameter +func (p *Parameter) Named(name string) *Parameter { + p.Name = name + return p +} + +// WithLocation a fluent builder method to override the location of the parameter +func (p *Parameter) WithLocation(in string) *Parameter { + p.In = in + return p +} + +// Typed a fluent builder method for the type of the parameter value +func (p *Parameter) Typed(tpe, format string) *Parameter { + p.Type = tpe + p.Format = format + return p +} + +// CollectionOf a fluent builder method for an array parameter +func (p *Parameter) CollectionOf(items *Items, format string) *Parameter { + p.Type = "array" + p.Items = items + p.CollectionFormat = format + return p +} + +// WithDefault sets the default value on this parameter +func (p *Parameter) WithDefault(defaultValue interface{}) *Parameter { + p.AsOptional() // with default implies optional + p.Default = defaultValue + return p +} + +// AllowsEmptyValues flags this parameter as being ok with empty values +func (p *Parameter) AllowsEmptyValues() *Parameter { + p.AllowEmptyValue = true + return p +} + +// NoEmptyValues flags this parameter as not liking empty values +func (p *Parameter) NoEmptyValues() *Parameter { + p.AllowEmptyValue = false + return p +} + +// AsOptional flags this parameter as optional +func (p *Parameter) AsOptional() *Parameter { + p.Required = false + return p +} + +// AsRequired flags this parameter as required +func (p *Parameter) AsRequired() *Parameter { + if p.Default != nil { // with a default required makes no sense + return p + } + p.Required = true + return p +} + +// WithMaxLength sets a max length value +func (p *Parameter) WithMaxLength(max int64) *Parameter { + p.MaxLength = &max + return p +} + +// WithMinLength sets a min length value +func (p *Parameter) WithMinLength(min int64) *Parameter { + p.MinLength = &min + return p +} + +// WithPattern sets a pattern value +func (p *Parameter) WithPattern(pattern string) *Parameter { + p.Pattern = pattern + return p +} + +// WithMultipleOf sets a multiple of value +func (p *Parameter) WithMultipleOf(number float64) *Parameter { + p.MultipleOf = &number + return p +} + +// WithMaximum sets a maximum number value +func (p *Parameter) WithMaximum(max float64, exclusive bool) *Parameter { + p.Maximum = &max + p.ExclusiveMaximum = exclusive + return p +} + +// WithMinimum sets a minimum number value +func (p *Parameter) WithMinimum(min float64, exclusive bool) *Parameter { + p.Minimum = &min + p.ExclusiveMinimum = exclusive + return p +} + +// WithEnum sets a the enum values (replace) +func (p *Parameter) WithEnum(values ...interface{}) *Parameter { + p.Enum = append([]interface{}{}, values...) + return p +} + +// WithMaxItems sets the max items +func (p *Parameter) WithMaxItems(size int64) *Parameter { + p.MaxItems = &size + return p +} + +// WithMinItems sets the min items +func (p *Parameter) WithMinItems(size int64) *Parameter { + p.MinItems = &size + return p +} + +// UniqueValues dictates that this array can only have unique items +func (p *Parameter) UniqueValues() *Parameter { + p.UniqueItems = true + return p +} + +// AllowDuplicates this array can have duplicates +func (p *Parameter) AllowDuplicates() *Parameter { + p.UniqueItems = false + return p +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (p *Parameter) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &p.CommonValidations); err != nil { + return err + } + if err := json.Unmarshal(data, &p.Refable); err != nil { + return err + } + if err := json.Unmarshal(data, &p.SimpleSchema); err != nil { + return err + } + if err := json.Unmarshal(data, &p.VendorExtensible); err != nil { + return err + } + if err := json.Unmarshal(data, &p.ParamProps); err != nil { + return err + } + return nil +} + +// MarshalJSON converts this items object to JSON +func (p Parameter) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(p.CommonValidations) + if err != nil { + return nil, err + } + b2, err := json.Marshal(p.SimpleSchema) + if err != nil { + return nil, err + } + b3, err := json.Marshal(p.Refable) + if err != nil { + return nil, err + } + b4, err := json.Marshal(p.VendorExtensible) + if err != nil { + return nil, err + } + b5, err := json.Marshal(p.ParamProps) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b3, b1, b2, b4, b5), nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/path_item.go b/src/runtime/vendor/github.com/go-openapi/spec/path_item.go new file mode 100644 index 000000000..a8ae63ece --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/path_item.go @@ -0,0 +1,90 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// PathItemProps the path item specific properties +type PathItemProps struct { + Get *Operation `json:"get,omitempty"` + Put *Operation `json:"put,omitempty"` + Post *Operation `json:"post,omitempty"` + Delete *Operation `json:"delete,omitempty"` + Options *Operation `json:"options,omitempty"` + Head *Operation `json:"head,omitempty"` + Patch *Operation `json:"patch,omitempty"` + Parameters []Parameter `json:"parameters,omitempty"` +} + +// PathItem describes the operations available on a single path. +// A Path Item may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering). +// The path itself is still exposed to the documentation viewer but they will +// not know which operations and parameters are available. +// +// For more information: http://goo.gl/8us55a#pathItemObject +type PathItem struct { + Refable + VendorExtensible + PathItemProps +} + +// JSONLookup look up a value by the json property name +func (p PathItem) JSONLookup(token string) (interface{}, error) { + if ex, ok := p.Extensions[token]; ok { + return &ex, nil + } + if token == "$ref" { + return &p.Ref, nil + } + r, _, err := jsonpointer.GetForToken(p.PathItemProps, token) + return r, err +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (p *PathItem) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &p.Refable); err != nil { + return err + } + if err := json.Unmarshal(data, &p.VendorExtensible); err != nil { + return err + } + if err := json.Unmarshal(data, &p.PathItemProps); err != nil { + return err + } + return nil +} + +// MarshalJSON converts this items object to JSON +func (p PathItem) MarshalJSON() ([]byte, error) { + b3, err := json.Marshal(p.Refable) + if err != nil { + return nil, err + } + b4, err := json.Marshal(p.VendorExtensible) + if err != nil { + return nil, err + } + b5, err := json.Marshal(p.PathItemProps) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b3, b4, b5) + return concated, nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/paths.go b/src/runtime/vendor/github.com/go-openapi/spec/paths.go new file mode 100644 index 000000000..9dc82a290 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/paths.go @@ -0,0 +1,97 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/go-openapi/swag" +) + +// Paths holds the relative paths to the individual endpoints. +// The path is appended to the [`basePath`](http://goo.gl/8us55a#swaggerBasePath) in order +// to construct the full URL. +// The Paths may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering). +// +// For more information: http://goo.gl/8us55a#pathsObject +type Paths struct { + VendorExtensible + Paths map[string]PathItem `json:"-"` // custom serializer to flatten this, each entry must start with "/" +} + +// JSONLookup look up a value by the json property name +func (p Paths) JSONLookup(token string) (interface{}, error) { + if pi, ok := p.Paths[token]; ok { + return &pi, nil + } + if ex, ok := p.Extensions[token]; ok { + return &ex, nil + } + return nil, fmt.Errorf("object has no field %q", token) +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (p *Paths) UnmarshalJSON(data []byte) error { + var res map[string]json.RawMessage + if err := json.Unmarshal(data, &res); err != nil { + return err + } + for k, v := range res { + if strings.HasPrefix(strings.ToLower(k), "x-") { + if p.Extensions == nil { + p.Extensions = make(map[string]interface{}) + } + var d interface{} + if err := json.Unmarshal(v, &d); err != nil { + return err + } + p.Extensions[k] = d + } + if strings.HasPrefix(k, "/") { + if p.Paths == nil { + p.Paths = make(map[string]PathItem) + } + var pi PathItem + if err := json.Unmarshal(v, &pi); err != nil { + return err + } + p.Paths[k] = pi + } + } + return nil +} + +// MarshalJSON converts this items object to JSON +func (p Paths) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(p.VendorExtensible) + if err != nil { + return nil, err + } + + pths := make(map[string]PathItem) + for k, v := range p.Paths { + if strings.HasPrefix(k, "/") { + pths[k] = v + } + } + b2, err := json.Marshal(pths) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b1, b2) + return concated, nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/ref.go b/src/runtime/vendor/github.com/go-openapi/spec/ref.go new file mode 100644 index 000000000..1405bfd8e --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/ref.go @@ -0,0 +1,167 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "net/http" + "os" + "path/filepath" + + "github.com/go-openapi/jsonreference" +) + +// Refable is a struct for things that accept a $ref property +type Refable struct { + Ref Ref +} + +// MarshalJSON marshals the ref to json +func (r Refable) MarshalJSON() ([]byte, error) { + return r.Ref.MarshalJSON() +} + +// UnmarshalJSON unmarshalss the ref from json +func (r *Refable) UnmarshalJSON(d []byte) error { + return json.Unmarshal(d, &r.Ref) +} + +// Ref represents a json reference that is potentially resolved +type Ref struct { + jsonreference.Ref +} + +// RemoteURI gets the remote uri part of the ref +func (r *Ref) RemoteURI() string { + if r.String() == "" { + return r.String() + } + + u := *r.GetURL() + u.Fragment = "" + return u.String() +} + +// IsValidURI returns true when the url the ref points to can be found +func (r *Ref) IsValidURI(basepaths ...string) bool { + if r.String() == "" { + return true + } + + v := r.RemoteURI() + if v == "" { + return true + } + + if r.HasFullURL { + rr, err := http.Get(v) + if err != nil { + return false + } + + return rr.StatusCode/100 == 2 + } + + if !(r.HasFileScheme || r.HasFullFilePath || r.HasURLPathOnly) { + return false + } + + // check for local file + pth := v + if r.HasURLPathOnly { + base := "." + if len(basepaths) > 0 { + base = filepath.Dir(filepath.Join(basepaths...)) + } + p, e := filepath.Abs(filepath.ToSlash(filepath.Join(base, pth))) + if e != nil { + return false + } + pth = p + } + + fi, err := os.Stat(filepath.ToSlash(pth)) + if err != nil { + return false + } + + return !fi.IsDir() +} + +// Inherits creates a new reference from a parent and a child +// If the child cannot inherit from the parent, an error is returned +func (r *Ref) Inherits(child Ref) (*Ref, error) { + ref, err := r.Ref.Inherits(child.Ref) + if err != nil { + return nil, err + } + return &Ref{Ref: *ref}, nil +} + +// NewRef creates a new instance of a ref object +// returns an error when the reference uri is an invalid uri +func NewRef(refURI string) (Ref, error) { + ref, err := jsonreference.New(refURI) + if err != nil { + return Ref{}, err + } + return Ref{Ref: ref}, nil +} + +// MustCreateRef creates a ref object but panics when refURI is invalid. +// Use the NewRef method for a version that returns an error. +func MustCreateRef(refURI string) Ref { + return Ref{Ref: jsonreference.MustCreateRef(refURI)} +} + +// MarshalJSON marshals this ref into a JSON object +func (r Ref) MarshalJSON() ([]byte, error) { + str := r.String() + if str == "" { + if r.IsRoot() { + return []byte(`{"$ref":""}`), nil + } + return []byte("{}"), nil + } + v := map[string]interface{}{"$ref": str} + return json.Marshal(v) +} + +// UnmarshalJSON unmarshals this ref from a JSON object +func (r *Ref) UnmarshalJSON(d []byte) error { + var v map[string]interface{} + if err := json.Unmarshal(d, &v); err != nil { + return err + } + return r.fromMap(v) +} + +func (r *Ref) fromMap(v map[string]interface{}) error { + if v == nil { + return nil + } + + if vv, ok := v["$ref"]; ok { + if str, ok := vv.(string); ok { + ref, err := jsonreference.New(str) + if err != nil { + return err + } + *r = Ref{Ref: ref} + } + } + + return nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/response.go b/src/runtime/vendor/github.com/go-openapi/spec/response.go new file mode 100644 index 000000000..586db0d78 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/response.go @@ -0,0 +1,134 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// ResponseProps properties specific to a response +type ResponseProps struct { + Description string `json:"description,omitempty"` + Schema *Schema `json:"schema,omitempty"` + Headers map[string]Header `json:"headers,omitempty"` + Examples map[string]interface{} `json:"examples,omitempty"` +} + +// Response describes a single response from an API Operation. +// +// For more information: http://goo.gl/8us55a#responseObject +type Response struct { + Refable + ResponseProps + VendorExtensible +} + +// JSONLookup look up a value by the json property name +func (r Response) JSONLookup(token string) (interface{}, error) { + if ex, ok := r.Extensions[token]; ok { + return &ex, nil + } + if token == "$ref" { + return &r.Ref, nil + } + ptr, _, err := jsonpointer.GetForToken(r.ResponseProps, token) + return ptr, err +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (r *Response) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &r.ResponseProps); err != nil { + return err + } + if err := json.Unmarshal(data, &r.Refable); err != nil { + return err + } + if err := json.Unmarshal(data, &r.VendorExtensible); err != nil { + return err + } + return nil +} + +// MarshalJSON converts this items object to JSON +func (r Response) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(r.ResponseProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(r.Refable) + if err != nil { + return nil, err + } + b3, err := json.Marshal(r.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2, b3), nil +} + +// NewResponse creates a new response instance +func NewResponse() *Response { + return new(Response) +} + +// ResponseRef creates a response as a json reference +func ResponseRef(url string) *Response { + resp := NewResponse() + resp.Ref = MustCreateRef(url) + return resp +} + +// WithDescription sets the description on this response, allows for chaining +func (r *Response) WithDescription(description string) *Response { + r.Description = description + return r +} + +// WithSchema sets the schema on this response, allows for chaining. +// Passing a nil argument removes the schema from this response +func (r *Response) WithSchema(schema *Schema) *Response { + r.Schema = schema + return r +} + +// AddHeader adds a header to this response +func (r *Response) AddHeader(name string, header *Header) *Response { + if header == nil { + return r.RemoveHeader(name) + } + if r.Headers == nil { + r.Headers = make(map[string]Header) + } + r.Headers[name] = *header + return r +} + +// RemoveHeader removes a header from this response +func (r *Response) RemoveHeader(name string) *Response { + delete(r.Headers, name) + return r +} + +// AddExample adds an example to this response +func (r *Response) AddExample(mediaType string, example interface{}) *Response { + if r.Examples == nil { + r.Examples = make(map[string]interface{}) + } + r.Examples[mediaType] = example + return r +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/responses.go b/src/runtime/vendor/github.com/go-openapi/spec/responses.go new file mode 100644 index 000000000..4efb6f868 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/responses.go @@ -0,0 +1,127 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" + + "github.com/go-openapi/swag" +) + +// Responses is a container for the expected responses of an operation. +// The container maps a HTTP response code to the expected response. +// It is not expected from the documentation to necessarily cover all possible HTTP response codes, +// since they may not be known in advance. However, it is expected from the documentation to cover +// a successful operation response and any known errors. +// +// The `default` can be used a default response object for all HTTP codes that are not covered +// individually by the specification. +// +// The `Responses Object` MUST contain at least one response code, and it SHOULD be the response +// for a successful operation call. +// +// For more information: http://goo.gl/8us55a#responsesObject +type Responses struct { + VendorExtensible + ResponsesProps +} + +// JSONLookup implements an interface to customize json pointer lookup +func (r Responses) JSONLookup(token string) (interface{}, error) { + if token == "default" { + return r.Default, nil + } + if ex, ok := r.Extensions[token]; ok { + return &ex, nil + } + if i, err := strconv.Atoi(token); err == nil { + if scr, ok := r.StatusCodeResponses[i]; ok { + return scr, nil + } + } + return nil, fmt.Errorf("object has no field %q", token) +} + +// UnmarshalJSON hydrates this items instance with the data from JSON +func (r *Responses) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &r.ResponsesProps); err != nil { + return err + } + if err := json.Unmarshal(data, &r.VendorExtensible); err != nil { + return err + } + if reflect.DeepEqual(ResponsesProps{}, r.ResponsesProps) { + r.ResponsesProps = ResponsesProps{} + } + return nil +} + +// MarshalJSON converts this items object to JSON +func (r Responses) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(r.ResponsesProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(r.VendorExtensible) + if err != nil { + return nil, err + } + concated := swag.ConcatJSON(b1, b2) + return concated, nil +} + +// ResponsesProps describes all responses for an operation. +// It tells what is the default response and maps all responses with a +// HTTP status code. +type ResponsesProps struct { + Default *Response + StatusCodeResponses map[int]Response +} + +// MarshalJSON marshals responses as JSON +func (r ResponsesProps) MarshalJSON() ([]byte, error) { + toser := map[string]Response{} + if r.Default != nil { + toser["default"] = *r.Default + } + for k, v := range r.StatusCodeResponses { + toser[strconv.Itoa(k)] = v + } + return json.Marshal(toser) +} + +// UnmarshalJSON unmarshals responses from JSON +func (r *ResponsesProps) UnmarshalJSON(data []byte) error { + var res map[string]Response + if err := json.Unmarshal(data, &res); err != nil { + return nil + } + if v, ok := res["default"]; ok { + r.Default = &v + delete(res, "default") + } + for k, v := range res { + if nk, err := strconv.Atoi(k); err == nil { + if r.StatusCodeResponses == nil { + r.StatusCodeResponses = map[int]Response{} + } + r.StatusCodeResponses[nk] = v + } + } + return nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/schema.go b/src/runtime/vendor/github.com/go-openapi/spec/schema.go new file mode 100644 index 000000000..b9481e29b --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/schema.go @@ -0,0 +1,636 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "fmt" + "net/url" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// BooleanProperty creates a boolean property +func BooleanProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"boolean"}}} +} + +// BoolProperty creates a boolean property +func BoolProperty() *Schema { return BooleanProperty() } + +// StringProperty creates a string property +func StringProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} +} + +// CharProperty creates a string property +func CharProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}}} +} + +// Float64Property creates a float64/double property +func Float64Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "double"}} +} + +// Float32Property creates a float32/float property +func Float32Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"number"}, Format: "float"}} +} + +// Int8Property creates an int8 property +func Int8Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int8"}} +} + +// Int16Property creates an int16 property +func Int16Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int16"}} +} + +// Int32Property creates an int32 property +func Int32Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int32"}} +} + +// Int64Property creates an int64 property +func Int64Property() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"integer"}, Format: "int64"}} +} + +// StrFmtProperty creates a property for the named string format +func StrFmtProperty(format string) *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: format}} +} + +// DateProperty creates a date property +func DateProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date"}} +} + +// DateTimeProperty creates a date time property +func DateTimeProperty() *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"string"}, Format: "date-time"}} +} + +// MapProperty creates a map property +func MapProperty(property *Schema) *Schema { + return &Schema{SchemaProps: SchemaProps{Type: []string{"object"}, AdditionalProperties: &SchemaOrBool{Allows: true, Schema: property}}} +} + +// RefProperty creates a ref property +func RefProperty(name string) *Schema { + return &Schema{SchemaProps: SchemaProps{Ref: MustCreateRef(name)}} +} + +// RefSchema creates a ref property +func RefSchema(name string) *Schema { + return &Schema{SchemaProps: SchemaProps{Ref: MustCreateRef(name)}} +} + +// ArrayProperty creates an array property +func ArrayProperty(items *Schema) *Schema { + if items == nil { + return &Schema{SchemaProps: SchemaProps{Type: []string{"array"}}} + } + return &Schema{SchemaProps: SchemaProps{Items: &SchemaOrArray{Schema: items}, Type: []string{"array"}}} +} + +// ComposedSchema creates a schema with allOf +func ComposedSchema(schemas ...Schema) *Schema { + s := new(Schema) + s.AllOf = schemas + return s +} + +// SchemaURL represents a schema url +type SchemaURL string + +// MarshalJSON marshal this to JSON +func (r SchemaURL) MarshalJSON() ([]byte, error) { + if r == "" { + return []byte("{}"), nil + } + v := map[string]interface{}{"$schema": string(r)} + return json.Marshal(v) +} + +// UnmarshalJSON unmarshal this from JSON +func (r *SchemaURL) UnmarshalJSON(data []byte) error { + var v map[string]interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + return r.fromMap(v) +} + +func (r *SchemaURL) fromMap(v map[string]interface{}) error { + if v == nil { + return nil + } + if vv, ok := v["$schema"]; ok { + if str, ok := vv.(string); ok { + u, err := url.Parse(str) + if err != nil { + return err + } + + *r = SchemaURL(u.String()) + } + } + return nil +} + +// type ExtraSchemaProps map[string]interface{} + +// // JSONSchema represents a structure that is a json schema draft 04 +// type JSONSchema struct { +// SchemaProps +// ExtraSchemaProps +// } + +// // MarshalJSON marshal this to JSON +// func (s JSONSchema) MarshalJSON() ([]byte, error) { +// b1, err := json.Marshal(s.SchemaProps) +// if err != nil { +// return nil, err +// } +// b2, err := s.Ref.MarshalJSON() +// if err != nil { +// return nil, err +// } +// b3, err := s.Schema.MarshalJSON() +// if err != nil { +// return nil, err +// } +// b4, err := json.Marshal(s.ExtraSchemaProps) +// if err != nil { +// return nil, err +// } +// return swag.ConcatJSON(b1, b2, b3, b4), nil +// } + +// // UnmarshalJSON marshal this from JSON +// func (s *JSONSchema) UnmarshalJSON(data []byte) error { +// var sch JSONSchema +// if err := json.Unmarshal(data, &sch.SchemaProps); err != nil { +// return err +// } +// if err := json.Unmarshal(data, &sch.Ref); err != nil { +// return err +// } +// if err := json.Unmarshal(data, &sch.Schema); err != nil { +// return err +// } +// if err := json.Unmarshal(data, &sch.ExtraSchemaProps); err != nil { +// return err +// } +// *s = sch +// return nil +// } + +// SchemaProps describes a JSON schema (draft 4) +type SchemaProps struct { + ID string `json:"id,omitempty"` + Ref Ref `json:"-"` + Schema SchemaURL `json:"-"` + Description string `json:"description,omitempty"` + Type StringOrArray `json:"type,omitempty"` + Format string `json:"format,omitempty"` + Title string `json:"title,omitempty"` + Default interface{} `json:"default,omitempty"` + Maximum *float64 `json:"maximum,omitempty"` + ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` + Minimum *float64 `json:"minimum,omitempty"` + ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty"` + MaxLength *int64 `json:"maxLength,omitempty"` + MinLength *int64 `json:"minLength,omitempty"` + Pattern string `json:"pattern,omitempty"` + MaxItems *int64 `json:"maxItems,omitempty"` + MinItems *int64 `json:"minItems,omitempty"` + UniqueItems bool `json:"uniqueItems,omitempty"` + MultipleOf *float64 `json:"multipleOf,omitempty"` + Enum []interface{} `json:"enum,omitempty"` + MaxProperties *int64 `json:"maxProperties,omitempty"` + MinProperties *int64 `json:"minProperties,omitempty"` + Required []string `json:"required,omitempty"` + Items *SchemaOrArray `json:"items,omitempty"` + AllOf []Schema `json:"allOf,omitempty"` + OneOf []Schema `json:"oneOf,omitempty"` + AnyOf []Schema `json:"anyOf,omitempty"` + Not *Schema `json:"not,omitempty"` + Properties map[string]Schema `json:"properties,omitempty"` + AdditionalProperties *SchemaOrBool `json:"additionalProperties,omitempty"` + PatternProperties map[string]Schema `json:"patternProperties,omitempty"` + Dependencies Dependencies `json:"dependencies,omitempty"` + AdditionalItems *SchemaOrBool `json:"additionalItems,omitempty"` + Definitions Definitions `json:"definitions,omitempty"` +} + +// SwaggerSchemaProps are additional properties supported by swagger schemas, but not JSON-schema (draft 4) +type SwaggerSchemaProps struct { + Discriminator string `json:"discriminator,omitempty"` + ReadOnly bool `json:"readOnly,omitempty"` + XML *XMLObject `json:"xml,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` + Example interface{} `json:"example,omitempty"` +} + +// Schema the schema object allows the definition of input and output data types. +// These types can be objects, but also primitives and arrays. +// This object is based on the [JSON Schema Specification Draft 4](http://json-schema.org/) +// and uses a predefined subset of it. +// On top of this subset, there are extensions provided by this specification to allow for more complete documentation. +// +// For more information: http://goo.gl/8us55a#schemaObject +type Schema struct { + VendorExtensible + SchemaProps + SwaggerSchemaProps + ExtraProps map[string]interface{} `json:"-"` +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s Schema) JSONLookup(token string) (interface{}, error) { + if ex, ok := s.Extensions[token]; ok { + return &ex, nil + } + + if ex, ok := s.ExtraProps[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(s.SchemaProps, token) + if r != nil || (err != nil && !strings.HasPrefix(err.Error(), "object has no field")) { + return r, err + } + r, _, err = jsonpointer.GetForToken(s.SwaggerSchemaProps, token) + return r, err +} + +// WithID sets the id for this schema, allows for chaining +func (s *Schema) WithID(id string) *Schema { + s.ID = id + return s +} + +// WithTitle sets the title for this schema, allows for chaining +func (s *Schema) WithTitle(title string) *Schema { + s.Title = title + return s +} + +// WithDescription sets the description for this schema, allows for chaining +func (s *Schema) WithDescription(description string) *Schema { + s.Description = description + return s +} + +// WithProperties sets the properties for this schema +func (s *Schema) WithProperties(schemas map[string]Schema) *Schema { + s.Properties = schemas + return s +} + +// SetProperty sets a property on this schema +func (s *Schema) SetProperty(name string, schema Schema) *Schema { + if s.Properties == nil { + s.Properties = make(map[string]Schema) + } + s.Properties[name] = schema + return s +} + +// WithAllOf sets the all of property +func (s *Schema) WithAllOf(schemas ...Schema) *Schema { + s.AllOf = schemas + return s +} + +// WithMaxProperties sets the max number of properties an object can have +func (s *Schema) WithMaxProperties(max int64) *Schema { + s.MaxProperties = &max + return s +} + +// WithMinProperties sets the min number of properties an object must have +func (s *Schema) WithMinProperties(min int64) *Schema { + s.MinProperties = &min + return s +} + +// Typed sets the type of this schema for a single value item +func (s *Schema) Typed(tpe, format string) *Schema { + s.Type = []string{tpe} + s.Format = format + return s +} + +// AddType adds a type with potential format to the types for this schema +func (s *Schema) AddType(tpe, format string) *Schema { + s.Type = append(s.Type, tpe) + if format != "" { + s.Format = format + } + return s +} + +// CollectionOf a fluent builder method for an array parameter +func (s *Schema) CollectionOf(items Schema) *Schema { + s.Type = []string{"array"} + s.Items = &SchemaOrArray{Schema: &items} + return s +} + +// WithDefault sets the default value on this parameter +func (s *Schema) WithDefault(defaultValue interface{}) *Schema { + s.Default = defaultValue + return s +} + +// WithRequired flags this parameter as required +func (s *Schema) WithRequired(items ...string) *Schema { + s.Required = items + return s +} + +// AddRequired adds field names to the required properties array +func (s *Schema) AddRequired(items ...string) *Schema { + s.Required = append(s.Required, items...) + return s +} + +// WithMaxLength sets a max length value +func (s *Schema) WithMaxLength(max int64) *Schema { + s.MaxLength = &max + return s +} + +// WithMinLength sets a min length value +func (s *Schema) WithMinLength(min int64) *Schema { + s.MinLength = &min + return s +} + +// WithPattern sets a pattern value +func (s *Schema) WithPattern(pattern string) *Schema { + s.Pattern = pattern + return s +} + +// WithMultipleOf sets a multiple of value +func (s *Schema) WithMultipleOf(number float64) *Schema { + s.MultipleOf = &number + return s +} + +// WithMaximum sets a maximum number value +func (s *Schema) WithMaximum(max float64, exclusive bool) *Schema { + s.Maximum = &max + s.ExclusiveMaximum = exclusive + return s +} + +// WithMinimum sets a minimum number value +func (s *Schema) WithMinimum(min float64, exclusive bool) *Schema { + s.Minimum = &min + s.ExclusiveMinimum = exclusive + return s +} + +// WithEnum sets a the enum values (replace) +func (s *Schema) WithEnum(values ...interface{}) *Schema { + s.Enum = append([]interface{}{}, values...) + return s +} + +// WithMaxItems sets the max items +func (s *Schema) WithMaxItems(size int64) *Schema { + s.MaxItems = &size + return s +} + +// WithMinItems sets the min items +func (s *Schema) WithMinItems(size int64) *Schema { + s.MinItems = &size + return s +} + +// UniqueValues dictates that this array can only have unique items +func (s *Schema) UniqueValues() *Schema { + s.UniqueItems = true + return s +} + +// AllowDuplicates this array can have duplicates +func (s *Schema) AllowDuplicates() *Schema { + s.UniqueItems = false + return s +} + +// AddToAllOf adds a schema to the allOf property +func (s *Schema) AddToAllOf(schemas ...Schema) *Schema { + s.AllOf = append(s.AllOf, schemas...) + return s +} + +// WithDiscriminator sets the name of the discriminator field +func (s *Schema) WithDiscriminator(discriminator string) *Schema { + s.Discriminator = discriminator + return s +} + +// AsReadOnly flags this schema as readonly +func (s *Schema) AsReadOnly() *Schema { + s.ReadOnly = true + return s +} + +// AsWritable flags this schema as writeable (not read-only) +func (s *Schema) AsWritable() *Schema { + s.ReadOnly = false + return s +} + +// WithExample sets the example for this schema +func (s *Schema) WithExample(example interface{}) *Schema { + s.Example = example + return s +} + +// WithExternalDocs sets/removes the external docs for/from this schema. +// When you pass empty strings as params the external documents will be removed. +// When you pass non-empty string as one value then those values will be used on the external docs object. +// So when you pass a non-empty description, you should also pass the url and vice versa. +func (s *Schema) WithExternalDocs(description, url string) *Schema { + if description == "" && url == "" { + s.ExternalDocs = nil + return s + } + + if s.ExternalDocs == nil { + s.ExternalDocs = &ExternalDocumentation{} + } + s.ExternalDocs.Description = description + s.ExternalDocs.URL = url + return s +} + +// WithXMLName sets the xml name for the object +func (s *Schema) WithXMLName(name string) *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Name = name + return s +} + +// WithXMLNamespace sets the xml namespace for the object +func (s *Schema) WithXMLNamespace(namespace string) *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Namespace = namespace + return s +} + +// WithXMLPrefix sets the xml prefix for the object +func (s *Schema) WithXMLPrefix(prefix string) *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Prefix = prefix + return s +} + +// AsXMLAttribute flags this object as xml attribute +func (s *Schema) AsXMLAttribute() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Attribute = true + return s +} + +// AsXMLElement flags this object as an xml node +func (s *Schema) AsXMLElement() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Attribute = false + return s +} + +// AsWrappedXML flags this object as wrapped, this is mostly useful for array types +func (s *Schema) AsWrappedXML() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Wrapped = true + return s +} + +// AsUnwrappedXML flags this object as an xml node +func (s *Schema) AsUnwrappedXML() *Schema { + if s.XML == nil { + s.XML = new(XMLObject) + } + s.XML.Wrapped = false + return s +} + +// MarshalJSON marshal this to JSON +func (s Schema) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(s.SchemaProps) + if err != nil { + return nil, fmt.Errorf("schema props %v", err) + } + b2, err := json.Marshal(s.VendorExtensible) + if err != nil { + return nil, fmt.Errorf("vendor props %v", err) + } + b3, err := s.Ref.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("ref prop %v", err) + } + b4, err := s.Schema.MarshalJSON() + if err != nil { + return nil, fmt.Errorf("schema prop %v", err) + } + b5, err := json.Marshal(s.SwaggerSchemaProps) + if err != nil { + return nil, fmt.Errorf("common validations %v", err) + } + var b6 []byte + if s.ExtraProps != nil { + jj, err := json.Marshal(s.ExtraProps) + if err != nil { + return nil, fmt.Errorf("extra props %v", err) + } + b6 = jj + } + return swag.ConcatJSON(b1, b2, b3, b4, b5, b6), nil +} + +// UnmarshalJSON marshal this from JSON +func (s *Schema) UnmarshalJSON(data []byte) error { + props := struct { + SchemaProps + SwaggerSchemaProps + }{} + if err := json.Unmarshal(data, &props); err != nil { + return err + } + + sch := Schema{ + SchemaProps: props.SchemaProps, + SwaggerSchemaProps: props.SwaggerSchemaProps, + } + + var d map[string]interface{} + if err := json.Unmarshal(data, &d); err != nil { + return err + } + + _ = sch.Ref.fromMap(d) + _ = sch.Schema.fromMap(d) + + delete(d, "$ref") + delete(d, "$schema") + for _, pn := range swag.DefaultJSONNameProvider.GetJSONNames(s) { + delete(d, pn) + } + + for k, vv := range d { + lk := strings.ToLower(k) + if strings.HasPrefix(lk, "x-") { + if sch.Extensions == nil { + sch.Extensions = map[string]interface{}{} + } + sch.Extensions[k] = vv + continue + } + if sch.ExtraProps == nil { + sch.ExtraProps = map[string]interface{}{} + } + sch.ExtraProps[k] = vv + } + + *s = sch + + return nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/security_scheme.go b/src/runtime/vendor/github.com/go-openapi/spec/security_scheme.go new file mode 100644 index 000000000..9f1b454ea --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/security_scheme.go @@ -0,0 +1,143 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +const ( + basic = "basic" + apiKey = "apiKey" + oauth2 = "oauth2" + implicit = "implicit" + password = "password" + application = "application" + accessCode = "accessCode" +) + +// BasicAuth creates a basic auth security scheme +func BasicAuth() *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{Type: basic}} +} + +// APIKeyAuth creates an api key auth security scheme +func APIKeyAuth(fieldName, valueSource string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{Type: apiKey, Name: fieldName, In: valueSource}} +} + +// OAuth2Implicit creates an implicit flow oauth2 security scheme +func OAuth2Implicit(authorizationURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: implicit, + AuthorizationURL: authorizationURL, + }} +} + +// OAuth2Password creates a password flow oauth2 security scheme +func OAuth2Password(tokenURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: password, + TokenURL: tokenURL, + }} +} + +// OAuth2Application creates an application flow oauth2 security scheme +func OAuth2Application(tokenURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: application, + TokenURL: tokenURL, + }} +} + +// OAuth2AccessToken creates an access token flow oauth2 security scheme +func OAuth2AccessToken(authorizationURL, tokenURL string) *SecurityScheme { + return &SecurityScheme{SecuritySchemeProps: SecuritySchemeProps{ + Type: oauth2, + Flow: accessCode, + AuthorizationURL: authorizationURL, + TokenURL: tokenURL, + }} +} + +// SecuritySchemeProps describes a swagger security scheme in the securityDefinitions section +type SecuritySchemeProps struct { + Description string `json:"description,omitempty"` + Type string `json:"type"` + Name string `json:"name,omitempty"` // api key + In string `json:"in,omitempty"` // api key + Flow string `json:"flow,omitempty"` // oauth2 + AuthorizationURL string `json:"authorizationUrl,omitempty"` // oauth2 + TokenURL string `json:"tokenUrl,omitempty"` // oauth2 + Scopes map[string]string `json:"scopes,omitempty"` // oauth2 +} + +// AddScope adds a scope to this security scheme +func (s *SecuritySchemeProps) AddScope(scope, description string) { + if s.Scopes == nil { + s.Scopes = make(map[string]string) + } + s.Scopes[scope] = description +} + +// SecurityScheme allows the definition of a security scheme that can be used by the operations. +// Supported schemes are basic authentication, an API key (either as a header or as a query parameter) +// and OAuth2's common flows (implicit, password, application and access code). +// +// For more information: http://goo.gl/8us55a#securitySchemeObject +type SecurityScheme struct { + VendorExtensible + SecuritySchemeProps +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SecurityScheme) JSONLookup(token string) (interface{}, error) { + if ex, ok := s.Extensions[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(s.SecuritySchemeProps, token) + return r, err +} + +// MarshalJSON marshal this to JSON +func (s SecurityScheme) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(s.SecuritySchemeProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(s.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON marshal this from JSON +func (s *SecurityScheme) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &s.SecuritySchemeProps); err != nil { + return err + } + if err := json.Unmarshal(data, &s.VendorExtensible); err != nil { + return err + } + return nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/spec.go b/src/runtime/vendor/github.com/go-openapi/spec/spec.go new file mode 100644 index 000000000..0bb045bc0 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/spec.go @@ -0,0 +1,86 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import "encoding/json" + +//go:generate curl -L --progress -o ./schemas/v2/schema.json http://swagger.io/v2/schema.json +//go:generate curl -L --progress -o ./schemas/jsonschema-draft-04.json http://json-schema.org/draft-04/schema +//go:generate go-bindata -pkg=spec -prefix=./schemas -ignore=.*\.md ./schemas/... +//go:generate perl -pi -e s,Json,JSON,g bindata.go + +const ( + // SwaggerSchemaURL the url for the swagger 2.0 schema to validate specs + SwaggerSchemaURL = "http://swagger.io/v2/schema.json#" + // JSONSchemaURL the url for the json schema schema + JSONSchemaURL = "http://json-schema.org/draft-04/schema#" +) + +var ( + jsonSchema *Schema + swaggerSchema *Schema +) + +func init() { + jsonSchema = MustLoadJSONSchemaDraft04() + swaggerSchema = MustLoadSwagger20Schema() +} + +// MustLoadJSONSchemaDraft04 panics when Swagger20Schema returns an error +func MustLoadJSONSchemaDraft04() *Schema { + d, e := JSONSchemaDraft04() + if e != nil { + panic(e) + } + return d +} + +// JSONSchemaDraft04 loads the json schema document for json shema draft04 +func JSONSchemaDraft04() (*Schema, error) { + b, err := Asset("jsonschema-draft-04.json") + if err != nil { + return nil, err + } + + schema := new(Schema) + if err := json.Unmarshal(b, schema); err != nil { + return nil, err + } + return schema, nil +} + +// MustLoadSwagger20Schema panics when Swagger20Schema returns an error +func MustLoadSwagger20Schema() *Schema { + d, e := Swagger20Schema() + if e != nil { + panic(e) + } + return d +} + +// Swagger20Schema loads the swagger 2.0 schema from the embedded assets +func Swagger20Schema() (*Schema, error) { + + b, err := Asset("v2/schema.json") + if err != nil { + return nil, err + } + + schema := new(Schema) + if err := json.Unmarshal(b, schema); err != nil { + return nil, err + } + return schema, nil +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/swagger.go b/src/runtime/vendor/github.com/go-openapi/spec/swagger.go new file mode 100644 index 000000000..4586a21c8 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/swagger.go @@ -0,0 +1,318 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// Swagger this is the root document object for the API specification. +// It combines what previously was the Resource Listing and API Declaration (version 1.2 and earlier) together into one document. +// +// For more information: http://goo.gl/8us55a#swagger-object- +type Swagger struct { + VendorExtensible + SwaggerProps +} + +// JSONLookup look up a value by the json property name +func (s Swagger) JSONLookup(token string) (interface{}, error) { + if ex, ok := s.Extensions[token]; ok { + return &ex, nil + } + r, _, err := jsonpointer.GetForToken(s.SwaggerProps, token) + return r, err +} + +// MarshalJSON marshals this swagger structure to json +func (s Swagger) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(s.SwaggerProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(s.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON unmarshals a swagger spec from json +func (s *Swagger) UnmarshalJSON(data []byte) error { + var sw Swagger + if err := json.Unmarshal(data, &sw.SwaggerProps); err != nil { + return err + } + if err := json.Unmarshal(data, &sw.VendorExtensible); err != nil { + return err + } + *s = sw + return nil +} + +// SwaggerProps captures the top-level properties of an Api specification +type SwaggerProps struct { + ID string `json:"id,omitempty"` + Consumes []string `json:"consumes,omitempty"` + Produces []string `json:"produces,omitempty"` + Schemes []string `json:"schemes,omitempty"` // the scheme, when present must be from [http, https, ws, wss] + Swagger string `json:"swagger,omitempty"` + Info *Info `json:"info,omitempty"` + Host string `json:"host,omitempty"` + BasePath string `json:"basePath,omitempty"` // must start with a leading "/" + Paths *Paths `json:"paths"` // required + Definitions Definitions `json:"definitions,omitempty"` + Parameters map[string]Parameter `json:"parameters,omitempty"` + Responses map[string]Response `json:"responses,omitempty"` + SecurityDefinitions SecurityDefinitions `json:"securityDefinitions,omitempty"` + Security []map[string][]string `json:"security,omitempty"` + Tags []Tag `json:"tags,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` +} + +// Dependencies represent a dependencies property +type Dependencies map[string]SchemaOrStringArray + +// SchemaOrBool represents a schema or boolean value, is biased towards true for the boolean property +type SchemaOrBool struct { + Allows bool + Schema *Schema +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SchemaOrBool) JSONLookup(token string) (interface{}, error) { + if token == "allows" { + return s.Allows, nil + } + r, _, err := jsonpointer.GetForToken(s.Schema, token) + return r, err +} + +var jsTrue = []byte("true") +var jsFalse = []byte("false") + +// MarshalJSON convert this object to JSON +func (s SchemaOrBool) MarshalJSON() ([]byte, error) { + if s.Schema != nil { + return json.Marshal(s.Schema) + } + + if s.Schema == nil && !s.Allows { + return jsFalse, nil + } + return jsTrue, nil +} + +// UnmarshalJSON converts this bool or schema object from a JSON structure +func (s *SchemaOrBool) UnmarshalJSON(data []byte) error { + var nw SchemaOrBool + if len(data) >= 4 { + if data[0] == '{' { + var sch Schema + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + nw.Allows = !(data[0] == 'f' && data[1] == 'a' && data[2] == 'l' && data[3] == 's' && data[4] == 'e') + } + *s = nw + return nil +} + +// SchemaOrStringArray represents a schema or a string array +type SchemaOrStringArray struct { + Schema *Schema + Property []string +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SchemaOrStringArray) JSONLookup(token string) (interface{}, error) { + r, _, err := jsonpointer.GetForToken(s.Schema, token) + return r, err +} + +// MarshalJSON converts this schema object or array into JSON structure +func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) { + if len(s.Property) > 0 { + return json.Marshal(s.Property) + } + if s.Schema != nil { + return json.Marshal(s.Schema) + } + return []byte("null"), nil +} + +// UnmarshalJSON converts this schema object or array from a JSON structure +func (s *SchemaOrStringArray) UnmarshalJSON(data []byte) error { + var first byte + if len(data) > 1 { + first = data[0] + } + var nw SchemaOrStringArray + if first == '{' { + var sch Schema + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + if first == '[' { + if err := json.Unmarshal(data, &nw.Property); err != nil { + return err + } + } + *s = nw + return nil +} + +// Definitions contains the models explicitly defined in this spec +// An object to hold data types that can be consumed and produced by operations. +// These data types can be primitives, arrays or models. +// +// For more information: http://goo.gl/8us55a#definitionsObject +type Definitions map[string]Schema + +// SecurityDefinitions a declaration of the security schemes available to be used in the specification. +// This does not enforce the security schemes on the operations and only serves to provide +// the relevant details for each scheme. +// +// For more information: http://goo.gl/8us55a#securityDefinitionsObject +type SecurityDefinitions map[string]*SecurityScheme + +// StringOrArray represents a value that can either be a string +// or an array of strings. Mainly here for serialization purposes +type StringOrArray []string + +// Contains returns true when the value is contained in the slice +func (s StringOrArray) Contains(value string) bool { + for _, str := range s { + if str == value { + return true + } + } + return false +} + +// JSONLookup implements an interface to customize json pointer lookup +func (s SchemaOrArray) JSONLookup(token string) (interface{}, error) { + if _, err := strconv.Atoi(token); err == nil { + r, _, err := jsonpointer.GetForToken(s.Schemas, token) + return r, err + } + r, _, err := jsonpointer.GetForToken(s.Schema, token) + return r, err +} + +// UnmarshalJSON unmarshals this string or array object from a JSON array or JSON string +func (s *StringOrArray) UnmarshalJSON(data []byte) error { + var first byte + if len(data) > 1 { + first = data[0] + } + + if first == '[' { + var parsed []string + if err := json.Unmarshal(data, &parsed); err != nil { + return err + } + *s = StringOrArray(parsed) + return nil + } + + var single interface{} + if err := json.Unmarshal(data, &single); err != nil { + return err + } + if single == nil { + return nil + } + switch single.(type) { + case string: + *s = StringOrArray([]string{single.(string)}) + return nil + default: + return fmt.Errorf("only string or array is allowed, not %T", single) + } +} + +// MarshalJSON converts this string or array to a JSON array or JSON string +func (s StringOrArray) MarshalJSON() ([]byte, error) { + if len(s) == 1 { + return json.Marshal([]string(s)[0]) + } + return json.Marshal([]string(s)) +} + +// SchemaOrArray represents a value that can either be a Schema +// or an array of Schema. Mainly here for serialization purposes +type SchemaOrArray struct { + Schema *Schema + Schemas []Schema +} + +// Len returns the number of schemas in this property +func (s SchemaOrArray) Len() int { + if s.Schema != nil { + return 1 + } + return len(s.Schemas) +} + +// ContainsType returns true when one of the schemas is of the specified type +func (s *SchemaOrArray) ContainsType(name string) bool { + if s.Schema != nil { + return s.Schema.Type != nil && s.Schema.Type.Contains(name) + } + return false +} + +// MarshalJSON converts this schema object or array into JSON structure +func (s SchemaOrArray) MarshalJSON() ([]byte, error) { + if len(s.Schemas) > 0 { + return json.Marshal(s.Schemas) + } + return json.Marshal(s.Schema) +} + +// UnmarshalJSON converts this schema object or array from a JSON structure +func (s *SchemaOrArray) UnmarshalJSON(data []byte) error { + var nw SchemaOrArray + var first byte + if len(data) > 1 { + first = data[0] + } + if first == '{' { + var sch Schema + if err := json.Unmarshal(data, &sch); err != nil { + return err + } + nw.Schema = &sch + } + if first == '[' { + if err := json.Unmarshal(data, &nw.Schemas); err != nil { + return err + } + } + *s = nw + return nil +} + +// vim:set ft=go noet sts=2 sw=2 ts=2: diff --git a/src/runtime/vendor/github.com/go-openapi/spec/tag.go b/src/runtime/vendor/github.com/go-openapi/spec/tag.go new file mode 100644 index 000000000..25256c4be --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/tag.go @@ -0,0 +1,74 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +import ( + "encoding/json" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/swag" +) + +// TagProps describe a tag entry in the top level tags section of a swagger spec +type TagProps struct { + Description string `json:"description,omitempty"` + Name string `json:"name,omitempty"` + ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"` +} + +// NewTag creates a new tag +func NewTag(name, description string, externalDocs *ExternalDocumentation) Tag { + return Tag{TagProps: TagProps{description, name, externalDocs}} +} + +// Tag allows adding meta data to a single tag that is used by the [Operation Object](http://goo.gl/8us55a#operationObject). +// It is not mandatory to have a Tag Object per tag used there. +// +// For more information: http://goo.gl/8us55a#tagObject +type Tag struct { + VendorExtensible + TagProps +} + +// JSONLookup implements an interface to customize json pointer lookup +func (t Tag) JSONLookup(token string) (interface{}, error) { + if ex, ok := t.Extensions[token]; ok { + return &ex, nil + } + + r, _, err := jsonpointer.GetForToken(t.TagProps, token) + return r, err +} + +// MarshalJSON marshal this to JSON +func (t Tag) MarshalJSON() ([]byte, error) { + b1, err := json.Marshal(t.TagProps) + if err != nil { + return nil, err + } + b2, err := json.Marshal(t.VendorExtensible) + if err != nil { + return nil, err + } + return swag.ConcatJSON(b1, b2), nil +} + +// UnmarshalJSON marshal this from JSON +func (t *Tag) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &t.TagProps); err != nil { + return err + } + return json.Unmarshal(data, &t.VendorExtensible) +} diff --git a/src/runtime/vendor/github.com/go-openapi/spec/xml_object.go b/src/runtime/vendor/github.com/go-openapi/spec/xml_object.go new file mode 100644 index 000000000..945a46703 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/spec/xml_object.go @@ -0,0 +1,68 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 spec + +// XMLObject a metadata object that allows for more fine-tuned XML model definitions. +// +// For more information: http://goo.gl/8us55a#xmlObject +type XMLObject struct { + Name string `json:"name,omitempty"` + Namespace string `json:"namespace,omitempty"` + Prefix string `json:"prefix,omitempty"` + Attribute bool `json:"attribute,omitempty"` + Wrapped bool `json:"wrapped,omitempty"` +} + +// WithName sets the xml name for the object +func (x *XMLObject) WithName(name string) *XMLObject { + x.Name = name + return x +} + +// WithNamespace sets the xml namespace for the object +func (x *XMLObject) WithNamespace(namespace string) *XMLObject { + x.Namespace = namespace + return x +} + +// WithPrefix sets the xml prefix for the object +func (x *XMLObject) WithPrefix(prefix string) *XMLObject { + x.Prefix = prefix + return x +} + +// AsAttribute flags this object as xml attribute +func (x *XMLObject) AsAttribute() *XMLObject { + x.Attribute = true + return x +} + +// AsElement flags this object as an xml node +func (x *XMLObject) AsElement() *XMLObject { + x.Attribute = false + return x +} + +// AsWrapped flags this object as wrapped, this is mostly useful for array types +func (x *XMLObject) AsWrapped() *XMLObject { + x.Wrapped = true + return x +} + +// AsUnwrapped flags this object as an xml node +func (x *XMLObject) AsUnwrapped() *XMLObject { + x.Wrapped = false + return x +} diff --git a/src/runtime/vendor/github.com/go-openapi/strfmt/.editorconfig b/src/runtime/vendor/github.com/go-openapi/strfmt/.editorconfig new file mode 100644 index 000000000..3152da69a --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/strfmt/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/src/runtime/vendor/github.com/go-openapi/strfmt/.gitignore b/src/runtime/vendor/github.com/go-openapi/strfmt/.gitignore new file mode 100644 index 000000000..dd91ed6a0 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/strfmt/.gitignore @@ -0,0 +1,2 @@ +secrets.yml +coverage.out diff --git a/src/runtime/vendor/github.com/go-openapi/strfmt/.golangci.yml b/src/runtime/vendor/github.com/go-openapi/strfmt/.golangci.yml new file mode 100644 index 000000000..a90f7e382 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/strfmt/.golangci.yml @@ -0,0 +1,20 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 30 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 4 + +linters: + enable-all: true + disable: + - maligned + - lll diff --git a/src/runtime/vendor/github.com/go-openapi/strfmt/.travis.yml b/src/runtime/vendor/github.com/go-openapi/strfmt/.travis.yml new file mode 100644 index 000000000..613ee9782 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/strfmt/.travis.yml @@ -0,0 +1,20 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- '1.9' +- 1.10.x +- 1.11.x +install: +- go get -u github.com/stretchr/testify/assert +- go get -u github.com/pborman/uuid +- go get -u github.com/asaskevich/govalidator +- go get -u github.com/mailru/easyjson +- go get -u github.com/go-openapi/errors +- go get -u github.com/mitchellh/mapstructure +- go get -u github.com/globalsign/mgo/bson +language: go +notifications: + slack: + secure: zE5AtIYTpYfQPnTzP+EaQPN7JKtfFAGv6PrJqoIZLOXa8B6zGb6+J1JRNNxWi7faWbyJOxa4FSSsuPsKZMycUK6wlLFIdhDxwqeo7Ew8r6rdZKdfUHQggfNS9wO79ARoNYUDHtmnaBUS+eWSM1YqSc4i99QxyyfuURLOeAaA/q14YbdlTlaw3lrZ0qT92ot1FnVGNOx064zuHtFeUf+jAVRMZ6Q3rvqllwIlPszE6rmHGXBt2VoJxRaBetdwd7FgkcYw9FPXKHhadwC7/75ZAdmxIukhxNMw4Tr5NuPcqNcnbYLenDP7B3lssGVIrP4BRSqekS1d/tqvdvnnFWHMwrNCkSnSc065G5+qWTlXKAemIclgiXXqE2furBNLm05MDdG8fn5epS0UNarkjD+zX336RiqwBlOX4KbF+vPyqcO98CsN0lnd+H6loc9reiTHs37orFFpQ+309av9be2GGsHUsRB9ssIyrewmhAccOmkRtr2dVTZJNFQwa5Kph5TNJuTjnZEwG/xUkEX2YSfwShOsb062JWiflV6PJdnl80pc9Tn7D5sO5Bf9DbijGRJwwP+YiiJtwtr+vsvS+n4sM0b5eqm4UoRo+JJO8ffoJtHS7ItuyRbVQCwEPJ4221WLcf5PquEEDdAPwR+K4Gj8qTXqTDdxOiES1xFUKVgmzhI= +script: +- ./hack/coverage diff --git a/src/runtime/vendor/github.com/go-openapi/strfmt/CODE_OF_CONDUCT.md b/src/runtime/vendor/github.com/go-openapi/strfmt/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9322b065e --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/strfmt/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/src/runtime/vendor/github.com/go-openapi/strfmt/LICENSE b/src/runtime/vendor/github.com/go-openapi/strfmt/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/strfmt/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/src/runtime/vendor/github.com/go-openapi/strfmt/README.md b/src/runtime/vendor/github.com/go-openapi/strfmt/README.md new file mode 100644 index 000000000..55f880415 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/strfmt/README.md @@ -0,0 +1,71 @@ +# Strfmt [![Build Status](https://travis-ci.org/go-openapi/strfmt.svg?branch=master)](https://travis-ci.org/go-openapi/strfmt) [![codecov](https://codecov.io/gh/go-openapi/strfmt/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/strfmt) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/strfmt/master/LICENSE) +[![GoDoc](https://godoc.org/github.com/go-openapi/strfmt?status.svg)](http://godoc.org/github.com/go-openapi/strfmt) +[![GolangCI](https://golangci.com/badges/github.com/go-openapi/strfmt.svg)](https://golangci.com) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/strfmt)](https://goreportcard.com/report/github.com/go-openapi/strfmt) + +This package exposes a registry of data types to support string formats in the go-openapi toolkit. + +strfmt represents a well known string format such as credit card or email. The go toolkit for OpenAPI specifications knows how to deal with those. + +## Supported data formats +go-openapi/strfmt follows the swagger 2.0 specification with the following formats +defined [here](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types). + +It also provides convenient extensions to go-openapi users. + +- [x] JSON-schema draft 4 formats + - date-time + - email + - hostname + - ipv4 + - ipv6 + - uri +- [x] swagger 2.0 format extensions + - binary + - byte (e.g. base64 encoded string) + - date (e.g. "1970-01-01") + - password +- [x] go-openapi custom format extensions + - bsonobjectid (BSON objectID) + - creditcard + - duration (e.g. "3 weeks", "1ms") + - hexcolor (e.g. "#FFFFFF") + - isbn, isbn10, isbn13 + - mac (e.g "01:02:03:04:05:06") + - rgbcolor (e.g. "rgb(100,100,100)") + - ssn + - uuid, uuid3, uuid4, uuid5 + +> NOTE: as the name stands for, this package is intended to support string formatting only. +> It does not provide validation for numerical values with swagger format extension for JSON types "number" or +> "integer" (e.g. float, double, int32...). + +## Format types +Types defined in strfmt expose marshaling and validation capabilities. + +List of defined types: +- Base64 +- CreditCard +- Date +- DateTime +- Duration +- Email +- HexColor +- Hostname +- IPv4 +- IPv6 +- ISBN +- ISBN10 +- ISBN13 +- MAC +- ObjectId +- Password +- RGBColor +- SSN +- URI +- UUID +- UUID3 +- UUID4 +- UUID5 diff --git a/src/runtime/vendor/github.com/go-openapi/strfmt/bson.go b/src/runtime/vendor/github.com/go-openapi/strfmt/bson.go new file mode 100644 index 000000000..be8ae2df5 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/strfmt/bson.go @@ -0,0 +1,127 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 strfmt + +import ( + "database/sql/driver" + "errors" + "fmt" + + "github.com/globalsign/mgo/bson" + "github.com/mailru/easyjson/jlexer" + "github.com/mailru/easyjson/jwriter" +) + +func init() { + var id ObjectId + // register this format in the default registry + Default.Add("bsonobjectid", &id, IsBSONObjectID) +} + +// IsBSONObjectID returns true when the string is a valid BSON.ObjectId +func IsBSONObjectID(str string) bool { + return bson.IsObjectIdHex(str) +} + +// ObjectId represents a BSON object ID (alias to github.com/globalsign/mgo/bson.ObjectId) +// +// swagger:strfmt bsonobjectid +type ObjectId bson.ObjectId + +// NewObjectId creates a ObjectId from a Hex String +func NewObjectId(hex string) ObjectId { + return ObjectId(bson.ObjectIdHex(hex)) +} + +// MarshalText turns this instance into text +func (id *ObjectId) MarshalText() ([]byte, error) { + return []byte(bson.ObjectId(*id).Hex()), nil +} + +// UnmarshalText hydrates this instance from text +func (id *ObjectId) UnmarshalText(data []byte) error { // validation is performed later on + *id = ObjectId(bson.ObjectIdHex(string(data))) + return nil +} + +// Scan read a value from a database driver +func (id *ObjectId) Scan(raw interface{}) error { + var data []byte + switch v := raw.(type) { + case []byte: + data = v + case string: + data = []byte(v) + default: + return fmt.Errorf("cannot sql.Scan() strfmt.URI from: %#v", v) + } + + return id.UnmarshalText(data) +} + +// Value converts a value to a database driver value +func (id *ObjectId) Value() (driver.Value, error) { + return driver.Value(string(*id)), nil +} + +func (id *ObjectId) String() string { + return string(*id) +} + +// MarshalJSON returns the ObjectId as JSON +func (id *ObjectId) MarshalJSON() ([]byte, error) { + var w jwriter.Writer + id.MarshalEasyJSON(&w) + return w.BuildBytes() +} + +// MarshalEasyJSON writes the ObjectId to a easyjson.Writer +func (id *ObjectId) MarshalEasyJSON(w *jwriter.Writer) { + w.String(bson.ObjectId(*id).Hex()) +} + +// UnmarshalJSON sets the ObjectId from JSON +func (id *ObjectId) UnmarshalJSON(data []byte) error { + l := jlexer.Lexer{Data: data} + id.UnmarshalEasyJSON(&l) + return l.Error() +} + +// UnmarshalEasyJSON sets the ObjectId from a easyjson.Lexer +func (id *ObjectId) UnmarshalEasyJSON(in *jlexer.Lexer) { + if data := in.String(); in.Ok() { + *id = NewObjectId(data) + } +} + +// GetBSON returns the hex representation of the ObjectId as a bson.M{} map. +func (id *ObjectId) GetBSON() (interface{}, error) { + return bson.M{"data": bson.ObjectId(*id).Hex()}, nil +} + +// SetBSON sets the ObjectId from raw bson data +func (id *ObjectId) SetBSON(raw bson.Raw) error { + var m bson.M + if err := raw.Unmarshal(&m); err != nil { + return err + } + + if data, ok := m["data"].(string); ok { + *id = NewObjectId(data) + return nil + } + + return errors.New("couldn't unmarshal bson raw value as ObjectId") +} diff --git a/src/runtime/vendor/github.com/go-openapi/strfmt/date.go b/src/runtime/vendor/github.com/go-openapi/strfmt/date.go new file mode 100644 index 000000000..ccdba4481 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/strfmt/date.go @@ -0,0 +1,150 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 strfmt + +import ( + "database/sql/driver" + "errors" + "fmt" + "time" + + "github.com/globalsign/mgo/bson" + "github.com/mailru/easyjson/jlexer" + "github.com/mailru/easyjson/jwriter" +) + +func init() { + d := Date{} + // register this format in the default registry + Default.Add("date", &d, IsDate) +} + +// IsDate returns true when the string is a valid date +func IsDate(str string) bool { + _, err := time.Parse(RFC3339FullDate, str) + return err == nil +} + +const ( + // RFC3339FullDate represents a full-date as specified by RFC3339 + // See: http://goo.gl/xXOvVd + RFC3339FullDate = "2006-01-02" +) + +// Date represents a date from the API +// +// swagger:strfmt date +type Date time.Time + +// String converts this date into a string +func (d Date) String() string { + return time.Time(d).Format(RFC3339FullDate) +} + +// UnmarshalText parses a text representation into a date type +func (d *Date) UnmarshalText(text []byte) error { + if len(text) == 0 { + return nil + } + dd, err := time.Parse(RFC3339FullDate, string(text)) + if err != nil { + return err + } + *d = Date(dd) + return nil +} + +// MarshalText serializes this date type to string +func (d Date) MarshalText() ([]byte, error) { + return []byte(d.String()), nil +} + +// Scan scans a Date value from database driver type. +func (d *Date) Scan(raw interface{}) error { + switch v := raw.(type) { + case []byte: + return d.UnmarshalText(v) + case string: + return d.UnmarshalText([]byte(v)) + case time.Time: + *d = Date(v) + return nil + case nil: + *d = Date{} + return nil + default: + return fmt.Errorf("cannot sql.Scan() strfmt.Date from: %#v", v) + } +} + +// Value converts Date to a primitive value ready to written to a database. +func (d Date) Value() (driver.Value, error) { + return driver.Value(d.String()), nil +} + +// MarshalJSON returns the Date as JSON +func (d Date) MarshalJSON() ([]byte, error) { + var w jwriter.Writer + d.MarshalEasyJSON(&w) + return w.BuildBytes() +} + +// MarshalEasyJSON writes the Date to a easyjson.Writer +func (d Date) MarshalEasyJSON(w *jwriter.Writer) { + w.String(time.Time(d).Format(RFC3339FullDate)) +} + +// UnmarshalJSON sets the Date from JSON +func (d *Date) UnmarshalJSON(data []byte) error { + if string(data) == jsonNull { + return nil + } + l := jlexer.Lexer{Data: data} + d.UnmarshalEasyJSON(&l) + return l.Error() +} + +// UnmarshalEasyJSON sets the Date from a easyjson.Lexer +func (d *Date) UnmarshalEasyJSON(in *jlexer.Lexer) { + if data := in.String(); in.Ok() { + tt, err := time.Parse(RFC3339FullDate, data) + if err != nil { + in.AddError(err) + return + } + *d = Date(tt) + } +} + +// GetBSON returns the Date as a bson.M{} map. +func (d *Date) GetBSON() (interface{}, error) { + return bson.M{"data": d.String()}, nil +} + +// SetBSON sets the Date from raw bson data +func (d *Date) SetBSON(raw bson.Raw) error { + var m bson.M + if err := raw.Unmarshal(&m); err != nil { + return err + } + + if data, ok := m["data"].(string); ok { + rd, err := time.Parse(RFC3339FullDate, data) + *d = Date(rd) + return err + } + + return errors.New("couldn't unmarshal bson raw value as Date") +} diff --git a/src/runtime/vendor/github.com/go-openapi/strfmt/default.go b/src/runtime/vendor/github.com/go-openapi/strfmt/default.go new file mode 100644 index 000000000..7d375e8d5 --- /dev/null +++ b/src/runtime/vendor/github.com/go-openapi/strfmt/default.go @@ -0,0 +1,1847 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 strfmt + +import ( + "database/sql/driver" + "encoding/base64" + "errors" + "fmt" + "net/mail" + "regexp" + "strings" + + "github.com/asaskevich/govalidator" + "github.com/globalsign/mgo/bson" + "github.com/mailru/easyjson/jlexer" + "github.com/mailru/easyjson/jwriter" +) + +const ( + // HostnamePattern http://json-schema.org/latest/json-schema-validation.html#anchor114 + // A string instance is valid against this attribute if it is a valid + // representation for an Internet host name, as defined by RFC 1034, section 3.1 [RFC1034]. + // http://tools.ietf.org/html/rfc1034#section-3.5 + // ::= any one of the ten digits 0 through 9 + // var digit = /[0-9]/; + // ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case + // var letter = /[a-zA-Z]/; + // ::= | + // var letDig = /[0-9a-zA-Z]/; + // ::= | "-" + // var letDigHyp = /[-0-9a-zA-Z]/; + // ::= | + // var ldhStr = /[-0-9a-zA-Z]+/; + //