diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..722d5e71d93ca0aa0db6fd22452e46be5604a84d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode diff --git a/LICENSE b/LICENSE index 261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64..f49a4e16e68b128803cc2dcea614603632b04eac 100644 --- a/LICENSE +++ b/LICENSE @@ -198,4 +198,4 @@ 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. + limitations under the License. \ No newline at end of file diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000000000000000000000000000000000000..ed1b165f39082399310408a487aece26062d8bb2 --- /dev/null +++ b/OWNERS @@ -0,0 +1,9 @@ +approvers: + - songxiangbing + - luorui +reviewers: + - songxiangbing + - luorui + - biantanggui + - kangfuan + - lining \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c95a1f18bff46bfe1329c72c81564a5b0dcb8e53 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# Ascend Docker Runtime.zh +[组件介绍](##"组件介绍") +[编译Ascend Docker Runtime](##"编译Ascend Docker Runtime") +[组件安装](##"组件安装") +[更新日志](##"更新日志") + +## 组件介绍 +容器引擎插件(Ascend Docker,又叫昇腾容器)是CANN的基础组件,为所有的AI训练/推理作业提供Ascend NPU(昇腾处理器)容器化支持,使用户AI作业能够以Docker容器的方式平滑运行在昇腾设备之上,如图1-1所示。 + +图1-1 Ascend Docker + +![image](assets/20210329102949456.png) + +### Ascend Docker设计简介 + +Ascend Docker本质上是基于OCI标准实现的Docker Runtime,不修改Docker引擎,对Docker以插件方式提供Ascend NPU适配功能。 +如图1-2所示,Ascend Docker通过OCI接口与原生Docker对接。在原生Docker的runc启动容器过程中,会调用prestart-hook对容器进行配置管理。 + +图1-2 Docker适配原理 + +![image](assets/20230118566.png) + +其中,prestart-hook是OCI定义的容器生存状态,即created状态到running状态的一个中间过渡所设置的钩子函数。在这个过渡状态,容器的namespace已经被创建,但容器的作业还没有启动,因此可以对容器进行设备挂载,cgroup配置等操作。这样随后启动的作业便可以使用到这些配置。 +Ascend Docker在prestart-hook这个钩子函数中,对容器做了以下配置操作: +1.根据ASCEND_VISIBLE_DEVICES,将对应的NPU设备挂载到容器的namespace。 +2.在Host上配置该容器的device cgroup,确保该容器只可以使用指定的NPU,保证设备的隔离。 +3.将Host上的CANN Runtime Library挂载到容器的namespace。 + +*** +## 编译Ascend Docker Runtime +执行以下步骤进行编译 +```shell +# 1、下载Ascend Docker Runtime源码 +git clone https://gitee.com/ascend/ascend-docker-runtime.git + +# 2、下载安全函数库 +cd ascend-docker-runtime/platform +git clone https://gitee.com/openeuler/libboundscheck.git + +# 3、下载makeself +cd ../opensource +git clone https://gitee.com/src-openeuler/makeself.git +tar -zxvf makeself/makeself-2.4.2.tar.gz + +# 4、编译 +cd ../build +bash build.sh +``` +编译完成后,会在output文件夹看到相应的二进制run包 +```shell +root@#:ascend-docker-runtime/output# ll +... +-rwxr-xr-x ... Ascend-docker-runtime_3.0.0_linux-x86_64.run* +``` + +## 组件安装 +请参考[《MindX DL用户指南》](https://www.hiascend.com/software/mindx-dl)中的“集群调度用户指南 > 安装部署指导 > 安装集群调度组件 > 典型安装场景 > 集群调度场景”进行。 + +## 更新日志 +| 版本 | 发布日期 | 修改说明 | +|:------:|:----:|:-----:| +| v3.0.0 | 2023-01-18 | 第一次发布 | diff --git a/assets/20210329102949456.png b/assets/20210329102949456.png new file mode 100644 index 0000000000000000000000000000000000000000..fa8a759568f3de872d20fee6ee0ab2a6a5baebc9 Binary files /dev/null and b/assets/20210329102949456.png differ diff --git a/assets/20230118566.png b/assets/20230118566.png new file mode 100644 index 0000000000000000000000000000000000000000..c777d62cc0a9fb694149f94c598c82f484b20a7c Binary files /dev/null and b/assets/20230118566.png differ diff --git a/build/build.sh b/build/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..6f4bbb378d70950ad5835dfc7a72857c01905994 --- /dev/null +++ b/build/build.sh @@ -0,0 +1,150 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. +# Description: ascend-docker-runtime build script +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +set -ex + +ROOT=$(cd $(dirname $0); pwd)/.. +TOP_DIR=$ROOT/.. + +OPENSRC=${ROOT}/opensource +PLATFORM=${ROOT}/platform +OUTPUT=${ROOT}/output +BUILD=${ROOT}/build + +CLIDIR=${ROOT}/cli +DESTROYDIR=${ROOT}/destroy +CLISRCNAME="main.c" + +INSTALLHELPERDIR=${ROOT}/install +INSTALLHELPERSRCNAME="main.go" + +HOOKDIR=${ROOT}/hook +HOOKSRCNAME="main.go" + +RUNTIMEDIR=${ROOT}/runtime +RUNTIMESRCNAME="main.go" + +CLISRCPATH=$(find ${CLIDIR} -name "${CLISRCNAME}") +CLISRCDIR=${CLISRCPATH%/${CLISRCNAME}} +DESTROYSRCPATH=$(find ${DESTROYDIR} -name "${CLISRCNAME}") +DESTROYDIR=${DESTROYSRCPATH%/${CLISRCNAME}} +INSTALLHELPERSRCPATH=$(find ${INSTALLHELPERDIR} -name "${INSTALLHELPERSRCNAME}") +INSTALLHELPERSRCDIR=${INSTALLHELPERSRCPATH%/${INSTALLHELPERSRCNAME}} +HOOKSRCPATH=$(find ${HOOKDIR} -name "${HOOKSRCNAME}") +HOOKSRCDIR=${HOOKSRCPATH%/${HOOKSRCNAME}} +RUNTIMESRCPATH=$(find ${RUNTIMEDIR} -name "${RUNTIMESRCNAME}") +RUNTIMESRCDIR=${RUNTIMESRCPATH%/${RUNTIMESRCNAME}} + +PACKAGENAME="Ascend-docker-runtime" + +VERSION="v3.0.0" +version_file="${TOP_DIR}"/service_config.ini +if [ -f "$version_file" ]; then + line=$(sed -n '4p' "$version_file" 2>&1) + #cut the chars after ':' + VERSION=${line#*:} +fi + +CPUARCH=$(uname -m) + +function build_bin() +{ + echo "make destroy" + [ -d "${BUILD}/build/destroy/build" ] && rm -rf ${BUILD}/build/destroy/build + mkdir -p ${BUILD}/build/destroy/build && cd ${BUILD}/build/destroy/build + + cmake ${DESTROYDIR} + make clean + make + + echo "make cli" + [ -d "${BUILD}/build/cli/build" ] && rm -rf ${BUILD}/build/cli/build + mkdir -p ${BUILD}/build/cli/build && cd ${BUILD}/build/cli/build + + cmake ${CLISRCDIR} + make clean + make + + [ -d "${ROOT}/opensource/src" ] && rm -rf ${ROOT}/opensource/src + mkdir ${ROOT}/opensource/src + export GOPATH="${ROOT}/opensource" + export GO111MODULE=on + export GONOSUMDB="*" + export GONOPROXY=*.huawei.com + export GOFLAGS="-mod=mod" + + echo "make installhelper" + cd ${INSTALLHELPERSRCDIR} + [ -d "${BUILD}/build/helper/build" ] && rm -rf ${BUILD}/build/helper/build + export CGO_ENABLED=1 + export CGO_CFLAGS="-fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 -fPIC -ftrapv" + export CGO_CPPFLAGS="-fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 -fPIC -ftrapv" + export CGO_LDFLAGS="-Wl,-z,now -Wl,-s,--build-id=none -pie" + mkdir -p ${BUILD}/build/helper/build + go build -buildmode=pie -ldflags='-linkmode=external -buildid=IdNetCheck -extldflags "-Wl,-z,now" -w -s' -trimpath -o ${BUILD}/build/helper/build/ascend-docker-plugin-install-helper ${INSTALLHELPERSRCDIR}/${INSTALLHELPERSRCNAME} + + echo "make hook" + [ -d "${HOOKSRCDIR}/build" ] && rm -rf ${HOOKSRCDIR}/build + mkdir ${HOOKSRCDIR}/build && cd ${HOOKSRCDIR}/build + go build -buildmode=pie -ldflags='-linkmode=external -buildid=IdNetCheck -extldflags "-Wl,-z,now" -w -s' -trimpath -o ascend-docker-hook ../${HOOKSRCNAME} + echo `pwd` + ls + + echo "make runtime" + cd ${RUNTIMEDIR} + [ -d "${RUNTIMESRCDIR}/build" ] && rm -rf ${RUNTIMESRCDIR}/build + mkdir ${RUNTIMESRCDIR}/build&&cd ${RUNTIMESRCDIR}/build + go build -buildmode=pie -ldflags='-linkmode=external -buildid=IdNetCheck -extldflags "-Wl,-z,now" -w -s' -trimpath -o ascend-docker-runtime ../${RUNTIMESRCNAME} +} + +function copy_file_output() +{ + cd ${BUILD} + if [ -d "run_pkg" ]; then + rm -r run_pkg + fi + mkdir run_pkg + + /bin/cp -f {${RUNTIMESRCDIR},${HOOKSRCDIR},${BUILD}/build/helper,${BUILD}/build/cli,${BUILD}/build/destroy}/build/ascend-docker* run_pkg + /bin/cp -f scripts/uninstall.sh run_pkg + /bin/cp -f scripts/base.list run_pkg + /bin/cp -f scripts/base.list_A500 run_pkg + /bin/cp -f scripts/base.list_A200 run_pkg + /bin/cp -f scripts/base.list_A200ISoC run_pkg + + /bin/cp -rf ${ROOT}/assets run_pkg + /bin/cp -f ${ROOT}/README.md run_pkg + /bin/cp -f scripts/run_main.sh run_pkg + + chmod 550 run_pkg/run_main.sh + + RUN_PKG_NAME="${PACKAGENAME}_${VERSION}_linux-${CPUARCH}.run" + DATE=$(date -u "+%Y-%m-%d") + sed -i "s/REPLACE_VERSION/${VERSION}/g" run_pkg/run_main.sh + bash ${OPENSRC}/makeself-release-2.4.2/makeself.sh --nomd5 --nocrc --help-header scripts/help.info --packaging-date ${DATE} \ + --tar-extra "--mtime=${DATE}" run_pkg "${RUN_PKG_NAME}" ascend-docker-runtime ./run_main.sh + mv ${RUN_PKG_NAME} ${OUTPUT} +} + +function clean() +{ + [ -d "${OUTPUT}" ] && cd ${OUTPUT}&&rm -rf * +} + +clean +build_bin +copy_file_output \ No newline at end of file diff --git a/build/libboundscheck/CMakeLists.txt b/build/libboundscheck/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..88b92caeca82922c89cc17844743f6e5df805f1b --- /dev/null +++ b/build/libboundscheck/CMakeLists.txt @@ -0,0 +1,7 @@ +# 查找当前目录下的所有源文件 +# 并将名称保存到 LIB_SRC 变量 +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/../../platform/libboundscheck/src LIB_SRC) + +#生成链接库 +add_library(libboundscheck ${LIB_SRC}) +target_compile_options(libboundscheck PRIVATE -fstack-protector-all -fpie) diff --git a/build/scripts/base.list b/build/scripts/base.list new file mode 100644 index 0000000000000000000000000000000000000000..0a6470a4362d6a3057f3d7a8b06b26dcb8bce582 --- /dev/null +++ b/build/scripts/base.list @@ -0,0 +1,4 @@ +/usr/local/Ascend/driver/lib64 +/usr/local/Ascend/driver/include +/usr/local/dcmi +/usr/local/bin/npu-smi \ No newline at end of file diff --git a/build/scripts/base.list_A200 b/build/scripts/base.list_A200 new file mode 100644 index 0000000000000000000000000000000000000000..30ec95503ba02983fdde86a4bdca5b7e49147827 --- /dev/null +++ b/build/scripts/base.list_A200 @@ -0,0 +1,5 @@ +/usr/local/Ascend/driver/lib64 +/usr/local/sbin/npu-smi +/usr/local/Ascend/driver/tools +/etc/hdcBasic.cfg +/etc/sys_version.conf \ No newline at end of file diff --git a/build/scripts/base.list_A200ISoC b/build/scripts/base.list_A200ISoC new file mode 100644 index 0000000000000000000000000000000000000000..7befba73a378c63ccbcd5d92ec0b250780ca58a7 --- /dev/null +++ b/build/scripts/base.list_A200ISoC @@ -0,0 +1,3 @@ +/usr/local/bin/npu-smi +/etc/hdcBasic.cfg +/etc/sys_version.conf \ No newline at end of file diff --git a/build/scripts/base.list_A500 b/build/scripts/base.list_A500 new file mode 100644 index 0000000000000000000000000000000000000000..c78a6ceeb57746acc027944801bea40a7ff3e49e --- /dev/null +++ b/build/scripts/base.list_A500 @@ -0,0 +1,3 @@ +/home/data/miniD/driver/lib64 +/usr/local/dcmi +/usr/local/bin/npu-smi \ No newline at end of file diff --git a/build/scripts/help.info b/build/scripts/help.info new file mode 100644 index 0000000000000000000000000000000000000000..31fe6e0e2974de14c2578af68cfd1d0dc4636655 --- /dev/null +++ b/build/scripts/help.info @@ -0,0 +1,5 @@ + --install Install into this system + --install-path Specify the installation path (default: /usr/local/Ascend/Ascend-Docker-Runtime) + --uninstall Uninstall the installed ascend-docker-runtime tool + --upgrade Upgrade the installed ascend-docker-runtime tool + --install-type= Only A500, A200ISoC and A200 need to specify the installation type of Ascend-docker-runtime (eg: --install-type=A500) \ No newline at end of file diff --git a/build/scripts/run_main.sh b/build/scripts/run_main.sh new file mode 100644 index 0000000000000000000000000000000000000000..95673c549106b33d6d1333a5d27a111616d55421 --- /dev/null +++ b/build/scripts/run_main.sh @@ -0,0 +1,309 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. +# Description: ascend-docker-runtime run package script +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +set -e + +ASCEND_RUNTIME_CONFIG_DIR=/etc/ascend-docker-runtime.d +DOCKER_CONFIG_DIR=/etc/docker +INSTALL_PATH=/usr/local/Ascend/Ascend-Docker-Runtime +readonly PACKAGE_VERSION=REPLACE_VERSION + +function save_install_args() { + if [ -f "${INSTALL_PATH}"/ascend_docker_runtime_install.info ]; then + rm "${INSTALL_PATH}"/ascend_docker_runtime_install.info + fi + { + echo -e "version=${PACKAGE_VERSION}" + echo -e "arch=$(uname -m)" + echo -e "os=linux" + echo -e "path=${INSTALL_PATH}" + echo -e "build=Ascend-docker-runtime_${PACKAGE_VERSION}-$(uname -m)" + echo -e "a500=${a500}" + echo -e "a200=${a200}" + echo -e "a200isoc=${a200isoc}" + } >> "${INSTALL_PATH}"/ascend_docker_runtime_install.info +} + +function install() +{ + echo 'installing ascend docker runtime' + + if [ ! -d "${INSTALL_PATH}" ]; then + mkdir -p ${INSTALL_PATH} + fi + if [ -L "${INSTALL_PATH}" ]; then + echo "ERROR: ${INSTALL_PATH} is symbolic link." + exit 1 + fi + cp -f ./ascend-docker-runtime ${INSTALL_PATH}/ascend-docker-runtime + cp -f ./ascend-docker-hook ${INSTALL_PATH}/ascend-docker-hook + cp -f ./ascend-docker-cli ${INSTALL_PATH}/ascend-docker-cli + cp -f ./ascend-docker-plugin-install-helper ${INSTALL_PATH}/ascend-docker-plugin-install-helper + cp -f ./ascend-docker-destroy ${INSTALL_PATH}/ascend-docker-destroy + chmod 550 ${INSTALL_PATH}/ascend-docker-runtime + chmod 550 ${INSTALL_PATH}/ascend-docker-hook + chmod 550 ${INSTALL_PATH}/ascend-docker-cli + chmod 550 ${INSTALL_PATH}/ascend-docker-plugin-install-helper + chmod 550 ${INSTALL_PATH}/ascend-docker-destroy + + if [ -L "${INSTALL_PATH}/script" ]; then + echo "ERROR: ${INSTALL_PATH}/script is symbolic link." + exit 1 + fi + cp -rf ./assets ${INSTALL_PATH}/assets + cp -f ./README.md ${INSTALL_PATH}/README.md + mkdir -p ${INSTALL_PATH}/script + cp -f ./uninstall.sh ${INSTALL_PATH}/script/uninstall.sh + chmod 500 ${INSTALL_PATH}/script/uninstall.sh + + if [ -d "${ASCEND_RUNTIME_CONFIG_DIR}" ]; then + rm -rf ${ASCEND_RUNTIME_CONFIG_DIR} + fi + if [ -L "${ASCEND_RUNTIME_CONFIG_DIR}" ]; then + echo "ERROR: ${ASCEND_RUNTIME_CONFIG_DIR} is symbolic link." + exit 1 + fi + mkdir -p ${ASCEND_RUNTIME_CONFIG_DIR} + chmod 750 ${ASCEND_RUNTIME_CONFIG_DIR} + if [ "${a500}" == "y" ]; then + cp -f ./base.list_A500 ${ASCEND_RUNTIME_CONFIG_DIR}/base.list + elif [ "${a200}" == "y" ]; then + cp -f ./base.list_A200 ${ASCEND_RUNTIME_CONFIG_DIR}/base.list + elif [ "${a200isoc}" == "y" ]; then + cp -f ./base.list_A200ISoC ${ASCEND_RUNTIME_CONFIG_DIR}/base.list + else + cp -f ./base.list ${ASCEND_RUNTIME_CONFIG_DIR}/base.list + fi + chmod 440 ${ASCEND_RUNTIME_CONFIG_DIR}/base.list + + echo 'install executable files success' + + if [ ! -d "${DOCKER_CONFIG_DIR}" ]; then + mkdir -p ${DOCKER_CONFIG_DIR} + fi + + SRC="${DOCKER_CONFIG_DIR}/daemon.json.${PPID}" + DST="${DOCKER_CONFIG_DIR}/daemon.json" + ./ascend-docker-plugin-install-helper add ${DST} ${SRC} ${INSTALL_PATH}/ascend-docker-runtime + if [ "$?" != "0" ]; then + echo 'create damon.json failed' + exit 1 + fi + + mv ${SRC} ${DST} + chmod 600 ${DST} + echo 'create damom.json success' + save_install_args + echo "[INFO]: Ascend Docker Runtime has been installed in: ${INSTALL_PATH}" + echo "[INFO]: The version of Ascend Docker Runtime is: ${PACKAGE_VERSION}" + echo 'please reboot docker daemon to take effect' +} + +function uninstall() +{ + echo "[INFO]: Uninstalling ascend docker runtime ${PACKAGE_VERSION}" + + if [ ! -d "${INSTALL_PATH}" ]; then + echo 'WARNING: the specified install path does not exist, skipping' + exit 0 + fi + + ${INSTALL_PATH}/script/uninstall.sh + echo 'remove daemon.json setting success' + + [ -n "${INSTALL_PATH}" ] && rm -rf ${INSTALL_PATH} + echo 'remove executable files success' + + echo 'del damom.json success' +} + +function upgrade() +{ + echo 'upgrading ascend docker runtime' + + if [ ! -d "${INSTALL_PATH}" ]; then + echo 'ERROR: the specified install path does not exist, stopping upgrading' + exit 1 + fi + + if [ ! -d "${ASCEND_RUNTIME_CONFIG_DIR}" ]; then + echo 'ERROR: the configuration directory does not exist' + exit 1 + fi + if [ -L "${INSTALL_PATH}" ]; then + echo "ERROR: ${INSTALL_PATH} is symbolic link." + exit 1 + fi + cp -f ./ascend-docker-runtime ${INSTALL_PATH}/ascend-docker-runtime + cp -f ./ascend-docker-hook ${INSTALL_PATH}/ascend-docker-hook + cp -f ./ascend-docker-cli ${INSTALL_PATH}/ascend-docker-cli + cp -f ./ascend-docker-plugin-install-helper ${INSTALL_PATH}/ascend-docker-plugin-install-helper + cp -f ./ascend-docker-destroy ${INSTALL_PATH}/ascend-docker-destroy + cp -f ./uninstall.sh ${INSTALL_PATH}/script/uninstall.sh + chmod 550 ${INSTALL_PATH}/ascend-docker-runtime + chmod 550 ${INSTALL_PATH}/ascend-docker-hook + chmod 550 ${INSTALL_PATH}/ascend-docker-cli + chmod 550 ${INSTALL_PATH}/ascend-docker-plugin-install-helper + chmod 550 ${INSTALL_PATH}/ascend-docker-destroy + chmod 500 ${INSTALL_PATH}/script/uninstall.sh + if [ -f "${INSTALL_PATH}"/ascend_docker_runtime_install.info ]; then + if [ "$(grep "a500=y" "${INSTALL_PATH}"/ascend_docker_runtime_install.info)" == "a500=y" ];then + a500=y + cp -f ./base.list_A500 ${ASCEND_RUNTIME_CONFIG_DIR}/base.list + elif [ "$(grep "a200=y" "${INSTALL_PATH}"/ascend_docker_runtime_install.info)" == "a200=y" ]; then + a200=y + cp -f ./base.list_A200 ${ASCEND_RUNTIME_CONFIG_DIR}/base.list + elif [ "x$(grep "a200isoc=y" "${INSTALL_PATH}"/ascend_docker_runtime_install.info)" == "xa200isoc=y" ]; then + a200isoc=y + cp -f ./base.list_A200ISoC ${ASCEND_RUNTIME_CONFIG_DIR}/base.list + else + cp -f ./base.list ${ASCEND_RUNTIME_CONFIG_DIR}/base.list + fi + save_install_args + fi + chmod 440 ${ASCEND_RUNTIME_CONFIG_DIR}/base.list + + echo "[INFO]: Ascend Docker Runtime has been installed in: ${INSTALL_PATH}" + echo '[INFO]: upgrade ascend docker runtime success' + echo "[INFO]: The version of Ascend Docker Runtime is: ${PACKAGE_VERSION}" +} + +INSTALL_FLAG=n +INSTALL_PATH_FLAG=n +UNINSTALL_FLAG=n +UPGRADE_FLAG=n +a500=n +a200=n +a200isoc=n +quiet_flag=n + +while true +do + case "$3" in + --check) + exit 0 + ;; + --quiet) + quiet_flag=y + shift + ;; + --install) + if [ "${INSTALL_FLAG}" == "y" ]; then + echo "warning :Repeat parameter!" + exit 1 + fi + INSTALL_FLAG=y + shift + ;; + --uninstall) + if [ "${UNINSTALL_FLAG}" == "y" ]; then + echo "warning :Repeat parameter!" + exit 1 + fi + UNINSTALL_FLAG=y + shift + ;; + --install-path=*) + if [ "${INSTALL_PATH_FLAG}" == "y" ]; then + echo "warning :Repeat parameter!" + exit 1 + fi + INSTALL_PATH_FLAG=y + INSTALL_PATH=$(echo $3 | cut -d"=" -f2) + INSTALL_PATH=$(echo ${INSTALL_PATH}/Ascend-Docker-Runtime | sed "s/\/*$//g") + shift + ;; + --upgrade) + if [ "${UPGRADE_FLAG}" == "y" ]; then + echo "warning :Repeat parameter!" + exit 1 + fi + UPGRADE_FLAG=y + shift + ;; + --install-type=*) + if [ "${a500}" == "y" ] || [ "${a200}" == "y" ] || [ "${a200isoc}" == "y" ]; then + echo "warning :Repeat parameter!" + exit 1 + fi + + if [ "$3" == "--install-type=A500" ]; then + a500=y + elif [ "$3" == "--install-type=A200" ]; then + a200=y + elif [ "$3" == "--install-type=A200ISoC" ]; then + a200isoc=y + else + echo "ERROR :Please check the parameter of --install-type=" + exit 1 + fi + shift + ;; + *) + if [ "x$3" != "x" ]; then + echo "warning :Unsupported parameters: $3" + exit 1 + fi + break + ;; + esac +done + +# install path must be absolute path +if [[ ! "${INSTALL_PATH}" =~ ^/.* ]]; then + echo "ERROR :Please follow the installation address after the --install-path=" + exit 1 +fi + +# it is not allowed to input only install-path +if [ "${INSTALL_PATH_FLAG}" == "y" ] && \ + [ "${INSTALL_FLAG}" == "n" ] && \ + [ "${UNINSTALL_FLAG}" == "n" ] && \ + [ "${UPGRADE_FLAG}" == "n" ]; then + echo "Error:only input command. When use --install-path you also need intput --install or --uninstall or --upgrade or --devel" + exit 1 +fi + +# it is not allowed to input only quiet +if [ "${quiet_flag}" == "y" ] && \ + [ "${INSTALL_FLAG}" == "n" ] && \ + [ "${UNINSTALL_FLAG}" == "n" ] && \ + [ "${UPGRADE_FLAG}" == "n" ]; then + echo "[ERROR] parameter error ! Mode is neither install, uninstall, upgrade." + exit 1 +fi + +# must run with root permission +if [ "${UID}" != "0" ]; then + echo 'please run with root permission' + exit 1 +fi + +if [ "${INSTALL_FLAG}" == "y" ]; then + install + exit 0 +fi + +if [ "${UNINSTALL_FLAG}" == "y" ]; then + uninstall + exit 0 +fi + +if [ "${UPGRADE_FLAG}" == "y" ]; then + upgrade + exit 0 +fi diff --git a/build/scripts/uninstall.sh b/build/scripts/uninstall.sh new file mode 100644 index 0000000000000000000000000000000000000000..4c320d5f1b8f8357601e03711e16819c190ea849 --- /dev/null +++ b/build/scripts/uninstall.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. +# Description: ascend-docker-runtime uninstall script +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +set -e +LOG_FILE="/var/log/ascend_seclog/ascend_toolbox_install.log" +echo "Ascend-Docker-Runtime" $(date +%Y%m%d-%H:%M:%S) "start uninstall" +echo "Ascend-Docker-Runtime" $(date +%Y%m%d-%H:%M:%S) "start uninstall" >>${LOG_FILE} +ROOT=$(cd $(dirname $0); pwd)/.. +DST='/etc/docker/daemon.json' +SRC="${DST}.${PPID}" +ASCEND_RUNTIME_CONFIG_DIR=/etc/ascend-docker-runtime.d + +if [ ! -f "${DST}" ]; then + exit 0 +fi + +${ROOT}/ascend-docker-plugin-install-helper rm ${DST} ${SRC} +if [ "$?" != "0" ]; then + echo "Ascend-Docker-Runtime" $(date +%Y%m%d-%H:%M:%S) "ERROR: del damon.json failed" + echo "Ascend-Docker-Runtime" $(date +%Y%m%d-%H:%M:%S) "ERROR: del damon.json failed" >>${LOG_FILE} + exit 1 +fi + +mv ${SRC} ${DST} + +[ -n "${ASCEND_RUNTIME_CONFIG_DIR}" ] && rm -rf ${ASCEND_RUNTIME_CONFIG_DIR} +INSTALL_ROOT_PATH=$(dirname $(dirname ${ROOT})) + +if test -d ${INSTALL_ROOT_PATH} +then + rm -rf ${INSTALL_ROOT_PATH} + echo "Ascend-Docker-Runtime $(date +%Y%m%d-%H:%M:%S) delete ${INSTALL_ROOT_PATH} succesfull" + echo "Ascend-Docker-Runtime $(date +%Y%m%d-%H:%M:%S) delete ${INSTALL_ROOT_PATH} succesfull" >>${LOG_FILE} +fi +echo "Ascend-Docker-Runtime" $(date +%Y%m%d-%H:%M:%S) "uninstall successfully" +echo "Ascend-Docker-Runtime" $(date +%Y%m%d-%H:%M:%S) "uninstall successfully" >>${LOG_FILE} \ No newline at end of file diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..bfd6f79fa2a903b95a5e28f9f6352670fe5066bd --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1,3 @@ +.idea +cmake-build-debug +.vscode \ No newline at end of file diff --git a/cli/README.txt b/cli/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..e5a077c34efd07ab293fcdd007c5fb75e2ae21f3 --- /dev/null +++ b/cli/README.txt @@ -0,0 +1 @@ +ascend-docker-cli \ No newline at end of file diff --git a/cli/src/CMakeLists.txt b/cli/src/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..bff30fb856996f460c806ed0d871b193d6942286 --- /dev/null +++ b/cli/src/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.26) +project(ascend-docker-cli C) +set(CMAKE_C_STANDARD 11) +## The common options using by both c and cxx + +message(STATUS "CMAKE_SHARED_LIBRARY_LINK_C_FLAGS = " ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}) +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") + +include_directories("${PROJECT_SOURCE_DIR}/../../platform/libboundscheck/include") +aux_source_directory(. SRC) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../build/libboundscheck ${CMAKE_CURRENT_SOURCE_DIR}/../../build/libboundscheck) +add_executable(ascend-docker-cli ${SRC}) +target_compile_options(ascend-docker-cli PRIVATE -fstack-protector-all -fpie -D_FORTIFY_SOURCE=2 -O2) +target_link_libraries(ascend-docker-cli -pie -Wl,-s,-z,now libboundscheck) diff --git a/cli/src/basic.c b/cli/src/basic.c new file mode 100644 index 0000000000000000000000000000000000000000..fd06d732cb666d04ed3e8a2a3639904c4aa92e06 --- /dev/null +++ b/cli/src/basic.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#include "basic.h" +#include +#include + +void InitParsedConfig(struct ParsedConfig *parsedConfig) +{ + if (parsedConfig == NULL) { + (void)fprintf(stderr, "parsedConfig pointer is null!\n"); + return; + } + + parsedConfig->devicesNr = MAX_DEVICE_NR; +} \ No newline at end of file diff --git a/cli/src/basic.h b/cli/src/basic.h new file mode 100644 index 0000000000000000000000000000000000000000..36d7065d148ff622a09613232b43a502a5bff52f --- /dev/null +++ b/cli/src/basic.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#ifndef _BASIC_H +#define _BASIC_H + +#include +#include +#include + +#define DEVICE_NAME "davinci" +#define VDEVICE_NAME "vdavinci" +#define DAVINCI_MANAGER "davinci_manager" +#define DEVMM_SVM "devmm_svm" +#define HISI_HDC "hisi_hdc" +#define DEFAULT_DIR_MODE 0755 +#define DEFAULT_LOG_MODE 0600 +#define DUMP_LOG_MODE 0400 +#define DEFAULT_LOGDIR_MODE 0700 +#define BUF_SIZE 1024 +#define MAX_DEVICE_NR 1024 +#define MAX_MOUNT_NR 512 +#define WHITE_LIST_NUM 9 + +#define ROOT_UID 0 + +#define ROOT_UID 0 + +#define LEVEL_INFO 0 +#define LEVEL_WARN 1 +#define LEVEL_ERROR 2 +#define LEVEL_DEBUG 3 +#define SCREEN_NO 0 +#define SCREEN_YES 1 + +#define LOG_ERROR(fmt, ...) \ + do { \ + char _content[BUF_SIZE] = {0}; \ + int _ret = sprintf_s(_content, BUF_SIZE, fmt, ##__VA_ARGS__); \ + if (_ret < 0) { \ + (void)fprintf(stderr, "cannot assemble log content"); \ + } else { \ + (void)fprintf(stderr, "%s", (const char *)_content); \ + } \ + } while (0) + +#define ALLOW_PATH "/devices.allow" +#define ROOT_GAP 4 +#define FSTYPE_GAP 2 +#define MOUNT_SUBSTR_GAP 2 +#define ROOT_SUBSTR_GAP 2 + +struct PathInfo { + char* src; + size_t srcLen; + char* dst; + size_t dstLen; +}; + +struct MountList { + unsigned int count; + char list[MAX_MOUNT_NR][PATH_MAX]; +}; + +struct ParsedConfig { + char rootfs[BUF_SIZE]; + size_t devices[MAX_DEVICE_NR]; + size_t devicesNr; + char containerNsPath[BUF_SIZE]; + char cgroupPath[BUF_SIZE]; + int originNsFd; + const struct MountList *files; + const struct MountList *dirs; +}; + +void InitParsedConfig(struct ParsedConfig *parsedConfig); + +#endif \ No newline at end of file diff --git a/cli/src/cgrp.c b/cli/src/cgrp.c new file mode 100644 index 0000000000000000000000000000000000000000..16bd9d9c89ddccc5bcd182d9b1a61f270ea27827 --- /dev/null +++ b/cli/src/cgrp.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#include "cgrp.h" +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "securec.h" + +#include "u_mount.h" +#include "utils.h" +#include "options.h" +#include "logger.h" + +bool TakeNthWord(char **pLine, unsigned int n, char **word) +{ + if (pLine == NULL || word == NULL) { + (void)fprintf(stderr, "pLine, word pointer is null!\n"); + return false; + } + + char *w = NULL; + for (unsigned int i = 0; i < n; i++) { + w = strsep(pLine, " "); + if (w == NULL || *w == '\0') { + return false; + } + } + + *word = w; + return true; +} + +bool CheckRootDir(char **pLine) +{ + if (pLine == NULL) { + (void)fprintf(stderr, "pLine pointer is null!\n"); + return false; + } + + char *rootDir = NULL; + if (!TakeNthWord(pLine, ROOT_GAP, &rootDir)) { + return false; + } + + return strlen(rootDir) < BUF_SIZE && !StrHasPrefix(rootDir, "/.."); +} + +bool CheckFsType(char **pLine) +{ + if (pLine == NULL) { + (void)fprintf(stderr, "pLine pointer is null!\n"); + return false; + } + + char* fsType = NULL; + if (!TakeNthWord(pLine, FSTYPE_GAP, &fsType)) { + return false; + } + + return IsStrEqual(fsType, "cgroup"); +} + +bool CheckSubStr(char **pLine, const char *subsys) +{ + if (pLine == NULL || subsys == NULL) { + (void)fprintf(stderr, "pLine, subsys pointer is null!\n"); + return false; + } + + char* substr = NULL; + if (!TakeNthWord(pLine, MOUNT_SUBSTR_GAP, &substr)) { + return false; + } + + return strstr(substr, subsys) != NULL; +} + +typedef char *(*ParseFileLine)(char *, const char *); +static bool GetFileInfo(const char* resolvedPath, char* buffer, const int bufferSize, const ParseFileLine fn) +{ + FILE *fp = NULL; + char *line = NULL; + size_t len = 0; + char *result = NULL; + fp = fopen(resolvedPath, "r"); + if (fp == NULL) { + Logger("cannot open file.", LEVEL_ERROR, SCREEN_YES); + return false; + } + + while (getline(&line, &len, fp) != -1) { + result = fn(line, "devices"); + if (result != NULL && strlen(result) < bufferSize) { + break; + } + } + + errno_t ret = strcpy_s(buffer, bufferSize, result); + free(line); + fclose(fp); + if (ret != EOK) { + return false; + } + return true; +} + +int ParseFileByLine(char* buffer, int bufferSize, const ParseFileLine fn, const char* filepath) +{ + if (buffer == NULL || filepath == NULL) { + (void)fprintf(stderr, "buffer, filepath pointer is null!\n"); + return -1; + } + + char resolvedPath[PATH_MAX] = {0x0}; + + if (realpath(filepath, resolvedPath) == NULL && errno != ENOENT) { + Logger("Cannot canonicalize path.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + const size_t maxFileSzieMb = 1024; // max 1G + if (!CheckExternalFile(resolvedPath, strlen(resolvedPath), maxFileSzieMb, true)) { + Logger("Check file legality failed.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + if (!GetFileInfo(resolvedPath, buffer, bufferSize, fn)) { + return -1; + } + + return 0; +} + +char *GetCgroupMount(char *line, const char *subsys) +{ + if (line == NULL || subsys == NULL) { + (void)fprintf(stderr, "line, subsys pointer is null!\n"); + return NULL; + } + + if (!CheckRootDir(&line)) { + return NULL; + } + + char *mountPoint = NULL; + mountPoint = strsep(&line, " "); + line = strchr(line, '-'); + if (mountPoint == NULL || *mountPoint == '\0') { + return NULL; + } + + if (!CheckFsType(&line)) { + return NULL; + } + + if (!CheckSubStr(&line, subsys)) { + return NULL; + } + + return mountPoint; +} + +char *GetCgroupRoot(char *line, const char *subSystem) +{ + if (line == NULL || subSystem == NULL) { + (void)fprintf(stderr, "line, subSystem pointer is null!\n"); + return NULL; + } + + char *token = NULL; + int i; + for (i = 0; i < ROOT_SUBSTR_GAP; ++i) { + token = strsep(&line, ":"); + if (token == NULL || *token == '\0') { + return NULL; + } + } + + char *rootDir = strsep(&line, ":"); + if (rootDir == NULL || *rootDir == '\0') { + return NULL; + } + + if (strlen(rootDir) >= BUF_SIZE || StrHasPrefix(rootDir, "/..")) { + return NULL; + } + if (strstr(token, subSystem) == NULL) { + return NULL; + } + return rootDir; +} + +int SetupDeviceCgroup(FILE *cgroupAllow, const char *devName) +{ + if (cgroupAllow == NULL || devName == NULL) { + (void)fprintf(stderr, "cgroupAllow, devName pointer is null!\n"); + return -1; + } + + int ret; + struct stat devStat; + char devPath[BUF_SIZE]; + + ret = sprintf_s(devPath, BUF_SIZE, "/dev/%s", devName); + if (ret < 0) { + char* str = FormatLogMessage("failed to assemble dev path for %s.", devName); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + + ret = stat((const char *)devPath, &devStat); + if (ret < 0) { + Logger("Failed to get stat of devpath.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + bool isFailed = fprintf(cgroupAllow, "c %u:%u rw", major(devStat.st_rdev), minor(devStat.st_rdev)) < 0 || + fflush(cgroupAllow) == EOF || ferror(cgroupAllow) < 0; + if (isFailed) { + Logger("write devices failed.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + return 0; +} + +int SetupDriverCgroup(FILE *cgroupAllow) +{ + if (cgroupAllow == NULL) { + (void)fprintf(stderr, "cgroupAllow pointer is null!\n"); + return -1; + } + + int ret; + ret = SetupDeviceCgroup(cgroupAllow, DAVINCI_MANAGER); + if (ret < 0) { + char* str = FormatLogMessage("failed to setup cgroup for %s.", DAVINCI_MANAGER); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + + bool is200Rc = false; + if (!DoMounting200RC(&is200Rc)) { + return -1; + } + if (is200Rc) { + return 0; + } + + ret = SetupDeviceCgroup(cgroupAllow, DEVMM_SVM); + if (ret < 0) { + char* str = FormatLogMessage("failed to setup cgroup for %s.", DEVMM_SVM); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + + ret = SetupDeviceCgroup(cgroupAllow, HISI_HDC); + if (ret < 0) { + char* str = FormatLogMessage("failed to setup cgroup for %s.", HISI_HDC); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + + return 0; +} + +int GetCgroupPath(const long pid, char *effPath, size_t maxSize) +{ + if (effPath == NULL) { + (void)fprintf(stderr, "effPath pointer is null!\n"); + return -1; + } + + int ret; + char mountPath[BUF_SIZE] = {0x0}; + char mount[BUF_SIZE] = {0x0}; + + ret = sprintf_s(mountPath, BUF_SIZE, "/proc/%d/mountinfo", (int)getppid()); + if (ret < 0) { + char* str = FormatLogMessage("assemble mount info path failed: ppid(%d).", getppid()); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + + ret = ParseFileByLine(mount, BUF_SIZE, GetCgroupMount, mountPath); + if (ret < 0) { + Logger("cat file content failed.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + char cgroup[BUF_SIZE] = {0x0}; + char cgroupPath[BUF_SIZE] = {0x0}; + ret = sprintf_s(cgroupPath, BUF_SIZE, "/proc/%d/cgroup", pid); + if (ret < 0) { + char* str = FormatLogMessage("assemble cgroup path failed: pid(%d).", pid); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + + ret = ParseFileByLine(cgroup, BUF_SIZE, GetCgroupRoot, cgroupPath); + if (ret < 0) { + Logger("cat file content failed.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + // cut last '\n' off + cgroup[strcspn(cgroup, "\n")] = '\0'; + + ret = sprintf_s(effPath, maxSize, "%s%s%s", mount, cgroup, ALLOW_PATH); + if (ret < 0) { + Logger("assemble cgroup device path failed.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + return 0; +} + +static bool SetupCgroupProcess(const char* resolvedCgroupPath, const struct ParsedConfig *config) +{ + char *str = NULL; + FILE *cgroupAllow = NULL; + char deviceName[BUF_SIZE] = {0}; + cgroupAllow = fopen(resolvedCgroupPath, "a"); + if (cgroupAllow == NULL) { + Logger("failed to open cgroup file.", LEVEL_ERROR, SCREEN_YES); + return false; + } + + if (SetupDriverCgroup(cgroupAllow) < 0) { + fclose(cgroupAllow); + Logger("failed to setup driver cgroup.", LEVEL_ERROR, SCREEN_YES); + return false; + } + for (size_t idx = 0; idx < config->devicesNr; idx++) { + if (sprintf_s(deviceName, BUF_SIZE, "%s%u", + (IsVirtual() ? VDEVICE_NAME : DEVICE_NAME), + config->devices[idx]) < 0) { + fclose(cgroupAllow); + str = FormatLogMessage("failed to assemble device path for no.%u.", config->devices[idx]); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return false; + } + + if (SetupDeviceCgroup(cgroupAllow, (const char *)deviceName) < 0) { + fclose(cgroupAllow); + Logger("failed to setup cgroup.", LEVEL_ERROR, SCREEN_YES); + return false; + } + } + free(str); + fclose(cgroupAllow); + return true; +} + +int SetupCgroup(const struct ParsedConfig *config) +{ + if (config == NULL) { + (void)fprintf(stderr, "config pointer is null!\n"); + return -1; + } + + char resolvedCgroupPath[PATH_MAX] = {0}; + if (realpath(config->cgroupPath, resolvedCgroupPath) == NULL && errno != ENOENT) { + Logger("cannot canonicalize cgroup.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + const size_t maxFileSzieMb = 1024; // max 1G + if (!CheckExternalFile(resolvedCgroupPath, strlen(resolvedCgroupPath), maxFileSzieMb, true)) { + Logger("Check file legality failed.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + if (!SetupCgroupProcess(resolvedCgroupPath, config)) { + return -1; + } + + return 0; +} \ No newline at end of file diff --git a/cli/src/cgrp.h b/cli/src/cgrp.h new file mode 100644 index 0000000000000000000000000000000000000000..9df72f2c9d260810f96bf2e02d9931b482b00461 --- /dev/null +++ b/cli/src/cgrp.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#ifndef _CGRP_H +#define _CGRP_H + +#include "basic.h" + +int GetCgroupPath(const long pid, char *effPath, size_t maxSize); +int SetupCgroup(const struct ParsedConfig *config); + +#endif \ No newline at end of file diff --git a/cli/src/logger.c b/cli/src/logger.c new file mode 100644 index 0000000000000000000000000000000000000000..c29daa8ad339d61f20126166534b328cab1ebece --- /dev/null +++ b/cli/src/logger.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" +#include "basic.h" +#include "utils.h" +#include "logger.h" + +#define FILE_MAX_SIZE (1024 * 1024 * 2) +#define LOG_PATH_DIR "/var/log/ascend-docker-runtime/" +#define TEMP_BUFFER 30 +#define YEAR_OFFSET 1900 +#define MONTH_OFFSET 1 +#define LOG_LENGTH 1024 + +int GetCurrentLocalTime(char* buffer, int length) +{ + if (buffer == NULL) { + (void)fprintf(stderr, "buffer pointer is null!\n"); + return -1; + } + + time_t timep = time(NULL); + if (timep == (time_t)-1) { + return -1; + } + struct tm result = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + struct tm *timeinfo = localtime_r(&timep, &result); + if (timeinfo == NULL) { + return -1; + } + return sprintf_s(buffer, + TEMP_BUFFER, + "[%04d-%02d-%02d %02d:%02d:%02d]", + (timeinfo->tm_year + YEAR_OFFSET), + (timeinfo->tm_mon + MONTH_OFFSET), + (timeinfo->tm_mday), + (timeinfo->tm_hour), + (timeinfo->tm_min), + (timeinfo->tm_sec)); +} + +int CreateLog(const char* filename) +{ + if (filename == NULL) { + return -1; + } + int exist; + exist = access(filename, 0); + int fd = 0; + if (exist != 0) { + fd = creat(filename, DEFAULT_LOG_MODE); + if (fd < 0) { + return -1; + } + close(fd); + } + return 0; +} + +static long GetLogSizeProcess(const char* path) +{ + FILE *fp = NULL; + fp = fopen(path, "rb"); + long length = 0; + if (fp != NULL) { + fseek(fp, 0, SEEK_END); + length = ftell(fp); + } + if (fp != NULL) { + fclose(fp); + fp = NULL; + } + return length; +} + +long GetLogSize(const char* filename) +{ + if (filename == NULL) { + (void)fprintf(stderr, "filename pointer is null!\n"); + return -1; + } + + int ret; + ret = CreateLog(filename); + if (ret < 0) { + return -1; + } + + char path[PATH_MAX + 1] = {0x00}; + if (strlen(filename) > PATH_MAX || realpath(filename, path) == NULL) { + return -1; + } + if (!CheckExistsFile(path, strlen(path), 0, false)) { + return -1; + } + return GetLogSizeProcess(path); +} + +int LogLoop(const char* filename) +{ + if (filename == NULL) { + (void)fprintf(stderr, "filename pointer is null!\n"); + return -1; + } + + int ret; + char* loopPath = LOG_PATH_DIR"docker-runtime-log.log.1"; + + if (!CheckExistsFile(loopPath, strlen(loopPath), 0, false)) { + return -1; + } + if (!CheckExistsFile(filename, strlen(filename), 0, false)) { + return -1; + } + if (access(loopPath, 0) == 0) { + unlink(loopPath); + } + if (rename(filename, loopPath) == -1) { + return -1; + } + if (chmod(loopPath, DUMP_LOG_MODE) != 0) { + return -1; + } + ret = CreateLog(filename); + if (ret < 0) { + return -1; + } + return ret; +} + +static void WriteLogInfo(const char* path, size_t pathLen, const char* buffer, const unsigned bufferSize) +{ + if (path == NULL) { + return; + } + FILE *fp = NULL; + int ret = 0; + fp = fopen(path, "a+"); + if (fp != NULL) { + char now[TEMP_BUFFER] = {0}; + ret = GetCurrentLocalTime(now, sizeof(now) / sizeof(char)); + if (ret < 0) { + fclose(fp); + return; + } + fwrite(now, strlen(now), 1, fp); + fwrite(buffer, bufferSize, 1, fp); + fclose(fp); + fp = NULL; + } + return; +} + +static bool LogConvertStorage(const char* filename, const long maxSize) +{ + long length = GetLogSize(filename); + if (length < 0) { + return false; + } + if (length > maxSize) { + int ret = LogLoop(filename); + if (ret < 0) { + return false; + } + } + return true; +} + +static void LogFileProcess(const char* filename, const long maxSize, const char* buffer, const unsigned bufferSize) +{ + if (filename == NULL) { + return; + } + int ret = 0; + char path[PATH_MAX + 1] = {0x00}; + if (!LogConvertStorage(filename, maxSize)) { + return; + } + if (strlen(filename) > PATH_MAX || realpath(filename, path) == NULL) { + return; + } + if (!CheckExistsFile(path, strlen(path), 0, false)) { + return; + } + WriteLogInfo(path, PATH_MAX + 1, buffer, bufferSize); +} + +void WriteLogFile(const char* filename, long maxSize, const char* buffer, unsigned bufferSize) +{ + if (filename == NULL || buffer == NULL) { + (void)fprintf(stderr, "filename, buffer pointer is null!\n"); + return; + } + + LogFileProcess(filename, maxSize, buffer, bufferSize); +} + +static void DivertAndWrite(const char *logPath, const char *msg, const int level) +{ + int ret; + size_t destMax = LOG_LENGTH; + if (destMax <= 0) { + return; + } + char* buffer = (char*)malloc(destMax * sizeof(char)); + if (buffer == NULL) { + return; + } + switch (level) { + case LEVEL_DEBUG: + ret = sprintf_s(buffer, destMax, "[Debug]%s\n", msg); + break; + case LEVEL_ERROR: + ret = sprintf_s(buffer, destMax, "[Error]%s\n", msg); + break; + case LEVEL_WARN: + ret = sprintf_s(buffer, destMax, "[Warn]%s\n", msg); + break; + default: + ret = sprintf_s(buffer, destMax, "[Info]%s\n", msg); + } + if (ret < 0) { + free(buffer); + return; + } + WriteLogFile(logPath, FILE_MAX_SIZE, buffer, strlen(buffer)); + free(buffer); +} + +void Logger(const char *msg, int level, int screen) +{ + if (msg == NULL) { + return; + } + if (screen == SCREEN_YES) { + LOG_ERROR(msg); + } + int ret; + char *logPath = LOG_PATH_DIR"docker-runtime-log.log"; + if (MakeDirWithParent(LOG_PATH_DIR, DEFAULT_LOGDIR_MODE) < 0) { + return; + } + + DivertAndWrite(logPath, msg, level); +} diff --git a/cli/src/logger.h b/cli/src/logger.h new file mode 100644 index 0000000000000000000000000000000000000000..a2f45c8ca536e799561bca4615990e3b05ce4c55 --- /dev/null +++ b/cli/src/logger.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#ifndef _LOGGER_H +#define _LOGGER_H + +void Logger(const char *msg, int level, int screen); + +#endif diff --git a/cli/src/main.c b/cli/src/main.c new file mode 100644 index 0000000000000000000000000000000000000000..834c92abad6bcfa552aa941197251e3d9593a0b9 --- /dev/null +++ b/cli/src/main.c @@ -0,0 +1,548 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" + +#include "basic.h" +#include "ns.h" +#include "u_mount.h" +#include "cgrp.h" +#include "options.h" +#include "utils.h" +#include "logger.h" + +#define DECIMAL 10 +#define MAX_ARGC 1024 +#define MAX_ARG_LEN 1024 + +struct CmdArgs { + char devices[BUF_SIZE]; + char rootfs[BUF_SIZE]; + long pid; + char options[BUF_SIZE]; + struct MountList files; + struct MountList dirs; +}; + +static struct option g_cmdOpts[] = { + {"devices", required_argument, 0, 'd'}, + {"pid", required_argument, 0, 'p'}, + {"rootfs", required_argument, 0, 'r'}, + {"options", required_argument, 0, 'o'}, + {"mount-file", required_argument, 0, 'f'}, + {"mount-dir", required_argument, 0, 'i'}, + {0, 0, 0, 0} +}; + +typedef bool (*CmdArgParser)(struct CmdArgs *args, const char *arg); + +static bool DevicesCmdArgParser(struct CmdArgs *args, const char *arg) +{ + if (args == NULL || arg == NULL) { + Logger("args, arg pointer is null!", LEVEL_ERROR, SCREEN_YES); + return false; + } + if (strlen(arg) > MAX_ARG_LEN) { + Logger("argument value too long!", LEVEL_ERROR, SCREEN_YES); + return false; + } + + for (size_t iLoop = 0; iLoop < strlen(arg); iLoop++) { + if ((isdigit(arg[iLoop]) == 0) && (arg[iLoop] != ',')) { + Logger("failed to check devices.", LEVEL_ERROR, SCREEN_YES); + return false; + } + } + + errno_t err = strcpy_s(args->devices, BUF_SIZE, arg); + if (err != EOK) { + Logger("failed to get devices from cmd args.", LEVEL_ERROR, SCREEN_YES); + return false; + } + + return true; +} + +static bool PidCmdArgParser(struct CmdArgs *args, const char *arg) +{ + char buff[PATH_MAX] = {0}; + + if (args == NULL || arg == NULL) { + Logger("args, arg pointer is null!", LEVEL_ERROR, SCREEN_YES); + return false; + } + args->pid = strtol(arg, NULL, DECIMAL); + const char* pidMax = "/proc/sys/kernel/pid_max"; + const size_t maxFileSzieMb = 10; // max 10MB + if (!CheckExternalFile(pidMax, strlen(pidMax), maxFileSzieMb, true)) { + Logger("failed to check pid_max path.", LEVEL_ERROR, SCREEN_YES); + return false; + } + errno = 0; + FILE* pFile = NULL; + pFile = fopen(pidMax, "r"); + if ((pFile == NULL) || (fgets(buff, PATH_MAX, pFile) == NULL)) { + if (pFile != NULL) { + (void)fclose(pFile); + } + pFile = NULL; + Logger("failed to get pid_max buff.", LEVEL_ERROR, SCREEN_YES); + return false; + } + (void)fclose(pFile); + if ((strlen(buff) > 0) && (buff[strlen(buff) -1] == '\n')) { + buff[strlen(buff) -1] = '\0'; + } + for (size_t iLoop = 0; iLoop < strlen(buff); iLoop++) { + if (isdigit(buff[iLoop]) == 0) { + Logger("failed to get pid_max value.", LEVEL_ERROR, SCREEN_YES); + return false; + } + } + if ((args->pid < 0) || (args->pid >= strtol(buff, NULL, DECIMAL))) { + Logger("The PID out of bounds.", LEVEL_ERROR, SCREEN_YES); + return false; + } + if (errno != 0) { + char* str = FormatLogMessage("failed to convert pid string from cmd args, pid string: %s.", arg); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return false; + } + return true; +} + +static bool CheckFileLegality(const char* filePath, const size_t filePathLen, + const size_t maxFileSzieMb) +{ + if ((filePathLen > PATH_MAX) || (filePathLen <= 0)) { // 长度越界 + Logger("filePathLen out of bounds!", LEVEL_ERROR, SCREEN_YES); + return false; + } + for (size_t iLoop = 0; iLoop < filePathLen; iLoop++) { + if (!IsValidChar(filePath[iLoop])) { // 非法字符 + Logger("filePath has an illegal character!", LEVEL_ERROR, SCREEN_YES); + return false; + } + } + char resolvedPath[PATH_MAX] = {0}; + if (realpath(filePath, resolvedPath) == NULL && errno != ENOENT) { + Logger("realpath failed!", LEVEL_ERROR, SCREEN_YES); + return false; + } + if (strcmp(resolvedPath, filePath) != 0) { // 存在软链接 + Logger("filePath has a soft link!", LEVEL_ERROR, SCREEN_YES); + return false; + } + return true; +} + +static bool RootfsCmdArgParser(struct CmdArgs *args, const char *arg) +{ + if (args == NULL || arg == NULL) { + Logger("args, arg pointer is null!", LEVEL_ERROR, SCREEN_YES); + return false; + } + + errno_t err = strcpy_s(args->rootfs, BUF_SIZE, arg); + if (err != EOK) { + Logger("failed to get rootfs path from cmd args", LEVEL_ERROR, SCREEN_YES); + return false; + } + const size_t maxFileSzieMb = 50; // max 50MB + if (!CheckFileLegality(args->rootfs, strlen(args->rootfs), maxFileSzieMb)) { + Logger("failed to check rootf.", LEVEL_ERROR, SCREEN_YES); + return false; + } + + return true; +} + +static bool OptionsCmdArgParser(struct CmdArgs *args, const char *arg) +{ + if (args == NULL || arg == NULL) { + Logger("args, arg pointer is null!", LEVEL_ERROR, SCREEN_YES); + return false; + } + + errno_t err = strcpy_s(args->options, BUF_SIZE, arg); + if (err != EOK) { + Logger("failed to get options string from cmd args", LEVEL_ERROR, SCREEN_YES); + return false; + } + + if ((strcmp(args->options, "NODRV,VIRTUAL") != 0) && + (strcmp(args->options, "NODRV") != 0) && + (strcmp(args->options, "VIRTUAL") != 0)) { + Logger("Whitelist check failed.", LEVEL_ERROR, SCREEN_YES); + return false; + } + + return true; +} + +static bool CheckWhiteList(const char* fileName) +{ + if (fileName == NULL) { + return false; + } + bool fileExists = false; + const char mountWhiteList[WHITE_LIST_NUM][PATH_MAX] = {{"/usr/local/Ascend/driver/lib64"}, + {"/usr/local/Ascend/driver/include"}, + {"/usr/local/dcmi"}, + {"/usr/local/bin/npu-smi"}, + {"/home/data/miniD/driver/lib64"}, + {"/usr/local/sbin/npu-smi"}, + {"/usr/local/Ascend/driver/tools"}, + {"/etc/hdcBasic.cfg"}, + {"/etc/sys_version.conf"} + }; + + for (size_t iLoop = 0; iLoop < WHITE_LIST_NUM; iLoop++) { + if (strcmp(mountWhiteList[iLoop], fileName) == 0) { + fileExists = true; + break; + } + } + if (!fileExists) { + char* str = FormatLogMessage("failed to check whiteList value: %s.", fileName); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return false; + } + return true; +} + +static bool MountFileCmdArgParser(struct CmdArgs *args, const char *arg) +{ + if (args == NULL || arg == NULL) { + Logger("args, arg pointer is null!", LEVEL_ERROR, SCREEN_YES); + return false; + } + + if (args->files.count >= MAX_MOUNT_NR) { + char* str = FormatLogMessage("too many files to mount, max number is %u", MAX_MOUNT_NR); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return false; + } + + char *dst = &args->files.list[args->files.count++][0]; + errno_t err = strcpy_s(dst, PATH_MAX, arg); + if (err != EOK) { + char* str = FormatLogMessage("failed to copy mount file path: %s", arg); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return false; + } + const size_t maxFileSzieMb = 50; // max 50MB + if (!CheckFileLegality(dst, strlen(dst), maxFileSzieMb)) { + char* str = FormatLogMessage("failed to check files: %s", dst); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return false; + } + + return CheckWhiteList(dst) ? true : false; +} + +static bool MountDirCmdArgParser(struct CmdArgs *args, const char *arg) +{ + if (args == NULL || arg == NULL) { + Logger("args, arg pointer is null!", LEVEL_ERROR, SCREEN_YES); + return false; + } + + if (args->dirs.count >= MAX_MOUNT_NR) { + char* str = FormatLogMessage("too many directories to mount, max number is %u", MAX_MOUNT_NR); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return false; + } + + char *dst = &args->dirs.list[args->dirs.count++][0]; + errno_t err = strcpy_s(dst, PATH_MAX, arg); + if (err != EOK) { + char* str = FormatLogMessage("error: failed to copy mount directory path: %s", arg); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return false; + } + const size_t maxFileSzieMb = 50; // max 50MB + if (!CheckFileLegality(dst, strlen(dst), maxFileSzieMb)) { + Logger("failed to check dir.", LEVEL_ERROR, SCREEN_YES); + return false; + } + + return CheckWhiteList(dst) ? true : false; +} + +#define NUM_OF_CMD_ARGS 6 + +static struct { + const char c; + CmdArgParser parser; +} g_cmdArgParsers[NUM_OF_CMD_ARGS] = { + {'d', DevicesCmdArgParser}, + {'p', PidCmdArgParser}, + {'r', RootfsCmdArgParser}, + {'o', OptionsCmdArgParser}, + {'f', MountFileCmdArgParser}, + {'i', MountDirCmdArgParser} +}; + +static int ParseOneCmdArg(struct CmdArgs *args, char indicator, const char *value) +{ + if (args == NULL || value == NULL) { + Logger("args, value pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + int i; + for (i = 0; i < NUM_OF_CMD_ARGS; i++) { + if (g_cmdArgParsers[i].c == indicator) { + break; + } + } + + bool isOK = g_cmdArgParsers[i].parser(args, value); + if (!isOK) { + char* str = FormatLogMessage("failed while parsing cmd arg, indicate char: %c, value: %s.", indicator, value); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + return 0; +} + +static inline bool IsCmdArgsValid(const struct CmdArgs *args) +{ + if (args == NULL) { + Logger("args pointer is null!", LEVEL_ERROR, SCREEN_YES); + return false; + } + return (strlen(args->devices) > 0) && (strlen(args->rootfs) > 0) && (args->pid > 0); +} + +static int ParseDeviceIDs(size_t *idList, size_t *idListSize, char *devices) +{ + if (idList == NULL || idListSize == NULL || devices == NULL) { + Logger("idList, idListSize, devices pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + static const char *sep = ","; + char *token = NULL; + char *context = NULL; + size_t idx = 0; + + token = strtok_s(devices, sep, &context); + while (token != NULL && idx < *idListSize) { + if (idx >= *idListSize) { + char* str = FormatLogMessage("too many devices(%u), support %u devices maximally", idx, *idListSize); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + + errno = 0; + idList[idx] = strtoul((const char *)token, NULL, DECIMAL); + if (errno != 0) { + char* str = FormatLogMessage("failed to convert device id (%s) from cmd args", token); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + + idx++; + token = strtok_s(NULL, sep, &context); + } + + *idListSize = idx; + return 0; +} + +int DoPrepare(const struct CmdArgs *args, struct ParsedConfig *config) +{ + if (args == NULL || config == NULL) { + Logger("args, config pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + int ret; + errno_t err; + + err = strcpy_s(config->rootfs, BUF_SIZE, args->rootfs); + if (err != EOK) { + Logger("failed to copy rootfs path to parsed config.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + ret = ParseDeviceIDs(config->devices, &config->devicesNr, (char *)args->devices); + if (ret < 0) { + Logger("failed to parse device ids from cmdline argument", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + ret = GetNsPath(args->pid, "mnt", config->containerNsPath, BUF_SIZE); + if (ret < 0) { + char* str = FormatLogMessage("failed to get container mnt ns path: pid(%d).", args->pid); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + ret = GetCgroupPath(args->pid, config->cgroupPath, BUF_SIZE); + if (ret < 0) { + Logger("failed to get cgroup path.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + char originNsPath[BUF_SIZE] = {0}; + ret = GetSelfNsPath("mnt", originNsPath, BUF_SIZE); + if (ret < 0) { + Logger("failed to get self ns path.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + config->originNsFd = open((const char *)originNsPath, O_RDONLY); // proc接口,非外部输入 + if (config->originNsFd < 0) { + char* str = FormatLogMessage("failed to get self ns fd: %s.", originNsPath); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + + config->files = (const struct MountList *)&args->files; + config->dirs = (const struct MountList *)&args->dirs; + + return 0; +} + +int SetupContainer(struct CmdArgs *args) +{ + if (args == NULL) { + Logger("args pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + int ret; + struct ParsedConfig config; + + InitParsedConfig(&config); + + Logger("prepare necessary config", LEVEL_INFO, SCREEN_YES); + ret = DoPrepare(args, &config); + if (ret < 0) { + Logger("failed to prepare nesessary config.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + // enter container's mount namespace + Logger("enter container's mount namespace", LEVEL_INFO, SCREEN_YES); + ret = EnterNsByPath((const char *)config.containerNsPath, CLONE_NEWNS); + if (ret < 0) { + char* str = FormatLogMessage("failed to set to container ns: %s.", config.containerNsPath); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + close(config.originNsFd); + return -1; + } + Logger("do mounting", LEVEL_INFO, SCREEN_YES); + ret = DoMounting(&config); + if (ret < 0) { + Logger("failed to do mounting.", LEVEL_ERROR, SCREEN_YES); + close(config.originNsFd); + return -1; + } + Logger("setup up cgroup", LEVEL_INFO, SCREEN_YES); + ret = SetupCgroup(&config); + if (ret < 0) { + Logger("failed to set up cgroup.", LEVEL_ERROR, SCREEN_YES); + close(config.originNsFd); + return -1; + } + + // back to original namespace + Logger("back to original namespace", LEVEL_INFO, SCREEN_YES); + ret = EnterNsByFd(config.originNsFd, CLONE_NEWNS); + if (ret < 0) { + Logger("failed to set ns back.", LEVEL_ERROR, SCREEN_YES); + close(config.originNsFd); + return -1; + } + + close(config.originNsFd); + return 0; +} + +int Process(int argc, char **argv) +{ + if (argv == NULL) { + Logger("argv pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + if (argc > MAX_ARGC) { + Logger("too many arguments!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + int c; + int ret; + struct CmdArgs args = {0}; + + Logger("runc start prestart-hook ...", LEVEL_INFO, SCREEN_YES); + while ((c = getopt_long(argc, argv, "d:p:r:o:f:i", g_cmdOpts, NULL)) != -1) { + ret = ParseOneCmdArg(&args, (char)c, optarg); + if (ret < 0) { + Logger("failed to parse cmd args.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + } + Logger("verify parameters valid and parse runtime options", LEVEL_INFO, SCREEN_YES); + if (!IsCmdArgsValid(&args)) { + Logger("information not completed or valid.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + ParseRuntimeOptions(args.options); + Logger("setup container config ...", LEVEL_INFO, SCREEN_YES); + ret = SetupContainer(&args); + if (ret < 0) { + Logger("failed to setup container.", LEVEL_ERROR, SCREEN_YES); + return ret; + } + Logger("prestart-hook setup container successful.", LEVEL_INFO, SCREEN_YES); + return 0; +} + +#ifdef gtest +int _main(int argc, char **argv) +{ +#else +int main(int argc, char **argv) +{ +#endif + int ret = Process(argc, argv); + return ret; +} \ No newline at end of file diff --git a/cli/src/ns.c b/cli/src/ns.c new file mode 100644 index 0000000000000000000000000000000000000000..3f4fb3eb5f23eaeeb363fc32d989a578edeaf169 --- /dev/null +++ b/cli/src/ns.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#define _GNU_SOURCE +#include "ns.h" + +#include +#include +#include +#include "basic.h" +#include "securec.h" +#include "utils.h" +#include "logger.h" + +int GetNsPath(const long pid, const char *nsType, char *buf, const size_t bufSize) +{ + if ((nsType == NULL) || (buf == NULL)) { + return -1; + } + static const char *fmtStr = "/proc/%d/ns/%s"; + return sprintf_s(buf, bufSize, fmtStr, pid, nsType); +} + +int GetSelfNsPath(const char *nsType, char *buf, const size_t bufSize) +{ + if ((nsType == NULL) || (buf == NULL)) { + return -1; + } + static const char *fmtStr = "/proc/self/ns/%s"; + return sprintf_s(buf, bufSize, fmtStr, nsType); +} + +int EnterNsByFd(int fd, int nsType) +{ + int ret = setns(fd, nsType); + if (ret < 0) { + char* str = FormatLogMessage("failed to set ns: fd(%d).", fd); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + return 0; +} + +int EnterNsByPath(const char *path, int nsType) +{ + if (path == NULL) { + return -1; + } + int fd; + int ret; + + fd = open(path, O_RDONLY); // proc文件接口,非外部输入 + if (fd < 0) { + Logger("Failed to open ns path.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + ret = EnterNsByFd(fd, nsType); + if (ret < 0) { + Logger("failed to set ns.", LEVEL_ERROR, SCREEN_YES); + close(fd); + return -1; + } + + close(fd); + return 0; +} \ No newline at end of file diff --git a/cli/src/ns.h b/cli/src/ns.h new file mode 100644 index 0000000000000000000000000000000000000000..c9d20528e5ccb27eca055689693858ff17b1e527 --- /dev/null +++ b/cli/src/ns.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#ifndef _NS_H +#define _NS_H + +#include + +int GetNsPath(const long pid, const char *nsType, char *buf, const size_t bufSize); +int GetSelfNsPath(const char *nsType, char *buf, const size_t bufSize); +int EnterNsByFd(int fd, int nsType); +int EnterNsByPath(const char *path, int nsType); + +#endif \ No newline at end of file diff --git a/cli/src/options.c b/cli/src/options.c new file mode 100644 index 0000000000000000000000000000000000000000..e34a3a4cc4ff7c7d8029f7859dccfd585b75cb42 --- /dev/null +++ b/cli/src/options.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#include "options.h" + +#include +#include +#include "securec.h" + +static struct { + bool noDrv; + bool isVirtual; +} g_runtimeOptions; + +static struct { + const char *name; + bool *flag; +} g_optionNameFlagTable[] = { + {"NODRV", &g_runtimeOptions.noDrv}, // 不挂载Driver + {"VIRTUAL", &g_runtimeOptions.isVirtual}, + {NULL, NULL} +}; + +void ParseRuntimeOptions(const char *options) +{ + if (options == NULL) { + (void)fprintf(stderr, "options pointer is null!\n"); + return; + } + + // set defaults value + g_runtimeOptions.noDrv = false; + g_runtimeOptions.isVirtual = false; + + static const char *seperator = ","; + char *runtimeOptions = strdup(options); + if (runtimeOptions == NULL) { + (void)fprintf(stderr, "strdup failed!\n"); + return; + } + char *context = NULL; + char *token = NULL; + + for (token = strtok_s(runtimeOptions, seperator, &context); + token != NULL; + token = strtok_s(NULL, seperator, &context)) { + for (int i = 0; g_optionNameFlagTable[i].name != NULL; i++) { + if (strcmp((const char *)token, g_optionNameFlagTable[i].name) == 0) { + *g_optionNameFlagTable[i].flag = true; + } + } + } + + free(runtimeOptions); +} + +bool IsOptionNoDrvSet() +{ + return g_runtimeOptions.noDrv; +} + +bool IsVirtual() +{ + return g_runtimeOptions.isVirtual; +} diff --git a/cli/src/options.h b/cli/src/options.h new file mode 100644 index 0000000000000000000000000000000000000000..aade70ba31d9f3713fb3deb9ad29b5a332ae797c --- /dev/null +++ b/cli/src/options.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#ifndef _OPTIONS_H +#define _OPTIONS_H + +#include + +void ParseRuntimeOptions(const char *options); +bool IsOptionNoDrvSet(); +bool IsVirtual(); + +#endif diff --git a/cli/src/u_mount.c b/cli/src/u_mount.c new file mode 100644 index 0000000000000000000000000000000000000000..99b88f9ff2fbe3c3ccecd2adca56f93f8b65de93 --- /dev/null +++ b/cli/src/u_mount.c @@ -0,0 +1,447 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#include "u_mount.h" + +#include +#include +#include +#include +#include +#include "securec.h" + +#include "basic.h" +#include "utils.h" +#include "options.h" +#include "logger.h" + +static bool checkSrcFile(const char *src) +{ + struct stat fileStat; + if (stat(src, &fileStat) != 0) { + return -1; // 待挂载源文件不存在 + } + if ((S_ISREG(fileStat.st_mode) != 0) || (S_ISDIR(fileStat.st_mode) != 0)) { // 只校验文件和目录 + const size_t maxFileSzieMb = 10 * 1024; // max 10 G + if (!CheckExternalFile(src, strlen(src), maxFileSzieMb, false)) { + char* str = FormatLogMessage("failed to mount src: %s.", src); + Logger(str, LEVEL_ERROR, SCREEN_YES); + return false; + } + } + if (S_ISDIR(fileStat.st_mode) != 0) { // 目录则增加递归校验子集 + if (!GetFileSubsetAndCheck(src, strlen(src))) { + char* str = FormatLogMessage("Check file subset failed: %s.", src); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return false; + } + } + return true; +} + +int Mount(const char *src, const char *dst) +{ + if (src == NULL || dst == NULL) { + Logger("src pointer or dst pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + static const unsigned long mountFlags = MS_BIND; + static const unsigned long remountFlags = MS_BIND | MS_REMOUNT | MS_RDONLY | MS_NOSUID; + if (!checkSrcFile(src)) { + return -1; + } + int ret = mount(src, dst, NULL, mountFlags, NULL); + if (ret < 0) { + Logger("failed to mount src.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + ret = mount(NULL, dst, NULL, remountFlags, NULL); + if (ret < 0) { + Logger("failed to re-mount. dst.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + return 0; +} + +static bool GetDeviceCheckParam(const char *rootfs, const char *srcDeviceName, + struct PathInfo* pathInfo) +{ + if (rootfs == NULL || srcDeviceName == NULL || pathInfo == NULL) { + Logger("rootfs, srcDeviceName, pathInfo pointer are null!", LEVEL_ERROR, SCREEN_YES); + return false; + } + return true; +} + +static int GetDeviceMntSrcDstProcess(const char *rootfs, const char *srcDeviceName, + const char *dstDeviceName, struct PathInfo* pathInfo) +{ + if (!GetDeviceCheckParam(rootfs, srcDeviceName, pathInfo)) { + return -1; + } + int ret = 0; + errno_t err; + char unresolvedDst[BUF_SIZE] = {0}; + char resolvedDst[PATH_MAX] = {0}; + size_t srcBufSize = pathInfo->srcLen; + size_t dstBufSize = pathInfo->dstLen; + char *src = pathInfo->src; + char *dst = pathInfo->dst; + + ret = sprintf_s(src, srcBufSize, "/dev/%s", srcDeviceName); + if (ret < 0) { + return -1; + } + ret = sprintf_s(unresolvedDst, BUF_SIZE, "%s%s", rootfs, src); + if (ret < 0) { + return -1; + } + if (realpath(unresolvedDst, resolvedDst) == NULL && errno != ENOENT) { + Logger("cannot canonicalize device dst.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + if (dstDeviceName != NULL) { + ret = sprintf_s(dst, dstBufSize, "%s/dev/%s", rootfs, dstDeviceName); + if (ret < 0) { + return -1; + } + } else { + err = strcpy_s(dst, dstBufSize, resolvedDst); + if (err != EOK) { + Logger("failed to copy resolved device mnt path to dst.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + } + return 0; +} + +static int GetDeviceMntSrcDst(const char *rootfs, const char *srcDeviceName, + const char *dstDeviceName, struct PathInfo* pathInfo) +{ + if (!GetDeviceCheckParam(rootfs, srcDeviceName, pathInfo)) { + return -1; + } + + int ret = VerifyPathInfo(pathInfo); + if (ret < 0) { + return -1; + } + + return GetDeviceMntSrcDstProcess(rootfs, srcDeviceName, dstDeviceName, pathInfo); +} + +static bool MountDeviceProcess(const char* dst, const char* src, const mode_t mode) +{ + int ret; + errno = 0; + struct stat dstStat; + ret = stat(dst, &dstStat); + if (ret == 0 && S_ISCHR(dstStat.st_mode)) { + return true; // 特权容器自动挂载HOST所有设备,故此处跳过 + } else if (ret == 0) { + Logger("dst already exists but not a char device as expected.", LEVEL_ERROR, SCREEN_YES); + return false; + } else if (ret < 0 && errno != ENOENT) { + Logger("failed to check dst stat", LEVEL_ERROR, SCREEN_YES); + return false; + } + ret = MakeMountPoints(dst, mode); + if (ret < 0) { + Logger("failed to create mount dst file.", LEVEL_ERROR, SCREEN_YES); + return false; + } + + ret = Mount(src, dst); + if (ret < 0) { + Logger("failed to mount dev.", LEVEL_ERROR, SCREEN_YES); + return false; + } + return true; +} + +int MountDevice(const char *rootfs, const char *srcDeviceName, const char *dstDeviceName) +{ + int ret; + char src[BUF_SIZE] = {0}; + char dst[BUF_SIZE] = {0}; + struct PathInfo pathInfo = {src, BUF_SIZE, dst, BUF_SIZE}; + ret = GetDeviceMntSrcDst(rootfs, srcDeviceName, dstDeviceName, &pathInfo); + if (ret < 0) { + Logger("failed to get mount src and dst path.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + struct stat srcStat; + ret = stat((const char *)src, &srcStat); + if (ret < 0) { + Logger("failed to stat src.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + if (!MountDeviceProcess(dst, src, srcStat.st_mode)) { + return -1; + } + + return 0; +} + +int DoDeviceMounting(const char *rootfs, const char *device_name, const size_t ids[], size_t idsNr) +{ + if (rootfs == NULL || device_name == NULL || ids == NULL) { + Logger("rootfs, device_name pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + char srcDeviceName[BUF_SIZE] = {0}; + char dstDeviceName[BUF_SIZE] = {0}; + + for (size_t idx = 0; idx < idsNr; idx++) { + int srcRet = sprintf_s(srcDeviceName, BUF_SIZE, "%s%u", device_name, ids[idx]); + int dstRet = sprintf_s(dstDeviceName, BUF_SIZE, "%s%u", DEVICE_NAME, ids[idx]); + if (srcRet < 0 || dstRet < 0) { + char* str = FormatLogMessage("assemble device name failed, id: %u.", ids[idx]); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + int ret = MountDevice(rootfs, srcDeviceName, dstDeviceName); + if (ret < 0) { + Logger("failed to mount device.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + } + + return 0; +} + +int MountFile(const char *rootfs, const char *filepath) +{ + if (rootfs == NULL || filepath == NULL) { + Logger("rootfs, filepath pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + int ret; + char dst[BUF_SIZE] = {0}; + + ret = sprintf_s(dst, BUF_SIZE, "%s%s", rootfs, filepath); + if (ret < 0) { + Logger("failed to assemble file mounting path.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + struct stat srcStat; + ret = stat(filepath, &srcStat); + if (ret < 0) { + return 0; + } + + ret = MakeMountPoints(dst, srcStat.st_mode); + if (ret < 0) { + Logger("failed to create mount dst file.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + ret = Mount(filepath, dst); + if (ret < 0) { + Logger("failed to mount dev.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + return 0; +} + +int MountDir(const char *rootfs, const char *src) +{ + if (rootfs == NULL || src == NULL) { + Logger("rootfs, src pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + int ret; + char dst[BUF_SIZE] = {0}; + + ret = sprintf_s(dst, BUF_SIZE, "%s%s", rootfs, src); + if (ret < 0) { + return -1; + } + + struct stat srcStat; + ret = stat(src, &srcStat); + if (ret < 0) { + return 0; + } + + ret = MakeDirWithParent(dst, DEFAULT_DIR_MODE); + if (ret < 0) { + Logger("failed to make dir.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + ret = Mount(src, dst); + if (ret < 0) { + Logger("failed to mount dir", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + return 0; +} + +bool DoMounting200RC(bool* is200Rc) +{ + char devmmPath[PATH_MAX] = {0}; + char hisiPath[PATH_MAX] = {0}; + if ((sprintf_s(devmmPath, PATH_MAX, "/dev/%s", DEVMM_SVM) < 0) || + (sprintf_s(hisiPath, PATH_MAX, "/dev/%s", HISI_HDC) < 0)) { + Logger("failed to assemble dev path.", LEVEL_ERROR, SCREEN_YES); + return false; + } + struct stat devStat; // 200 soc 不需要挂载此两个设备 + if ((stat(devmmPath, &devStat) != 0) && (stat(hisiPath, &devStat) != 0)) { + Logger("200 Soc.", LEVEL_ERROR, SCREEN_YES); + *is200Rc = true; + } + return true; +} + +static bool DoMountingDevice(const char *rootfs) +{ + int ret = MountDevice(rootfs, DEVMM_SVM, NULL); + if (ret < 0) { + char* str = FormatLogMessage("failed to mount device %s.", DEVMM_SVM); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return false; + } + + ret = MountDevice(rootfs, HISI_HDC, NULL); + if (ret < 0) { + char* str = FormatLogMessage("failed to mount device %s.", HISI_HDC); + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return false; + } + return true; +} + +int DoCtrlDeviceMounting(const char *rootfs) +{ + if (rootfs == NULL) { + Logger("rootfs pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + /* device */ + int ret = MountDevice(rootfs, DAVINCI_MANAGER, NULL); + if (ret < 0) { + char* str = FormatLogMessage("failed to mount device %s.", DAVINCI_MANAGER); // error1 + Logger(str, LEVEL_ERROR, SCREEN_YES); + free(str); + return -1; + } + bool is200Rc = false; + if (!DoMounting200RC(&is200Rc)) { + return -1; + } + if (is200Rc) { + return 0; + } + if (!DoMountingDevice(rootfs)) { + return -1; + } + + return 0; +} + +int DoDirectoryMounting(const char *rootfs, const struct MountList *list) +{ + if (rootfs == NULL || list == NULL) { + Logger("rootfs, list pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + int ret; + for (unsigned int i = 0; i < list->count; i++) { + ret = MountDir(rootfs, (const char *)&list->list[i][0]); + if (ret < 0) { + Logger("failed to do directory mounting", LEVEL_ERROR, SCREEN_YES); + return -1; + } + } + + return 0; +} + +int DoFileMounting(const char *rootfs, const struct MountList *list) +{ + if (rootfs == NULL || list == NULL) { + Logger("rootfs, list pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + int ret; + for (unsigned int i = 0; i < list->count; i++) { + ret = MountFile(rootfs, (const char *)&list->list[i][0]); + if (ret < 0) { + Logger("failed to do file mounting for.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + } + + return 0; +} + +int DoMounting(const struct ParsedConfig *config) +{ + if (config == NULL) { + Logger("config pointer is null!", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + int ret; + ret = DoDeviceMounting(config->rootfs, + (IsVirtual() ? VDEVICE_NAME : DEVICE_NAME), + config->devices, config->devicesNr); + if (ret < 0) { + Logger("failed to mount devices.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + ret = DoCtrlDeviceMounting(config->rootfs); + if (ret < 0) { + Logger("failed to mount ctrl devices.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + if (IsOptionNoDrvSet()) { + return 0; + } + + ret = DoFileMounting(config->rootfs, config->files); + if (ret < 0) { + Logger("failed to mount files.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + ret = DoDirectoryMounting(config->rootfs, config->dirs); + if (ret < 0) { + Logger("failed to do mount directories.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + return 0; +} \ No newline at end of file diff --git a/cli/src/u_mount.h b/cli/src/u_mount.h new file mode 100644 index 0000000000000000000000000000000000000000..59d90fe8b8e076e0e51df6a994e9f598e260fe0a --- /dev/null +++ b/cli/src/u_mount.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#ifndef _MOUNT_H +#define _MOUNT_H + +#include +#include "basic.h" + +int DoMounting(const struct ParsedConfig *config); +bool DoMounting200RC(bool* is200Rc); + +#endif \ No newline at end of file diff --git a/cli/src/utils.c b/cli/src/utils.c new file mode 100644 index 0000000000000000000000000000000000000000..605e3812d2fb2a87d9f5db37ea1215e01a7e6672 --- /dev/null +++ b/cli/src/utils.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" +#include "logger.h" + +#define LOG_LENGTH 1024 + +static bool g_checkWgroup = true; + +char *FormatLogMessage(char *format, ...) +{ + if (format == NULL) { + (void)fprintf(stderr, "format pointer is null!\n"); + return NULL; + } + + va_list list; + // 获取格式化后字符串的长度 + va_start(list, format); + char buff[LOG_LENGTH] = {0}; + int ret = vsnprintf_s(buff, sizeof(buff), sizeof(buff) - 1, format, list); + va_end(list); + if (ret <= 0) { + return NULL; + } + size_t size = (size_t)ret; + size++; + // 复位va_list, 将格式化字符串写入到buf + va_start(list, format); + char *buf = (char *)malloc(size); + if (buf == NULL) { + return NULL; + } + ret = vsnprintf_s(buf, size, size - 1, format, list); + va_end(list); + if (ret <= 0) { + free(buf); + return NULL; + } + return buf; +} + +int IsStrEqual(const char *s1, const char *s2) +{ + return (strcmp(s1, s2) == 0); +} + +bool StrHasPrefix(const char *str, const char *prefix) +{ + return (strncmp(str, prefix, strlen(prefix)) == 0); +} + +static int MkDir(const char *dir, mode_t mode) +{ + if (dir == NULL) { + (void)fprintf(stderr, "dir pointer is null!\n"); + return -1; + } + + return mkdir(dir, mode); +} + +int VerifyPathInfo(const struct PathInfo* pathInfo) +{ + if (pathInfo == NULL || pathInfo->dst == NULL || pathInfo->src == NULL) { + return -1; + } + return 0; +} + +int CheckDirExists(const char *dir) +{ + if (dir == NULL) { + (void)fprintf(stderr, "dir pointer is null!\n"); + return -1; + } + + DIR *ptr = opendir(dir); + if (NULL == ptr) { + return -1; + } + + closedir(ptr); + return 0; +} + +int GetParentPathStr(const char *path, char *parent, size_t bufSize) +{ + if (path == NULL || parent == NULL) { + (void)fprintf(stderr, "path pointer or parentPath is null!\n"); + return -1; + } + + char *ptr = strrchr(path, '/'); + if (ptr == NULL) { + return 0; + } + + int len = (int)strlen(path) - (int)strlen(ptr); + if (len < 1) { + return 0; + } + + errno_t ret = strncpy_s(parent, bufSize, path, len); + if (ret != EOK) { + return -1; + } + + return 0; +} + +int MakeDirWithParent(const char *path, mode_t mode) +{ + if (path == NULL) { + (void)fprintf(stderr, "path pointer is null!\n"); + return -1; + } + + if (*path == '\0' || *path == '.') { + return 0; + } + if (CheckDirExists(path) == 0) { + return 0; + } + + char parentPath[BUF_SIZE] = {0}; + GetParentPathStr(path, parentPath, BUF_SIZE); + if (strlen(parentPath) > 0 && MakeDirWithParent(parentPath, mode) < 0) { + return -1; + } + + int ret = MkDir(path, mode); + if (ret < 0) { + return -1; + } + + return 0; +} + +int MakeMountPoints(const char *path, mode_t mode) +{ + if (path == NULL) { + (void)fprintf(stderr, "path pointer is null!\n"); + return -1; + } + + /* directory */ + char parentDir[BUF_SIZE] = {0}; + GetParentPathStr(path, parentDir, BUF_SIZE); + + int ret = MakeDirWithParent(parentDir, DEFAULT_DIR_MODE); + if (ret < 0) { + Logger("Failed to make parent dir for file.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + char resolvedPath[PATH_MAX] = {0}; + if (realpath(path, resolvedPath) == NULL && errno != ENOENT) { + Logger("failed to resolve path.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + int fd = open(resolvedPath, O_NOFOLLOW | O_CREAT, mode); + if (fd < 0) { + Logger("cannot create file.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + close(fd); + return 0; +} + +static bool ShowExceptionInfo(const char* exceptionInfo) +{ + (void)fprintf(stderr, exceptionInfo); + (void)fprintf(stderr, "\n"); + return false; +} + +static bool CheckFileOwner(const struct stat fileStat, const bool checkOwner) +{ + if (checkOwner) { + if ((fileStat.st_uid != ROOT_UID) && (fileStat.st_uid != geteuid())) { // 操作文件owner非root/自己 + return ShowExceptionInfo("Please check the folder owner!"); + } + } + return true; +} + +static bool CheckParentDir(const char* filePath, const size_t filePathLen, + struct stat fileStat, const bool checkOwner) +{ + char buf[PATH_MAX] = {0}; + if (strncpy_s(buf, sizeof(buf), filePath, filePathLen) != EOK) { + return false; + } + for (int iLoop = 0; iLoop < PATH_MAX; iLoop++) { + if (!CheckFileOwner(fileStat, checkOwner)) { + return false; + } + if ((fileStat.st_mode & S_IWOTH) != 0) { // 操作文件对other用户可写 + return ShowExceptionInfo("Please check the write permission!"); + } + if (g_checkWgroup && ((fileStat.st_mode & S_IWGRP) != 0)) { // 除日志文件外对group可写 + return ShowExceptionInfo("Please check the write permission!"); + } + if (S_ISLNK(fileStat.st_mode) != 0) { // 存在软链接 + return ShowExceptionInfo("resolvedPath is symbolic link!"); + } + if ((strcmp(buf, "/") == 0) || (strstr(buf, "/") == NULL)) { + break; + } + if (strcmp(dirname(buf), ".") == 0) { + break; + } + if (lstat(buf, &fileStat) != 0) { + return false; + } + } + return true; +} + +static bool CheckLegality(const char* filePath, const size_t filePathLen, + const unsigned long long maxFileSzieMb, const bool checkOwner) +{ + const unsigned long long maxFileSzieB = maxFileSzieMb * 1024 * 1024; + char buf[PATH_MAX] = {0}; + if (strncpy_s(buf, sizeof(buf), filePath, filePathLen) != EOK) { + return false; + } + struct stat fileStat; + if ((lstat(buf, &fileStat) != 0) || + ((S_ISREG(fileStat.st_mode) == 0) && (S_ISDIR(fileStat.st_mode) == 0))) { + return ShowExceptionInfo("filePath does not exist or is not a file/dir!"); + } + if ((maxFileSzieMb > 0) && (fileStat.st_size >= maxFileSzieB)) { // 文件大小超限,日志文件不校验大小,由轮滚机制保护 + return ShowExceptionInfo("fileSize out of bounds!"); + } + return CheckParentDir(filePath, filePathLen, fileStat, checkOwner); +} + +bool IsValidChar(const char c) +{ + if (isalnum(c) != 0) { + return true; + } + // ._-/~为合法字符 + if ((c == '.') || (c == '_') || + (c == '-') || (c == '/') || (c == '~')) { + return true; + } + return false; +} + +bool CheckExternalFile(const char* filePath, const size_t filePathLen, + const size_t maxFileSzieMb, const bool checkOwner) +{ + if ((filePathLen > PATH_MAX) || (filePathLen <= 0)) { // 长度越界 + return ShowExceptionInfo("filePathLen out of bounds!"); + } + for (size_t iLoop = 0; iLoop < filePathLen; iLoop++) { + if (!IsValidChar(filePath[iLoop])) { // 非法字符 + return ShowExceptionInfo("filePath has an illegal character!"); + } + } + return CheckLegality(filePath, filePathLen, maxFileSzieMb, checkOwner); +} + +bool CheckExistsFile(const char* filePath, const size_t filePathLen, + const size_t maxFileSzieMb, const bool checkWgroup) +{ + struct stat fileStat; + if (lstat(filePath, &fileStat) != 0) { + return true; // 文件不存在 + } + if (S_ISREG(fileStat.st_mode) == 0) { // 不是文件 + return false; + } + g_checkWgroup = checkWgroup; + if (!CheckExternalFile(filePath, filePathLen, maxFileSzieMb, true)) { + g_checkWgroup = true; + return false; + } + g_checkWgroup = true; + return true; +} + +static bool CheckFileSubset(const char* filePath, const size_t filePathLen, + const size_t maxFileSzieMb) +{ + const unsigned long long maxFileSzieB = maxFileSzieMb * 1024 * 1024; + int iLoop; + if ((filePathLen > PATH_MAX) || (filePathLen <= 0)) { // 长度越界 + return ShowExceptionInfo("filePathLen out of bounds!"); + } + for (iLoop = 0; iLoop < filePathLen; iLoop++) { + if (!IsValidChar(filePath[iLoop])) { // 非法字符 + return ShowExceptionInfo("filePath has an illegal character!"); + } + } + struct stat fileStat; + if (lstat(filePath, &fileStat) != 0) { + return ShowExceptionInfo("filePath does not exist!"); + } + if (S_ISLNK(fileStat.st_mode) != 0) { // 存在软链接 + return ShowExceptionInfo("filePath is symbolic link!"); + } + if (fileStat.st_size >= maxFileSzieB) { // 文件大小超限 + return ShowExceptionInfo("fileSize out of bounds!"); + } + return true; +} + +bool GetFileSubsetAndCheck(const char *basePath, const size_t basePathLen) +{ + DIR *dir = NULL; + struct dirent *ptr = NULL; + char base[PATH_MAX] = {0}; + + if ((dir = opendir(basePath)) == NULL) { + return ShowExceptionInfo("Open dir error!"); + } + while ((ptr = readdir(dir)) != NULL) { + if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) { + continue; + } + memset_s(base, PATH_MAX, 0, PATH_MAX); + if (strcpy_s(base, PATH_MAX, basePath) != 0) { + return ShowExceptionInfo("Strcpy failed!"); + } + if (strcat_sp(base, PATH_MAX, "/") != 0 || + strcat_sp(base, PATH_MAX, ptr->d_name) != 0) { + return ShowExceptionInfo("Strcat failed!"); + } + if (ptr->d_type == DT_REG) { // 文件 + const size_t maxFileSzieMb = 10; // max 10 MB + if (!CheckFileSubset(base, strlen(base), maxFileSzieMb)) { + return false; + } + } else if (ptr->d_type == DT_LNK) { // 软链接 + return ShowExceptionInfo("FilePath has a soft link!"); + } else if (ptr->d_type == DT_DIR) { // 目录 + if (!GetFileSubsetAndCheck(base, strlen(base))) { + return false; + } + } + } + return true; +} \ No newline at end of file diff --git a/cli/src/utils.h b/cli/src/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..70d93d152ea08a169b762cdb3140f8f1f0917742 --- /dev/null +++ b/cli/src/utils.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#ifndef _UTILS_H +#define _UTILS_H + +#include +#include +#include +#include +#include +#include +#include "basic.h" + +char *FormatLogMessage(char *format, ...); +int IsStrEqual(const char *s1, const char *s2); +bool StrHasPrefix(const char *str, const char *prefix); +int VerifyPathInfo(const struct PathInfo* pathInfo); +int CheckDirExists(const char *dir); +int GetParentPathStr(const char *path, char *parent, size_t bufSize); +int MakeDirWithParent(const char *path, mode_t mode); +int MakeMountPoints(const char *path, mode_t mode); +bool IsValidChar(const char c); +bool CheckExternalFile(const char* filePath, const size_t filePathLen, + const size_t maxFileSzieMb, const bool checkOwner); +bool GetFileSubsetAndCheck(const char *basePath, const size_t basePathLen); +bool CheckExistsFile(const char* filePath, const size_t filePathLen, + const size_t maxFileSzieMb, const bool checkWgroup); +#endif \ No newline at end of file diff --git a/cli/test/dt/CMakeLists.txt b/cli/test/dt/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7b97ddf8387e1e4fecd88ac834ad1733d3c72fd5 --- /dev/null +++ b/cli/test/dt/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 2.6) + +project(demo) +macro(dtcenter_init_complier_settings) + set(compiler_flags + CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELWITHDEBINFO + ) + + + if(UNIX) + # 调试开关 + add_definitions(-g) + # 关闭优化开关 + add_definitions(-O0) + # 4.8.5编译选项 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + + # ASAN编译选项 + if (BUILD_ASAN) + add_definitions(-fsanitize=address -fsanitize-recover=all) + add_definitions(-fno-omit-frame-pointer -fno-stack-protector) + add_definitions(-fsanitize=leak) + endif(BUILD_ASAN) + + if (BUILD_FUZZ) + # 包含FUZZ时,必须使用C++11语法编译 + # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + endif(BUILD_FUZZ) + + # DTCenter定义的宏 + add_definitions(-DDT_COMPILE_GCC -DAUTOSTAR_LINUX) + + # 默认不支持中文用例名,如果要支持请打开以下配置 + # add_definitions(-DDTCENTER_CN2EN) + + # GCC编译器告警开关 + add_definitions(-Wall -Wextra -D_GLIBCXX_USE_CXX11_ABI=1) + add_definitions(-Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-parentheses -Wno-write-strings -Wno-format-security) + add_definitions(-Wno-sign-compare -Wno-nonnull-compare -Wno-return-type -Wno-comment -Wno-ignored-qualifiers -Wno-missing-field-initializers) + add_definitions(-fprofile-arcs -ftest-coverage) + # 以下只对C++语言有有效 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -Wno-reorder") + + # GCC 4.3.4不支持 -Wno-conversion-null, 先注释掉 + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-conversion-null") + + #add_definitions(-rdynamic) + + + endif(UNIX) +endmacro() +#dtcenter_init_complier_settings() +#add_definitions(-g -O0) +#add_definitions(-DDT_COMPILE_GCC -DAUTOSTAR_LINUX) +add_definitions(-Dgtest) +#add_definitions(-lgcov --coverage) + +# GCC编译器告警开关 +#add_definitions(-Wall -Wextra) +#add_definitions(-Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-parentheses -Wno-write-strings -Wno-format-security) +#add_definitions(-Wno-sign-compare -Wno-return-type -Wno-comment -Wno-ignored-qualifiers) +#SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector -std=c11 -D _GNU_SOURCE -fprofile-arcs") +#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector -std=c++11 -D _GNU_SOURCE -fprofile-arcs") + +add_subdirectory(srclib) +set(UT_DEMO_SRCS testcase/main.cpp testcase/gtest_mytest.cpp) + +add_executable(ut_demo ${UT_DEMO_SRCS}) +target_link_libraries(ut_demo + ${PROJECT_SOURCE_DIR}/Depend/lib/libgtest.a + ${PROJECT_SOURCE_DIR}/Depend/lib/libmockcpp.a + ) +target_link_libraries(ut_demo -fprofile-arcs -pthread) +target_include_directories(ut_demo PUBLIC ${PROJECT_SOURCE_DIR}/Depend/googletest/googletest/include) +target_include_directories(ut_demo PUBLIC ${PROJECT_SOURCE_DIR}/Depend/mockcpp/3rdparty) +target_include_directories(ut_demo PUBLIC ${PROJECT_SOURCE_DIR}/Depend/mockcpp/include) +target_include_directories(ut_demo PUBLIC ${PROJECT_SOURCE_DIR}/Depend/HuaweiSecureC/include) +target_link_libraries(ut_demo demolib) diff --git a/cli/test/dt/Depend/CMakeLists.txt b/cli/test/dt/Depend/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..4754dfa364954d48823d3dcd2ede18831b5d0d1a --- /dev/null +++ b/cli/test/dt/Depend/CMakeLists.txt @@ -0,0 +1,38 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8) + +PROJECT(depend_lib) + +# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to +# make it prominent in the GUI. +#option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF) + +# Specify where is boost +option(USE_DTCENTER_BOOST "Use ${CMAKE_CURRENT_LIST_DIR}/boost header files or not." ON) +if(USE_DTCENTER_BOOST) + set(BOOST_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}") +else() + set(BOOST_INCLUDE_DIRS "/usr/include" CACHE PATH "path to boost include dir") +endif() + +# helper functions and macros used by dtcenter. +#include(${CMAKE_CURRENT_LIST_DIR}/../cmake/internal_utils.cmake) +#dtcenter_init_complier_settings() + +# Subdirectories +set(mockcpp_dir mockcpp) +set(gtest_dir googletest/googletest/) +set(gtest_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/${gtest_dir}) +set(mockcpp_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/${mockcpp_dir}) + +# 将gtest, mockcpp 打包成 depend_lib +set(gtest_library $) +set(mockcpp_library $) + +# global_stubs +ADD_LIBRARY(ut_main STATIC ${CMAKE_CURRENT_LIST_DIR}/public_stub.cpp + ${CMAKE_CURRENT_LIST_DIR}/ut_main.cpp) +TARGET_INCLUDE_DIRECTORIES(ut_main PUBLIC "${gtest_SOURCE_DIR}/include") +TARGET_INCLUDE_DIRECTORIES(ut_main PUBLIC "${mockcpp_SOURCE_DIR}/include") + +add_subdirectory(${gtest_dir} gtest.out) +add_subdirectory(${mockcpp_dir} mockcpp.out) diff --git a/cli/test/dt/Depend/googletest/README b/cli/test/dt/Depend/googletest/README new file mode 100644 index 0000000000000000000000000000000000000000..1887da6955abb267f48ef914b2819a410930253d --- /dev/null +++ b/cli/test/dt/Depend/googletest/README @@ -0,0 +1 @@ +googletest \ No newline at end of file diff --git a/cli/test/dt/Depend/libboundscheck/CMakeLists.txt b/cli/test/dt/Depend/libboundscheck/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e94c2c999f3a91957627ad49472cb33f74f0735a --- /dev/null +++ b/cli/test/dt/Depend/libboundscheck/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.0) +# 查找当前目录下的所有源文件 +# 并将名称保存到 LIB_SRC 变量 +aux_source_directory(src LIB_SRC) +include_directories(include) + +#生成链接库 +add_library(libboundscheck STATIC ${LIB_SRC}) +target_compile_options(libboundscheck PRIVATE -fstack-protector-all -fpie) diff --git a/cli/test/dt/Depend/mockcpp/README b/cli/test/dt/Depend/mockcpp/README new file mode 100644 index 0000000000000000000000000000000000000000..ee7b4de344524dcc8aed06efb042b83a9f9504a4 --- /dev/null +++ b/cli/test/dt/Depend/mockcpp/README @@ -0,0 +1 @@ +mockcpp \ No newline at end of file diff --git a/cli/test/dt/Depend/public_stub.cpp b/cli/test/dt/Depend/public_stub.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d00bb6eccd825e56c1a24b5580694cf146756f72 --- /dev/null +++ b/cli/test/dt/Depend/public_stub.cpp @@ -0,0 +1,20 @@ +/* + * Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. + * 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. + */ +#ifndef __PUBLIC_STUB_H__ +#define __PUBLIC_STUB_H__ + + + +#endif diff --git a/cli/test/dt/Depend/ut_main.cpp b/cli/test/dt/Depend/ut_main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4fa6a6911f93321a9cf6c3527f518d5e3c4cb669 --- /dev/null +++ b/cli/test/dt/Depend/ut_main.cpp @@ -0,0 +1,26 @@ +/* + * Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. + * 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. + */ +#include "gtest/gtest.h" +#include + +using namespace testing; + + +int main(int argc, char* argv[]) +{ + InitGoogleTest(&argc, argv); + int result = RUN_ALL_TESTS(); + return result; +} diff --git a/cli/test/dt/README.md b/cli/test/dt/README.md new file mode 100644 index 0000000000000000000000000000000000000000..479b4ed6de791c402e25c4be7951e6effeb58cae --- /dev/null +++ b/cli/test/dt/README.md @@ -0,0 +1,31 @@ +# 使用测试用例 + +进入Ascend Docker Runtime的根目录 +```shell +cd +``` +下载相关的开源库,注意对应的tag +```shell +git clone -b v2.7 https://github.com/sinojelly/mockcpp.git +git clone -b release-1.10.0 https://github.com/google/googletest.git +git clone -b v1.1.10 https://gitee.com/openeuler/libboundscheck.git +``` +进入dt目录 +```shell +cd cli/test/dt +``` +编译 +```shell +sh build.sh +``` +结果会显示相关的测试用例覆盖率 +```shell +... +Writing directory view page. +Overall coverage rate: + lines......: 63.3% (746 of 1179 lines) + functions..: 91.4% (74 of 81 functions) + branches...: 54.0% (409 of 758 branches) +-------------run_ut cli end------------------- +run_lcov_cli succeed. +``` \ No newline at end of file diff --git a/cli/test/dt/Scripts/pre.sh b/cli/test/dt/Scripts/pre.sh new file mode 100644 index 0000000000000000000000000000000000000000..6f546ba32805168b4079ddf1ce62fb2b99621492 --- /dev/null +++ b/cli/test/dt/Scripts/pre.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. +# +# 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. +# ============================================================================ + +cd ../Depend/ + +if [ -d "./lib" ]; then + rm -rf ./lib +fi + +BUILD_DIR="./build" +if [ ! -d "${BUILD_DIR}" ]; then + mkdir ${BUILD_DIR} +else + rm -rf ${BUILD_DIR}/* + echo "prefix clean" +fi +cd ${BUILD_DIR} +cmake .. +make + +mkdir ../lib +find . -name "*a" +cp ./lib/libgtest.a ../lib +cp ./mockcpp.out/src/libmockcpp.a ../lib +cp libut_main.a ../lib + +cd .. +if [ -d "${BUILD_DIR}" ]; then + rm -rf ${BUILD_DIR} +fi + +cd ../Scripts + diff --git a/cli/test/dt/build.sh b/cli/test/dt/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..00f01ebafcff95a576feabfed9ae3a406f1264ff --- /dev/null +++ b/cli/test/dt/build.sh @@ -0,0 +1,180 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. +# +# 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. +# ============================================================================ + +CUR_DIR=$(cd `dirname $0`;pwd) +SRC_ROOT="../../src" + +# execute pre script +usage() +{ + echo "usage:" + echo "./build.sh [module]." + echo "eg: ./build.sh" + echo "eg: ./build.sh cli" + exit 1 +} + +makepre() +{ + if [ -f ${CUR_DIR}/../../../googletest.tar.gz ]; then + cd ${CUR_DIR}/../../../ + tar xf ${CUR_DIR}/../../../googletest.tar.gz + mv googletest-release-1.10.0/ googletest + cd ${CUR_DIR} + fi + if [ -d ${CUR_DIR}/../../../googletest ]; then + cp -rf ${CUR_DIR}/../../../googletest/* ${CUR_DIR}/Depend/googletest/ + fi + + if [ -f ${CUR_DIR}/../../../mockcpp.tar.gz ]; then + cd ${CUR_DIR}/../../../ + tar xf ${CUR_DIR}/../../../mockcpp.tar.gz + cd ${CUR_DIR} + fi + if [ -d ${CUR_DIR}/../../../mockcpp ]; then + cp -rf ${CUR_DIR}/../../../mockcpp/* ${CUR_DIR}/Depend/mockcpp/ + cd ${CUR_DIR}/Depend/mockcpp + sed -i '16s/SET(MOCKCPP_SRC_ROOT ${CMAKE_SOURCE_DIR})/SET(MOCKCPP_SRC_ROOT ${CMAKE_SOURCE_DIR}\/mockcpp)/' ./src/CMakeLists.txt + cmake . + make + cd ${CUR_DIR} + fi + + # need to compile lib if it is not present + if [ ! -d Depend/lib ] + then + echo "-------------make pre begin-------------------" + cd Scripts + chmod u+x ./pre.sh + bash -ex ./pre.sh + cd - + echo "-------------make pre end---------------------" + fi + + if [ $? -ne 0 ] + then + return 1 + else + return 0 + fi +} + +build_boundscheck() +{ + echo "-------------build_ut boundscheck begin-------------------" + if [ -f ${CUR_DIR}/../../../libboundscheck.tar.gz ]; then + cd ${CUR_DIR}/../../../ + tar xf ${CUR_DIR}/../../../libboundscheck.tar.gz + mv libboundscheck-v1.1.10/ libboundscheck + cd ${CUR_DIR} + fi + if [ -d ${CUR_DIR}/../../../libboundscheck ]; then + cp -rf ${CUR_DIR}/../../../libboundscheck/* ${CUR_DIR}/Depend/libboundscheck/ + cp -rf ${CUR_DIR}/../../../libboundscheck/* ${SRC_ROOT}/libboundscheck/ + fi + cd ${CUR_DIR}/Depend/libboundscheck + if [ -d ./build ]; then + rm -rf ./build + fi + mkdir ./build + cd ./build + cmake .. + make + cp liblibboundscheck.a ${CUR_DIR}/srclib + echo "-------------build_ut boundscheck end-------------------" +} + +build_cli() +{ + echo "-------------build_ut cli begin-------------------" + cd ${CUR_DIR} + if [ -d ${CUR_DIR}/build ]; then + rm -rf ${CUR_DIR}/build + fi + pwd + find ../.. -name "*a" + mkdir ${CUR_DIR}/build + cd ${CUR_DIR}/build + cmake .. + make + echo "-------------build_ut cli end---------------------" +} + +run_lcov_cli() +{ + echo "-------------run_ut cli begin-------------------" + cd ${CUR_DIR}/build + ./ut_demo --gtest_output=xml:${CUR_DIR}/test_detail.xml + cd ${SRC_ROOT}/.. + ENABLE_BRANCH_COV="--rc lcov_branch_coverage=1" + lcov --no-external -o result.info -b . -d . -c $ENABLE_BRANCH_COV + genhtml --branch-coverage result.info -o Report $ENABLE_BRANCH_COV + cd ${CUR_DIR} + mkdir xml + cp -f test_detail.xml ./xml/test_detail.xml + mkdir html + cp -rf ${SRC_ROOT}/../Report/* ./html/ + echo "-------------run_ut cli end-------------------" +} + +main() +{ + # 找到SRC目录 + cd ${SRC_ROOT} + SRC_ROOT=$(pwd) + cd - + + # step_1 输入检测 + if [ $# -gt 1 ] + then + usage + return + fi + # step_2 执行pre脚本 + makepre + if [ $? -ne 0 ] + then + echo "pre failed." + return 1 + else + echo "pre succeed." + fi + + echo SRC_ROOT is "$SRC_ROOT" + + # step_3 编译对应模块 + build_boundscheck + build_cli + if [ $? -ne 0 ] + then + echo "build_cli failed." + return 1 + else + echo "build_cli succeed." + fi + + # step_4 执行ut_demo程序和lcov覆盖率 + run_lcov_cli + if [ $? -ne 0 ] + then + echo "run_lcov_cli failed." + return 1 + else + echo "run_lcov_cli succeed." + fi +} + +main $* diff --git a/cli/test/dt/cmake/internal_utils.cmake b/cli/test/dt/cmake/internal_utils.cmake new file mode 100644 index 0000000000000000000000000000000000000000..bd1dde27e63ff2d771b21581cad8654b1886009a --- /dev/null +++ b/cli/test/dt/cmake/internal_utils.cmake @@ -0,0 +1,72 @@ +macro(dtcenter_init_complier_settings) + set(compiler_flags + CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELWITHDEBINFO + ) + + if(UNIX) + # 调试开关 + add_definitions(-g) + # 关闭优化开关 + add_definitions(-O0) + # 4.8.5编译选项 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + + # ASAN编译选项 + if (BUILD_ASAN) + add_definitions(-fsanitize=address -fsanitize-recover=all) + add_definitions(-fno-omit-frame-pointer -fno-stack-protector) + add_definitions(-fsanitize=leak) + endif(BUILD_ASAN) + + if (BUILD_FUZZ) + # 包含FUZZ时,必须使用C++11语法编译 + # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + endif(BUILD_FUZZ) + + # DTCenter定义的宏 + add_definitions(-DDT_COMPILE_GCC -DAUTOSTAR_LINUX) + + # 默认不支持中文用例名,如果要支持请打开以下配置 + # add_definitions(-DDTCENTER_CN2EN) + + # GCC编译器告警开关 + add_definitions(-Wall -Wextra -D_GLIBCXX_USE_CXX11_ABI=1) + add_definitions(-Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-parentheses -Wno-write-strings -Wno-format-security) + add_definitions(-Wno-sign-compare -Wno-nonnull-compare -Wno-return-type -Wno-comment -Wno-ignored-qualifiers -Wno-missing-field-initializers) + #add_definitions(-fprofile-arcs -ftest-coverage) + # 以下只对C++语言有有效 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -Wno-reorder") + + # GCC 4.3.4不支持 -Wno-conversion-null, 先注释掉 + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-conversion-null") + + add_definitions(-rdynamic) + endif(UNIX) + + # 2020/05/06 通用编译选项 + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector -std=c11 -D _GNU_SOURCE -fprofile-arcs") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector -std=c++11 -D _GNU_SOURCE -fprofile-arcs") + SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,-z,noexecstack -Wl,-z,relro ${CMAKE_SHARED_LINKER_FLAGS}") + SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb") + endif() + + + +endmacro() + +function(output_conan_include_dirs) + get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) + foreach(dir ${dirs}) + message(STATUS "dir='${dir}'") + endforeach() +endfunction() diff --git a/cli/test/dt/srclib/CMakeLists.txt b/cli/test/dt/srclib/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c361d8d0e1e5360f2ae28439843d175448b873e6 --- /dev/null +++ b/cli/test/dt/srclib/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 2.6) + +project(demolib) + +# 代码覆盖率 +# coverage option +OPTION (ENABLE_COVERAGE "Use gcov" ON) +MESSAGE(STATUS ENABLE_COVERAGE=${ENABLE_COVERAGE}) +IF(ENABLE_COVERAGE) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") +ENDIF() +#add_definitions(-Dgtest -lgcov) +add_definitions(-Dgtest) +#set(UT_DEMO_SRCS ../../../src/main.c) +aux_source_directory(../../../src UT_DEMO_SRCS) +include_directories("../Depend/libboundscheck/include") + +add_library(${PROJECT_NAME} ${UT_DEMO_SRCS}) +target_link_libraries(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/liblibboundscheck.a) diff --git a/cli/test/dt/testcase/gtest_mytest.cpp b/cli/test/dt/testcase/gtest_mytest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cee96e853699901d8fcbad9ac19192cefd5c3b4e --- /dev/null +++ b/cli/test/dt/testcase/gtest_mytest.cpp @@ -0,0 +1,1189 @@ +/* + * Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. + * 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. + */ +#include +#include +#include +#include +#include +#include "securec.h" +#include "gtest/gtest.h" +#include "mockcpp/mockcpp.hpp" + +using namespace std; +using namespace testing; + +#ifndef GOOGLE_TEST +# define STATIC static +#else +# define STATIC +#endif + +#define DAVINCI_MANAGER_PATH "/dev/davinci_manager" +#define DEVICE_NAME "davinci" +#define BUF_SIZE 1024 +#define MAX_DEVICE_NR 1024 +#define MAX_MOUNT_NR 512 +typedef char *(*ParseFileLine)(char *, const char *); +extern "C" int IsStrEqual(const char *s1, const char *s2); +extern "C" int GetNsPath(const int pid, const char *nsType, char *buf, size_t bufSize); +extern "C" int snprintf_s(char *strDest, size_t destMax, size_t count, const char *format, ...); +extern "C" int open(const char *path, int flags); +extern "C" int close(int fd); +extern "C" int stat(const char *file_name, struct stat *buf); +extern "C" int mount(const char *source, const char *target, + const char *filesystemtype, unsigned long mountflags, const void *data); +extern "C" int Mount(const char *src, const char *dst); +STATIC int MkDir(const char *dir, mode_t mode); +extern "C" int rmdir(const char *pathname); +extern "C" int EnterNsByFd(int fd, int nsType); +extern "C" bool StrHasPrefix(const char *str, const char *prefix); +extern "C" int GetNsPath(const int pid, const char *nsType, char *buf, size_t bufSize); +extern "C" int GetSelfNsPath(const char *nsType, char *buf, size_t bufSize); +extern "C" int EnterNsByPath(const char *path, int nsType); +extern "C" int MountDevice(const char *rootfs, const char *deviceName); +extern "C" int DoDeviceMounting(const char *rootfs, const char *device_name, const size_t ids[], size_t idsNr); +extern "C" int CheckDirExists(char *dir, int len); +extern "C" int GetParentPathStr(const char *path, char *parent, size_t bufSize); +extern "C" int MakeDirWithParent(const char *path, mode_t mode); +extern "C" int MountDir(const char *rootfs, const char *file, unsigned long reMountRwFlag); +extern "C" int DoCtrlDeviceMounting(const char *rootfs); +extern "C" char *GetCgroupMount(char *line, const char *subsys); +extern "C" char *GetCgroupRoot(char *line, const char *subSystem); +extern "C" int ParseFileByLine(char* buffer, int bufferSize, ParseFileLine fn, const char* filepath); +extern "C" int SetupDeviceCgroup(FILE *cgroupAllow, const char *devPath); +extern "C" int SetupDriverCgroup(FILE *cgroupAllow); +extern "C" int GetCgroupPath(int pid, char *effPath, const size_t maxSize); +extern "C" int SetupCgroup(const struct ParsedConfig *config); +extern "C" int SetupContainer(struct CmdArgs *args); +extern "C" int Process(int argc, char **argv); +extern "C" int DoFileMounting(const char *rootfs, const struct MountList *list); +extern "C" int DoMounting(const struct ParsedConfig *config); +extern "C" int DoDirectoryMounting(const char *rootfs, const struct MountList *list); +extern "C" int DoPrepare(const struct CmdArgs *args, struct ParsedConfig *config); +extern "C" int ParseRuntimeOptions(const char *options); +extern "C" bool IsOptionNoDrvSet(); +extern "C" bool IsVirtual(); +extern "C" int MakeMountPoints(const char *path, mode_t mode); +extern "C" int LogLoop(const char* filename); +extern "C" bool TakeNthWord(char **pLine, unsigned int n, char **word); +extern "C" bool CheckRootDir(char **pLine); + +struct MountList { + unsigned int count; + char list[MAX_MOUNT_NR][PATH_MAX]; +}; + +struct CmdArgs { + char devices[BUF_SIZE]; + char rootfs[BUF_SIZE]; + int pid; + char options[BUF_SIZE]; + struct MountList files; + struct MountList dirs; +}; + +struct ParsedConfig { + char rootfs[BUF_SIZE]; + unsigned int devices[MAX_DEVICE_NR]; + size_t devicesNr; + char containerNsPath[BUF_SIZE]; + char cgroupPath[BUF_SIZE]; + int originNsFd; + const struct MountList *files; + const struct MountList *dirs; +}; + +int stub_setns(int fd, int nstype) +{ + return 0; +} + +int Stub_GetNsPath_Failed(const int pid, const char *nsType, char *buf, size_t bufSize) +{ + return -1; +} + +int Stub_GetSelfNsPath_Failed(const char *nsType, char *buf, size_t bufSize) +{ + return -1; +} + +int Stub_EnterNsByFd_Success(int fd, int nsType) +{ + return 0; +} + +int Stub_EnterNsByFd_Failed(int fd, int nsType) +{ + return -1; +} + +int stub_open_success(const char *path, int flags) +{ + return 0; +} + +int stub_open_failed(const char *path, int flags) +{ + return -1; +} + +int stub_close_success(int fd) +{ + return 0; +} + +int stub_MkDir_success(const char *dir, int mode) +{ + return 0; +} + +int stub_MkDir_failed(const char *dir, int mode) +{ + return -1; +} + +int stub_mount_success(const char *source, const char *target, + const char *filesystemtype, unsigned long mountflags, const void *data) +{ + return 0; +} + +int stub_Mount_success(const char *src, const char *dst) +{ + return 0; +} + +int stub_Mount_failed(const char *src, const char *dst) +{ + return -1; +} + +int stub_mount_failed(const char *source, const char *target, + const char *filesystemtype, unsigned long mountflags, const void *data) +{ + return -1; +} + +int stub_stat_success(const char *file_name, struct stat *buf) +{ + return 0; +} + +int stub_stat_failed(const char *file_name, struct stat *buf) +{ + return -1; +} + +int Stub_MountDevice_Success(const char *rootfs, const char *deviceName) +{ + return 0; +} + +int Stub_MountDevice_Failed(const char *rootfs, const char *deviceName) +{ + return -1; +} + +int Stub_MountDir_Success(const char *rootfs, const char *file, unsigned long reMountRwFlag) +{ + return 0; +} + +int Stub_MountDir_Failed(const char *rootfs, const char *file, unsigned long reMountRwFlag) +{ + return -1; +} + +int Stub_CheckDirExists_Success(char *dir, int len) +{ + return 0; +} + +int Stub_MakeDirWithParent_Success(const char *path, mode_t mode) +{ + return 0; +} + +int Stub_MakeDirWithParent_Failed(const char *path, mode_t mode) +{ + return -1; +} + +int Stub_CheckDirExists_Failed(char *dir, int len) +{ + return -1; +} + +int Stub_EnterNsByPath_Success(const char *path, int nsType) +{ + return 0; +} + +int Stub_EnterNsByPath_Failed(const char *path, int nsType) +{ + return 0; +} + +int Stub_DoDeviceMounting_Success(const char *rootfs, const char *device_name, const size_t ids[], size_t idsNr) +{ + return 0; +} + +int Stub_DoDeviceMounting_Failed(const char *rootfs, const char *device_name, const size_t ids[], size_t idsNr) +{ + return -1; +} + +int Stub_DoCtrlDeviceMounting_Success(const char *rootfs) +{ + return 0; +} + +int Stub_DoCtrlDeviceMounting_Failed(const char *rootfs) +{ + return -1; +} + +int Stub_DoDirectoryMounting_Success(const char *rootfs, const struct MountList *list) +{ + return 0; +} + +int Stub_DoDirectoryMounting_Failed(const char *rootfs, const struct MountList *list) +{ + return -1; +} + +int Stub_DoFileMounting_Success(const char *rootfs, const struct MountList *list) +{ + return 0; +} + +int Stub_DoFileMounting_Failed(const char *rootfs, const struct MountList *list) +{ + return -1; +} + +int Stub_DoMounting_Success(const struct ParsedConfig *config) +{ + return 0; +} + +int Stub_DoMounting_Failed(const struct ParsedConfig *config) +{ + return -1; +} + +int Stub_SetupCgroup_Success(const struct ParsedConfig *config) +{ + return 0; +} + +int Stub_SetupCgroup_Failed(const struct ParsedConfig *config) +{ + return 0; +} + +int Stub_SetupContainer_Success(struct CmdArgs *args) +{ + return 0; +} + +int Stub_SetupDeviceCgroup_Success(FILE *cgroupAllow, const char *devPath) +{ + return 0; +} + +int Stub_SetupDeviceCgroup_Failed(FILE *cgroupAllow, const char *devPath) +{ + return -1; +} + +int Stub_SetupDriverCgroup_Fail(FILE *cgroupAllow) +{ + return -1; +} + +int Stub_SetupDriverCgroup_Success(FILE *cgroupAllow) +{ + return 0; +} + +int Stub_DoPrepare_Failed(const struct CmdArgs *args, struct ParsedConfig *config) +{ + return -1; +} + +int Stub_DoPrepare_Success(const struct CmdArgs *args, struct ParsedConfig *config) +{ + return 0; +} + +int Stub_ParseFileByLine_Success(char* buffer, int bufferSize, ParseFileLine fn, const char* filepath) +{ + return 0; +} + +int Stub_GetCgroupPath_Success(int pid, char *effPath, const size_t maxSize) +{ + return 0; +} + +bool Stub_IsOptionNoDrvSet_True() +{ + return true; +} + +bool Stub_IsOptionNoDrvSet_False() +{ + return false; +} + +class Test_Fhho : public Test { +protected: + static void SetUpTestCase() + { + cout << "TestSuite测试套事件:在第一个testcase之前执行" << endl; + } + static void TearDownTestCase() + { + cout << "TestSuite测试套事件:在最后一个testcase之后执行" << endl; + } + //如果想在相同的测试套中设置两种事件,那么可以写在一起,运行就看到效果了 + virtual void SetUp() + { + cout << "TestSuite测试用例事件:在每个testcase之前执行" << endl; + } + virtual void TearDown() + { + cout << "TestSuite测试用例事件:在每个testcase之后执行" << endl; + } +}; + +TEST_F(Test_Fhho, ClassEQ) +{ + int pid = 1; + const char* nsType = "mnt"; + char buf[100] = {0x0}; + int bufSize = 100; + int ret = GetNsPath(pid, nsType, buf, 100); + EXPECT_LE(0, ret); +} + +TEST_F(Test_Fhho, StatusOne) +{ + int pid = 1; + int nsType = 1; + MOCKER(setns) + .stubs() + .will(invoke(stub_setns)); + int ret = EnterNsByFd(pid, nsType); + GlobalMockObject::verify(); + EXPECT_LE(0, ret); +} + +TEST_F(Test_Fhho, StatusTwo) +{ + // The test does not have a file handle into the namespace + int pid = 1; + int nsType = 1; + int ret = EnterNsByFd(pid, nsType); + EXPECT_LE(-1, ret); +} + +TEST_F(Test_Fhho, StatusOne1) +{ + char containerNsPath[BUF_SIZE] = {0}; + int nsType = 1; + MOCKER(open).stubs().will(invoke(stub_open_success)); + int ret = EnterNsByPath(containerNsPath, nsType); + GlobalMockObject::verify(); + EXPECT_LE(-1, ret); +} + +TEST_F(Test_Fhho, StatusTwo1) +{ + // The test has no path into the namespace + char containerNsPath[BUF_SIZE] = {0}; + int nsType = 1; + int ret = EnterNsByPath(containerNsPath, nsType); + EXPECT_LE(-1, ret); +} + +TEST_F(Test_Fhho, GetNsPathAndGetSelfNsPath) +{ + char containerNsPath[BUF_SIZE] = {0}; + int containerPid = 1; + EXPECT_LE(0, GetNsPath(containerPid, "mnt", containerNsPath, BUF_SIZE)); + char nsPath[BUF_SIZE] = {0}; + EXPECT_LE(0, GetSelfNsPath("mnt", nsPath, BUF_SIZE)); +} + +TEST_F(Test_Fhho, StatusOne2) +{ + char *rootfs="/home"; + MOCKER(Mount).stubs().will(invoke(stub_Mount_failed)); + char *deviceName="davinci100"; + EXPECT_EQ(-1, MountDevice(rootfs, deviceName)); + GlobalMockObject::verify(); +} + +TEST_F(Test_Fhho, StatusTwo2) +{ + // Test root file system does not exist mount + // Assign a false file path + char *rootfs="/home/notexists"; + char *deviceName="davinci0"; + EXPECT_EQ(-1, MountDevice(rootfs, deviceName)); +} + +TEST_F(Test_Fhho, StatusThree1) +{ + char *rootfs="/home"; + MOCKER(stat).stubs().will(invoke(stub_stat_success)); + MOCKER(close).stubs().will(invoke(stub_close_success)); + MOCKER(open).stubs().will(invoke(stub_open_success)); + MOCKER(mount).stubs().will(invoke(stub_Mount_failed)); + char *deviceName="davinci0"; + GlobalMockObject::verify(); + EXPECT_EQ(-1, MountDevice(rootfs, deviceName)); +} + +TEST_F(Test_Fhho, StatusFour) +{ + char *rootfs="/home"; + MOCKER(mount).stubs().will(invoke(stub_mount_success)); + MOCKER(stat).stubs().will(invoke(stub_stat_failed)); + char *deviceName="davinci0"; + GlobalMockObject::verify(); + EXPECT_EQ(-1, MountDevice(rootfs, deviceName)); +} + +TEST_F(Test_Fhho, StatusOneDoDeviceMounting) +{ + MOCKER(MountDevice).stubs().will(invoke(Stub_MountDevice_Success)); + char *rootfs = "/home"; + size_t devicesList[2] = {1, 2}; + size_t idNr = 2; + char *device_name = "davinci"; + int ret = DoDeviceMounting(rootfs, device_name, devicesList, idNr); + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusTwoDoDeviceMounting) +{ + MOCKER(MountDevice).stubs().will(invoke(Stub_MountDevice_Failed)); + char *rootfs = "/home"; + size_t devicesList[2] = {1, 2}; + size_t idNr = 2; + char *device_name = "davinci"; + int ret = DoDeviceMounting(rootfs, device_name, devicesList, idNr); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusOneDoDirectoryMounting) +{ + MOCKER(MountDir).stubs().will(invoke(Stub_MountDir_Failed)); + struct MountList list = {0}; + list.count = 1; + char *rootfs = "/home"; + int ret = DoDirectoryMounting(rootfs, &list); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusTwoDoDirectoryMounting) +{ + MOCKER(MountDir).stubs().will(invoke(Stub_MountDir_Success)); + struct MountList list = {0}; + list.count = 3; + char *rootfs = "/home"; + int ret = DoDirectoryMounting(rootfs, &list); + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusOneDoMounting) +{ + MOCKER(DoDeviceMounting).stubs().will(invoke(Stub_DoDeviceMounting_Failed)); + struct ParsedConfig config; + (void)strcpy_s(config.rootfs, sizeof(config.rootfs), "/home"); + config.devices[0] = 1; + config.devices[1] = 2; + config.devicesNr = 2; + int ret = DoMounting(&config); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusTwoDoMounting) +{ + MOCKER(DoDeviceMounting).stubs().will(invoke(Stub_DoDeviceMounting_Success)); + MOCKER(DoCtrlDeviceMounting).stubs().will(invoke(Stub_DoCtrlDeviceMounting_Failed)); + struct ParsedConfig config; + (void)strcpy_s(config.rootfs, sizeof(config.rootfs), "/home"); + config.devices[0] = 1; + config.devices[1] = 2; + config.devicesNr = 2; + int ret = DoMounting(&config); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusTwoDoMountingNULL) +{ + MOCKER(DoDeviceMounting).stubs().will(invoke(Stub_DoDeviceMounting_Success)); + MOCKER(DoCtrlDeviceMounting).stubs().will(invoke(Stub_DoCtrlDeviceMounting_Failed)); + struct ParsedConfig *config = NULL; + int ret = DoMounting(config); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + + +TEST_F(Test_Fhho, StatusThreeDoMounting) +{ + MOCKER(DoDeviceMounting).stubs().will(invoke(Stub_DoDeviceMounting_Success)); + MOCKER(DoCtrlDeviceMounting).stubs().will(invoke(Stub_DoCtrlDeviceMounting_Success)); + MOCKER(DoFileMounting).stubs().will(invoke(Stub_DoFileMounting_Success)); + MOCKER(DoDirectoryMounting).stubs().will(invoke(Stub_DoDirectoryMounting_Failed)); + struct ParsedConfig config; + (void)strcpy_s(config.rootfs, sizeof(config.rootfs), "/home"); + config.devices[0] = 1; + config.devices[1] = 2; + config.devicesNr = 2; + int ret = DoMounting(&config); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusFourDoMounting) +{ + MOCKER(DoDeviceMounting).stubs().will(invoke(Stub_DoDeviceMounting_Success)); + MOCKER(DoCtrlDeviceMounting).stubs().will(invoke(Stub_DoCtrlDeviceMounting_Success)); + MOCKER(IsOptionNoDrvSet).stubs().will(invoke(Stub_IsOptionNoDrvSet_False)); + MOCKER(DoFileMounting).stubs().will(invoke(Stub_DoFileMounting_Success)); + MOCKER(DoDirectoryMounting).stubs().will(invoke(Stub_DoDirectoryMounting_Success)); + struct ParsedConfig config; + (void)strcpy_s(config.rootfs, sizeof(config.rootfs), "/home"); + config.devices[0] = 1; + config.devices[1] = 2; + config.devicesNr = 2; + int ret = DoMounting(&config); + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + + +TEST_F(Test_Fhho, StatusOneCheckDirExists) +{ + // Test directory exists + char *dir = "/home"; + int len = strlen(dir); + int ret = CheckDirExists(dir, len); + EXPECT_EQ(0, ret); +} + + +TEST_F(Test_Fhho, StatusTwoCheckDirExists) +{ + // Test directory does not exist + char *dir = "/home/notexist"; + int len = strlen(dir); + int ret = CheckDirExists(dir, len); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusOneCheckDirExists1) +{ + // Test get path parent directory + char *path = "/usr/bin"; + char parent[BUF_SIZE] = {0}; + int ret = GetParentPathStr(path, parent, BUF_SIZE); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusOneCheckDirExists11) +{ + // Test get path parent directory + char *path = nullptr; + char parent[BUF_SIZE] = {0}; + int ret = GetParentPathStr(path, parent, BUF_SIZE); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusOneMakeDirWithParent) +{ + // The test create directory contains the parent directory + mode_t mode = 0755; + char parentDir[BUF_SIZE] = {0}; + int ret = MakeDirWithParent(parentDir, mode); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, MakeMountPoints1) +{ + // The test create directory contains the parent directory + mode_t mode = 0755; + char *path = "/home"; + int ret = MakeMountPoints(path, mode); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, LogLoopSuccess) +{ + // The test create directory contains the parent directory + char* filename = "/home/var/log/sys.log"; + int ret = LogLoop(filename); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusTwoMakeDirWithParent) +{ + mode_t mode = 0755; + char parentDir[BUF_SIZE] = {0}; + MOCKER(CheckDirExists).stubs().will(invoke(Stub_CheckDirExists_Success)); + int ret = MakeDirWithParent(parentDir, mode); + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + +#ifdef GOOGLE_TEST +TEST_F(Test_Fhho, MkDirtestsuccess) +{ + // The test create directory contains the parent directory + mode_t mode = 0755; + char *dir = "/home"; + int ret = MkDir(dir, mode); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusThreeMakeDirWithParent) +{ + char *pathData = "/path/abc/abcd"; + mode_t mode = 0755; + char *path = NULL; + path = strdup(pathData); + MOCKER(CheckDirExists).stubs().will(invoke(Stub_CheckDirExists_Failed)); + MOCKER(MkDir).stubs().will(invoke(stub_MkDir_success)); + int ret = MakeDirWithParent(path, mode); + ret = MakeDirWithParent(path, mode); + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusThreeMountDir) +{ + MOCKER(CheckDirExists).stubs().will(invoke(Stub_CheckDirExists_Failed)); + MOCKER(MkDir).stubs().will(invoke(stub_MkDir_failed)); + char *rootfs = "/rootfs"; + unsigned long reMountRwFlag = MS_BIND | MS_REMOUNT | MS_RDONLY | MS_NOSUID | MS_NOEXEC; + int ret = MountDir(rootfs, "/home", reMountRwFlag); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} +#endif + +TEST_F(Test_Fhho, StatusOneMountDir) +{ + MOCKER(stat).stubs().will(invoke(stub_stat_failed)); + char *rootfs = "/dev"; + unsigned long reMountRwFlag = MS_BIND | MS_REMOUNT | MS_RDONLY | MS_NOSUID | MS_NOEXEC; + int ret = MountDir(rootfs, DAVINCI_MANAGER_PATH, reMountRwFlag); + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusTwoMountDir) +{ + MOCKER(CheckDirExists).stubs().will(invoke(Stub_CheckDirExists_Failed)); + MOCKER(MakeDirWithParent).stubs().will(invoke(Stub_MakeDirWithParent_Failed)); + char *rootfs = "/rootfs"; + unsigned long reMountRwFlag = MS_BIND | MS_REMOUNT | MS_RDONLY | MS_NOSUID | MS_NOEXEC; + int ret = MountDir(rootfs, "/home", reMountRwFlag); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusFourMountDir) +{ + MOCKER(CheckDirExists).stubs().will(invoke(Stub_CheckDirExists_Failed)); + MOCKER(MakeDirWithParent).stubs().will(invoke(Stub_MakeDirWithParent_Success)); + MOCKER(mount).stubs().will(invoke(stub_mount_failed)); + char *rootfs = "/rootfs"; + unsigned long reMountRwFlag = MS_BIND | MS_REMOUNT | MS_RDONLY | MS_NOSUID | MS_NOEXEC; + int ret = MountDir(rootfs, "/home", reMountRwFlag); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusFiveMountDir) +{ + MOCKER(stat).stubs().will(invoke(stub_stat_failed)); + MOCKER(MakeDirWithParent).stubs().will(invoke(Stub_MakeDirWithParent_Success)); + MOCKER(Mount).stubs().will(invoke(stub_Mount_success)); + char *rootfs = "/rootfs"; + unsigned long reMountRwFlag = MS_BIND | MS_REMOUNT | MS_RDONLY | MS_NOSUID | MS_NOEXEC; + int ret = MountDir(rootfs, "/dev/random", reMountRwFlag); + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusOneDoCtrlDeviceMounting) +{ + char *rootfs = "/home"; + MOCKER(MountDir).stubs().will(invoke(Stub_MountDir_Failed)); + int ret = DoCtrlDeviceMounting(rootfs); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusTwoDoCtrlDeviceMounting) +{ + MOCKER(MountDevice).stubs().will(invoke(Stub_MountDevice_Success)); + char *rootfs = "/home"; + int ret = DoCtrlDeviceMounting(rootfs); + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusOneGetCgroupMount) +{ + char *lineData = "406 403 0:27 /docker/ba186404524744c189c6a03d2b66288a963a562a79b11005ae935104fc8c47b2 /sys/fs/cgroup/devices ro,nosuid,nodev,noexec,relatime master:15 - cgroup cgroup rw,devices"; + char *line = NULL; + line = strdup(lineData); + char *subsys = "devices"; + char *expectRes = "/sys/fs/cgroup/devices"; + char *actualRes = GetCgroupMount(line, subsys); + EXPECT_EQ(0, strcmp(actualRes, expectRes)); +} + +TEST_F(Test_Fhho, StatusOneGetCgroupMount1) +{ + char *lineData = "3:devices:/docker/ba186404524744c189c6a03d2b66288a963a562a79b11005ae935104fc8c47b2"; + char *line = NULL; + line = strdup(lineData); + char *subsys = "devices"; + char *expectRes = "/docker/ba186404524744c189c6a03d2b66288a963a562a79b11005ae935104fc8c47b2"; + char *actualRes = GetCgroupRoot(line, subsys); + EXPECT_EQ(0, strcmp(actualRes, expectRes)); +} + +TEST_F(Test_Fhho, StatusOneParseFileByLine) +{ + // Test parse file content does not exist + char *mountPath= "/not_exist_dir/mountinfo.txt"; + char mount[BUF_SIZE] = {0x0}; + int ret = ParseFileByLine(mount, BUF_SIZE, GetCgroupMount, mountPath); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusOneSetupDeviceCgroup) +{ + char *cgroupPathData = "devices.allow"; + char *cgroupPath = NULL; + cgroupPath = strdup(cgroupPathData); + FILE *cgroupAllow = NULL; + cgroupAllow = fopen(cgroupPath, "a"); + MOCKER(stat).stubs().will(invoke(stub_stat_failed)); + int ret = SetupDeviceCgroup(cgroupAllow, cgroupPath); + if (cgroupAllow != NULL) { + (void)fclose(cgroupAllow); + } + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusTwoSetupDeviceCgroup) +{ + char *cgroupPathData = "/not_exist_dir/devices1.allow"; + char *cgroupPath = NULL; + cgroupPath = strdup(cgroupPathData); + FILE *cgroupAllow = NULL; + cgroupAllow = fopen(cgroupPath, "a"); + MOCKER(stat).stubs().will(invoke(stub_stat_success)); + int ret = SetupDeviceCgroup(cgroupAllow, cgroupPath); + if (cgroupAllow != NULL) { + (void)fclose(cgroupAllow); + } + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusOneSetupDriverCgroup) +{ + char *cgroupPath = "devices.allow"; + FILE *cgroupAllow = NULL; + cgroupAllow = fopen(cgroupPath, "a"); + MOCKER(SetupDeviceCgroup).stubs().will(invoke(Stub_SetupDeviceCgroup_Success)); + int ret = SetupDriverCgroup(cgroupAllow); + if (cgroupAllow != NULL) { + (void)fclose(cgroupAllow); + } + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusTwoSetupDriverCgroup) +{ + char *cgroupPath = "devices1.allow"; + FILE *cgroupAllow = NULL; + cgroupAllow = fopen(cgroupPath, "a"); + MOCKER(SetupDeviceCgroup).stubs().will(invoke(Stub_SetupDeviceCgroup_Failed)); + int ret = SetupDriverCgroup(cgroupAllow); + if (cgroupAllow != NULL) { + (void)fclose(cgroupAllow); + } + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusOneSetupDriverCgroup1) +{ + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + + MOCKER(ParseFileByLine).stubs().will(invoke(Stub_ParseFileByLine_Success)); + + char cgroupPath[BUF_SIZE] = {0}; + int ret = GetCgroupPath(args.pid, cgroupPath, BUF_SIZE); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusOneSetupDriverCgroup2) +{ + struct ParsedConfig config; + (void)strcpy_s(config.rootfs, sizeof(config.rootfs), "/home"); + config.devices[0] = 1; + config.devices[1] = 2; + config.devicesNr = 2; + (void)strcpy_s(config.cgroupPath, sizeof(config.cgroupPath), "/not_exist_dir/cgroup_path"); + int ret = SetupCgroup(&config); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusTwoSetupCgroup) +{ + struct ParsedConfig config; + (void)strcpy_s(config.rootfs, sizeof(config.rootfs), "/home"); + config.devices[0] = 1; + config.devices[1] = 2; + config.devicesNr = 2; + (void)strcpy_s(config.cgroupPath, sizeof(config.cgroupPath), "devices.allow"); + MOCKER(SetupDriverCgroup).stubs().will(invoke(Stub_SetupDriverCgroup_Fail)); + int ret = SetupCgroup(&config); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusThreeSetupCgroup) +{ + struct ParsedConfig config; + (void)strcpy_s(config.rootfs, sizeof(config.rootfs), "/home"); + config.devices[0] = 1; + config.devices[1] = 2; + config.devicesNr = 2; + (void)strcpy_s(config.cgroupPath, sizeof(config.cgroupPath), "devices.allow"); + MOCKER(SetupDriverCgroup).stubs().will(invoke(Stub_SetupDriverCgroup_Success)); + MOCKER(SetupDeviceCgroup).stubs().will(invoke(Stub_SetupDeviceCgroup_Failed)); + int ret = SetupCgroup(&config); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusOneSetupContainer) +{ + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + MOCKER(DoPrepare).stubs().will(invoke(Stub_DoPrepare_Failed)); + int ret = SetupContainer(&args); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusTwoSetupContainer) +{ + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + MOCKER(DoPrepare).stubs().will(invoke(Stub_DoPrepare_Success)); + MOCKER(EnterNsByPath).stubs().will(invoke(Stub_EnterNsByPath_Failed)); + int ret = SetupContainer(&args); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusThreeSetupContainer) +{ + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + MOCKER(DoPrepare).stubs().will(invoke(Stub_DoPrepare_Success)); + MOCKER(EnterNsByPath).stubs().will(invoke(Stub_EnterNsByPath_Success)); + MOCKER(DoMounting).stubs().will(invoke(Stub_DoMounting_Failed)); + int ret = SetupContainer(&args); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusFourSetupContainer) +{ + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + MOCKER(DoPrepare).stubs().will(invoke(Stub_DoPrepare_Success)); + MOCKER(EnterNsByPath).stubs().will(invoke(Stub_EnterNsByPath_Success)); + MOCKER(DoMounting).stubs().will(invoke(Stub_DoMounting_Success)); + MOCKER(SetupCgroup).stubs().will(invoke(Stub_SetupCgroup_Failed)); + int ret = SetupContainer(&args); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusFiveSetupContainer) +{ + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + MOCKER(DoPrepare).stubs().will(invoke(Stub_DoPrepare_Success)); + MOCKER(EnterNsByPath).stubs().will(invoke(Stub_EnterNsByPath_Success)); + MOCKER(DoMounting).stubs().will(invoke(Stub_DoMounting_Success)); + MOCKER(SetupCgroup).stubs().will(invoke(Stub_SetupCgroup_Success)); + MOCKER(EnterNsByFd).stubs().will(invoke(Stub_EnterNsByFd_Failed)); + int ret = SetupContainer(&args); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusSixSetupContainer) +{ + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + + MOCKER(DoPrepare).stubs().will(invoke(Stub_DoPrepare_Success)); + MOCKER(EnterNsByPath).stubs().will(invoke(Stub_EnterNsByPath_Success)); + MOCKER(DoMounting).stubs().will(invoke(Stub_DoMounting_Success)); + MOCKER(SetupCgroup).stubs().will(invoke(Stub_SetupCgroup_Success)); + MOCKER(EnterNsByFd).stubs().will(invoke(Stub_EnterNsByFd_Success)); + int ret = SetupContainer(&args); + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusOneProcess) +{ + // test parameter is null + int argc = 0; + char **argv = NULL; + int ret = Process(argc, argv); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, TakeNthWord) +{ + // test parameter is null + char **pLine = nullptr; + unsigned int n = 2; + char **word = nullptr; + bool ret = TakeNthWord(pLine, n, word); + EXPECT_EQ(false, ret); +} + + +TEST_F(Test_Fhho, CheckRootDir) +{ + // test parameter is null + char **pLine = nullptr; + bool ret = CheckRootDir(pLine); + EXPECT_EQ(false, ret); +} + +TEST_F(Test_Fhho, StatusTwoProcess) +{ + // Test the correct options + const int argc = 7; + const char *argvData[argc] = {"ascend-docker-cli", "--devices", "1,2", "--pid", "123", "--rootfs", "/home"}; + int ret = Process(argc,const_cast(argvData)); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusThreeProcess) +{ + // Test error options + const int argc = 7; + const char *argvData[argc] = {"ascend-docker-cli", "--evices", "1,2", "--idd", "123", "--ootfs", "/home"}; + int ret = Process(argc, const_cast(argvData)); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusFourProcess) +{ + const int argc = 7; + const char *argvData[argc] = {"ascend-docker-cli", "--evices", "1,2", "--idd", "123", "--ootfs", "/home"}; + MOCKER(SetupContainer).stubs().will(invoke(Stub_SetupContainer_Success)); + int ret = Process(argc,const_cast(argvData)); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusFiveProcess) +{ + const int argc = 13; + const char *argvData[argc] = {"ascend-docker-cli", "--devices", "1,2", "--pid", "123", "--rootfs", + "/home", "--options", "base", "--mount-file", "a.list", "--mount-dir", "/home/code"}; + MOCKER(SetupContainer).stubs().will(invoke(Stub_SetupContainer_Success)); + int ret = Process(argc,const_cast(argvData)); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusSixProcess) +{ + const int argc = 13; + const char *argvData[argc] = {"ascend-docker-cli", "--devices", "1,2", "--pid", "123", "--rootfs", + "/home", "--opt", "base", "--mount-f", "a.list", "--mount-dir", "/root/sxv"}; + MOCKER(SetupContainer).stubs().will(invoke(Stub_SetupContainer_Success)); + int ret = Process(argc,const_cast(argvData)); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusSevenProcess) +{ + const int argc = 13; + const char *argvData[argc] = {"ascend-docker-cli", "--ops", "--devices", "1,2", "--pid", "123", + "--rootfs", "/home", "base", "--mounle", "a.list", "--mount-dir", "/home/code"}; + MOCKER(SetupContainer).stubs().will(invoke(Stub_SetupContainer_Success)); + int ret = Process(argc,const_cast(argvData)); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusOneParseRuntimeOptions) +{ + // Test the right options + const char options[BUF_SIZE] = "1,2"; + // Options is the parameter value of -o + int ret = ParseRuntimeOptions(options); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusOneDoPrepare) +{ + MOCKER(GetCgroupPath).stubs().will(invoke(Stub_GetCgroupPath_Success)); + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + struct ParsedConfig config; + config.devicesNr = 1024; + int ret = DoPrepare(&args, &config); + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusTwoDoPrepare) +{ + MOCKER(GetCgroupPath).stubs().will(invoke(Stub_GetCgroupPath_Success)); + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + struct ParsedConfig config; + config.devicesNr = 1024; + int ret = DoPrepare(&args, &config); + GlobalMockObject::verify(); + EXPECT_EQ(0, ret); +} + +TEST_F(Test_Fhho, StatusThreeDoPrepare) +{ + MOCKER(GetNsPath).stubs().will(invoke(Stub_GetNsPath_Failed)); + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + struct ParsedConfig config; + config.devicesNr = 1024; + int ret = DoPrepare(&args, &config); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusFourDoPrepare) +{ + MOCKER(GetCgroupPath).stubs().will(invoke(Stub_GetCgroupPath_Success)); + MOCKER(GetSelfNsPath).stubs().will(invoke(Stub_GetSelfNsPath_Failed)); + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + struct ParsedConfig config; + config.devicesNr = 1024; + int ret = DoPrepare(&args, &config); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusFiveDoPrepare) +{ + MOCKER(GetCgroupPath).stubs().will(invoke(Stub_GetCgroupPath_Success)); + MOCKER(open).stubs().will(invoke(stub_open_failed)); + struct CmdArgs args; + (void)strcpy_s(args.rootfs, sizeof(args.rootfs), "/home"); + args.devices[0] = '1'; + args.devices[1] = '2'; + args.pid = 1; + struct ParsedConfig config; + config.devicesNr = 1024; + int ret = DoPrepare(&args, &config); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} + +TEST_F(Test_Fhho, StatusSixDoPrepare) +{ + MOCKER(GetCgroupPath).stubs().will(invoke(Stub_GetCgroupPath_Success)); + MOCKER(open).stubs().will(invoke(stub_open_failed)); + + struct ParsedConfig config; + config.devicesNr = 1024; + int ret = DoPrepare(NULL, &config); + GlobalMockObject::verify(); + EXPECT_EQ(-1, ret); +} \ No newline at end of file diff --git a/cli/test/dt/testcase/main.cpp b/cli/test/dt/testcase/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8b6588f883008e3b580a6a35b5b3144100a318f --- /dev/null +++ b/cli/test/dt/testcase/main.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#include +#include +#include "gtest/gtest.h" +#include "mockcpp/mockcpp.hpp" + +using namespace std; +using namespace testing; + +int main(int argc, char* argv[], char* evn[]) +{ + InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + if (1 == ret) { + printf("有用例错误,请按任意键继续。。。"); + } + + return ret; +} diff --git a/cli/test/dt_go/build.sh b/cli/test/dt_go/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..8eb0feefe39bcf323b8188ae8e711d1e7fc97537 --- /dev/null +++ b/cli/test/dt_go/build.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# Copyright(C) Huawei Technologies Co.,Ltd. 2020-2022. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +set -e +umask 077 +CUR_DIR=$(dirname "$(readlink -f $0)") +TOP_DIR=$(realpath "${CUR_DIR}"/../../..) +RUNTIME_DIR=${TOP_DIR}/CODE/mindxcheckutils +export GOPATH="${TOP_DIR}/CODE/opensource" +export PATH="${GOPATH}/bin/;$PATH" +export GO111MODULE=on +export GONOSUMDB="*" + +function execute_test() { + cd ${RUNTIME_DIR} + go mod tidy + go install github.com/axw/gocov/gocov@v1.0.0 + go install github.com/matm/gocov-html@latest + go install gotest.tools/gotestsum@latest + if ! (go test -mod=mod -gcflags=all=-l -v -race -coverprofile cov.out ${RUNTIME_DIR} >./$file_input); then + echo '****** go test cases error! ******' + exit 1 + else + echo ${file_detail_output} + ${GOPATH}/bin/gocov convert cov.out | ${GOPATH}/bin/gocov-html >${file_detail_output} + ${GOPATH}/bin/gotestsum --junitfile "${TOP_DIR}"/test/unit-tests.xml "${RUNTIME_DIR}"/... + fi +} + +file_input='testDockerPlugin.txt' +file_detail_output="${TOP_DIR}/test/api.html" + +echo "************************************* Start LLT Test *************************************" +mkdir -p "${TOP_DIR}"/test/ +cd "${TOP_DIR}"/test/ +if [ -f "$file_detail_output" ]; then + rm -rf $file_detail_output +fi +if [ -f "$file_input" ]; then + rm -rf $file_input +fi +execute_test + +echo "************************************* End LLT Test *************************************" + +exit 0 diff --git a/destroy/src/CMakeLists.txt b/destroy/src/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ad64a495015f8f38b6dbc908539526b6be99a91c --- /dev/null +++ b/destroy/src/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 2.26) +project(ascend-docker-destroy C) +set(CMAKE_C_STANDARD 11) +## The common options using by both c and cxx + +add_compile_options(-fstack-protector-all -D _GNU_SOURCE -Wl,--no-undefined) + +message(STATUS "CMAKE_SHARED_LIBRARY_LINK_C_FLAGS = " ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS}) +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +include_directories("${PROJECT_SOURCE_DIR}/../../platform/libboundscheck/include") +include_directories("${PROJECT_SOURCE_DIR}/../../cli/src") +aux_source_directory(. SRC) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../build/libboundscheck ${CMAKE_CURRENT_SOURCE_DIR}/../../build/libboundscheck) +add_executable(ascend-docker-destroy ../../cli/src/logger.c ../../cli/src/utils.c ../../cli/src/basic.c ${SRC} ) +target_compile_options(ascend-docker-destroy PRIVATE -fstack-protector-all -fpie -ldl -D_FORTIFY_SOURCE=2 -O2) +target_link_libraries(ascend-docker-destroy -ldl -pie -Wl,-s,-z,now libboundscheck) diff --git a/destroy/src/main.c b/destroy/src/main.c new file mode 100644 index 0000000000000000000000000000000000000000..16136f1a5d3d6bcad88fbf1aed130c9f4e77bc85 --- /dev/null +++ b/destroy/src/main.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" +#include "basic.h" +#include "logger.h" +#include "utils.h" + +#define DCMI_INIT "dcmi_init" +#define DCMI_SET_DESTROY_VDEVICE "dcmi_set_destroy_vdevice" +#define ROOT_UID 0 +#define DECIMAL 10 +#define DESTROY_PARAMS_NUM 4 +#define PARAMS_SECOND 1 +#define PARAMS_THIRD 2 +#define PARAMS_FOURTH 3 +#define ID_MAX 65535 + +static bool ShowExceptionInfo(const char* exceptionInfo) +{ + Logger(exceptionInfo, LEVEL_ERROR, SCREEN_YES); + return false; +} + +static bool CheckFileOwner(const struct stat fileStat, const bool checkOwner) +{ + if (checkOwner) { + if ((fileStat.st_uid != ROOT_UID) && (fileStat.st_uid != geteuid())) { // 操作文件owner非root/自己 + return ShowExceptionInfo("Please check the folder owner!"); + } + } + return true; +} + +static bool CheckParentDir(char* buf, const size_t bufLen, struct stat fileStat, const bool checkOwner) +{ + if (buf == NULL) { + return false; + } + for (int iLoop = 0; iLoop < PATH_MAX; iLoop++) { + if (!CheckFileOwner(fileStat, checkOwner)) { + return false; + } + if ((fileStat.st_mode & S_IWOTH) != 0) { // 操作文件对other用户可写 + return ShowExceptionInfo("Please check the write permission!"); + } + if ((strcmp(buf, "/") == 0) || (strstr(buf, "/") == NULL)) { + break; + } + if (strcmp(dirname(buf), ".") == 0) { + break; + } + if (stat(buf, &fileStat) != 0) { + return false; + } + } + return true; +} + +static bool CheckLegality(const char* resolvedPath, const size_t resolvedPathLen, + const unsigned long long maxFileSzieMb, const bool checkOwner) +{ + const unsigned long long maxFileSzieB = maxFileSzieMb * 1024 * 1024; + char buf[PATH_MAX] = {0}; + if (strncpy_s(buf, sizeof(buf), resolvedPath, resolvedPathLen) != EOK) { + return false; + } + struct stat fileStat; + if ((stat(buf, &fileStat) != 0) || + ((S_ISREG(fileStat.st_mode) == 0) && (S_ISDIR(fileStat.st_mode) == 0))) { + return ShowExceptionInfo("resolvedPath does not exist or is not a file!"); + } + if (fileStat.st_size >= maxFileSzieB) { // 文件大小超限 + return ShowExceptionInfo("fileSize out of bounds!"); + } + return CheckParentDir(buf, PATH_MAX, fileStat, checkOwner); +} + +static bool IsAValidChar(const char c) +{ + if (isalnum(c) != 0) { + return true; + } + // ._-/~为合法字符 + if ((c == '.') || (c == '_') || + (c == '-') || (c == '/') || (c == '~')) { + return true; + } + return false; +} + +static bool CheckFileName(const char* filePath, const size_t filePathLen) +{ + int iLoop; + if ((filePathLen > PATH_MAX) || (filePathLen <= 0)) { // 长度越界 + return ShowExceptionInfo("filePathLen out of bounds!"); + } + for (iLoop = 0; iLoop < filePathLen; iLoop++) { + if (!IsAValidChar(filePath[iLoop])) { // 非法字符 + return ShowExceptionInfo("filePath has an illegal character!"); + } + } + return true; +} + +static bool CheckAExternalFile(const char* filePath, const size_t filePathLen, + const size_t maxFileSzieMb, const bool checkOwner) +{ + if (filePath == NULL) { + return false; + } + if (!CheckFileName(filePath, filePathLen)) { + return false; + } + char resolvedPath[PATH_MAX] = {0}; + if (realpath(filePath, resolvedPath) == NULL && errno != ENOENT) { + return ShowExceptionInfo("realpath failed!"); + } + if (strstr(resolvedPath, filePath) == NULL) { // 存在软链接 + return ShowExceptionInfo("filePath has a soft link!"); + } + return CheckLegality(resolvedPath, strlen(resolvedPath), maxFileSzieMb, checkOwner); +} + +static bool DeclareDcmiApiAndCheck(void **handle) +{ + *handle = dlopen("libdcmi.so", RTLD_LAZY); + if (*handle == NULL) { + Logger("dlopen failed.", LEVEL_ERROR, SCREEN_YES); + return false; + } + struct link_map *pLinkMap; + int ret = dlinfo(*handle, RTLD_DI_LINKMAP, &pLinkMap); + if (ret == 0) { + const size_t maxFileSzieMb = 10; // max 10 mb + if (!CheckAExternalFile(pLinkMap->l_name, strlen(pLinkMap->l_name), maxFileSzieMb, true)) { + Logger("check sofile failed.", LEVEL_ERROR, SCREEN_YES); + return false; + } + } else { + Logger("dlinfo sofile failed.", LEVEL_ERROR, SCREEN_YES); + return false; + } + + return true; +} + +static void DcmiDlAbnormalExit(void **handle, const char* errorInfo) +{ + Logger(errorInfo, LEVEL_INFO, SCREEN_YES); + if (*handle != NULL) { + dlclose(*handle); + *handle = NULL; + } +} + +static void DcmiDlclose(void **handle) +{ + if (*handle != NULL) { + dlclose(*handle); + *handle = NULL; + } +} + +static bool CheckLimitId(const int IdValue) +{ + if (IdValue < 0 || IdValue > ID_MAX) { + return false; + } + return true; +} + +static bool GetAndCheckID(const char *argv[], int *cardId, + int *deviceId, int *vDeviceId) +{ + errno = 0; + *cardId = atoi(argv[PARAMS_SECOND]); + if ((errno != 0) || !CheckLimitId(*cardId)) { + return false; + } + *deviceId = atoi(argv[PARAMS_THIRD]); + if ((errno != 0) || !CheckLimitId(*deviceId)) { + return false; + } + *vDeviceId = atoi(argv[PARAMS_FOURTH]); + if ((errno != 0) || !CheckLimitId(*vDeviceId)) { + return false; + } + return true; +} + +static bool DcmiInitProcess(void *handle) +{ + if (handle == NULL) { + return false; + } + int (*dcmi_init)(void) = NULL; + dcmi_init = dlsym(handle, DCMI_INIT); + if (dcmi_init == NULL) { + DcmiDlAbnormalExit(&handle, "DeclareDlApi failed"); + return false; + } + int ret = dcmi_init(); + if (ret != 0) { + Logger("dcmi_init faile.", LEVEL_ERROR, SCREEN_YES); + DcmiDlclose(&handle); + return false; + } + return true; +} + +static bool DcmiDestroyProcess(void *handle, const int cardId, + const int deviceId, const int vDeviceId) +{ + if (handle == NULL) { + return false; + } + int (*dcmi_set_destroy_vdevice)(int, int, int) = NULL; + dcmi_set_destroy_vdevice = dlsym(handle, DCMI_SET_DESTROY_VDEVICE); + if (dcmi_set_destroy_vdevice == NULL) { + DcmiDlAbnormalExit(&handle, "DeclareDlApi failed"); + return false; + } + int ret = dcmi_set_destroy_vdevice(cardId, deviceId, vDeviceId); + if (ret != 0) { + Logger("dcmi_set_destroy_vdevice failed.", LEVEL_ERROR, SCREEN_YES); + DcmiDlclose(&handle); + return false; + } + return true; +} + +static int DestroyEntrance(const char *argv[]) +{ + if (argv == NULL) { + return -1; + } + int cardId = 0; + int deviceId = 0; + int vDeviceId = 0; + char *str = FormatLogMessage("start to destroy v-device %d start...", vDeviceId); + Logger(str, LEVEL_INFO, SCREEN_YES); + free(str); + if (!GetAndCheckID(argv, &cardId, &deviceId, &vDeviceId)) { + return -1; + } + + void *handle = NULL; + if (!DeclareDcmiApiAndCheck(&handle)) { + Logger("Declare dcmi failed.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + if (!DcmiInitProcess(handle)) { + return -1; + } + if (!DcmiDestroyProcess(handle, cardId, deviceId, vDeviceId)) { + return -1; + } + DcmiDlclose(&handle); + char *strEnd = FormatLogMessage("destroy v-device %d successfully", vDeviceId); + Logger(strEnd, LEVEL_INFO, SCREEN_YES); + free(strEnd); + return 0; +} + +static bool EntryCheck(const int argc, const char *argv[]) +{ + if (argc != DESTROY_PARAMS_NUM) { + Logger("destroy params namber error.", LEVEL_ERROR, SCREEN_YES); + return false; + } + for (int iLoop = 1; iLoop < argc; iLoop++) { + for (size_t jLoop = 0; jLoop < strlen(argv[iLoop]); jLoop++) { + if (isdigit(argv[iLoop][jLoop]) == 0) { + return false; + } + } + } + return true; +} + +int main(const int argc, const char *argv[]) +{ + if (!EntryCheck(argc, argv)) { + Logger("destroy params value error.", LEVEL_ERROR, SCREEN_YES); + return -1; + } + + return DestroyEntrance(argv); +} \ No newline at end of file diff --git a/hook/.gitignore b/hook/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..0feb4a11c678bf9380d11e44b2d8c4167c6b81a9 --- /dev/null +++ b/hook/.gitignore @@ -0,0 +1,3 @@ +.vscode +.idea +ascend-docker-hook diff --git a/hook/go.mod b/hook/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..20169799a8f4b7e99690d2459ba281287c575a39 --- /dev/null +++ b/hook/go.mod @@ -0,0 +1,23 @@ +module main + +go 1.17 + +require ( + github.com/opencontainers/runtime-spec v1.0.3-0.20220718201635-a8106e99982b + github.com/prashantv/gostub v1.1.0 + huawei.com/npu-exporter/v3 v3.0.0 + mindxcheckutils v1.0.0 +) + +require ( + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gopherjs/gopherjs v1.17.2 // indirect + github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/smartystreets/assertions v1.13.0 // indirect + golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect +) + +replace ( + huawei.com/npu-exporter/v3 => gitee.com/ascend/ascend-npu-exporter/v3 v3.0.0 + mindxcheckutils => ../mindxcheckutils +) diff --git a/hook/go.sum b/hook/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..90ced845f515421e7547b7923b83f72fda3748e4 --- /dev/null +++ b/hook/go.sum @@ -0,0 +1,22 @@ +gitee.com/ascend/ascend-npu-exporter/v3 v3.0.0 h1:JfB5Kmce3mWEzbtAhybJozGp6+yJH+jU7D6WytEcOzs= +gitee.com/ascend/ascend-npu-exporter/v3 v3.0.0/go.mod h1:78lAYBVM18u8mobeoKqhJP7POvbayTYBk32hnm9IkfQ= +github.com/agiledragon/gomonkey/v2 v2.8.0 h1:u2K2nNGyk0ippzklz1CWalllEB9ptD+DtSXeCX5O000= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +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/opencontainers/runtime-spec v1.0.3-0.20220718201635-a8106e99982b h1:udwtfS44rxYE/ViMLchHQBjfE60GZSB1arY7BFbyxLs= +github.com/opencontainers/runtime-spec v1.0.3-0.20220718201635-a8106e99982b/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs= +github.com/smartystreets/assertions v1.13.0/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/hook/main.go b/hook/main.go new file mode 100644 index 0000000000000000000000000000000000000000..8f302cbd506f9d9736b216971266487c65ef1011 --- /dev/null +++ b/hook/main.go @@ -0,0 +1,477 @@ +/* Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package main +package main + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "log" + "os" + "path" + "path/filepath" + "sort" + "strconv" + "strings" + "syscall" + + "github.com/opencontainers/runtime-spec/specs-go" + + "mindxcheckutils" + + "huawei.com/npu-exporter/v3/common-utils/hwlog" +) + +const ( + loggingPrefix = "ascend-docker-hook" + runLogPath = "/var/log/ascend-docker-runtime/hook-run.log" + operateLogPath = "/var/log/ascend-docker-runtime/hook-operate.log" + ascendVisibleDevices = "ASCEND_VISIBLE_DEVICES" + ascendRuntimeOptions = "ASCEND_RUNTIME_OPTIONS" + ascendRuntimeMounts = "ASCEND_RUNTIME_MOUNTS" + ascendDockerCli = "ascend-docker-cli" + defaultAscendDockerCli = "/usr/local/bin/ascend-docker-cli" + configDir = "/etc/ascend-docker-runtime.d" + baseConfig = "base" + configFileSuffix = "list" + + borderNum = 2 + kvPairSize = 2 + maxCommandLength = 65535 +) + +var ( + containerConfigInputStream = os.Stdin + doExec = syscall.Exec + ascendDockerCliName = ascendDockerCli + defaultAscendDockerCliName = defaultAscendDockerCli +) + +var validRuntimeOptions = [...]string{ + "NODRV", + "VIRTUAL", +} + +type containerConfig struct { + Pid int + Rootfs string + Env []string +} + +func initLogModule(ctx context.Context) error { + const backups = 2 + const logMaxAge = 365 + runLogConfig := hwlog.LogConfig{ + LogFileName: runLogPath, + LogLevel: 0, + MaxBackups: backups, + MaxAge: logMaxAge, + OnlyToFile: true, + FileMaxSize: 2, + } + if err := hwlog.InitRunLogger(&runLogConfig, ctx); err != nil { + fmt.Printf("hwlog init failed, error is %v", err) + return err + } + operateLogConfig := hwlog.LogConfig{ + LogFileName: operateLogPath, + LogLevel: 0, + MaxBackups: backups, + MaxAge: logMaxAge, + OnlyToFile: true, + FileMaxSize: 2, + } + if err := hwlog.InitOperateLogger(&operateLogConfig, ctx); err != nil { + fmt.Printf("hwlog init failed, error is %v", err) + return err + } + return nil +} + +func removeDuplication(devices []int) []int { + list := make([]int, 0, len(devices)) + prev := -1 + + for _, device := range devices { + if device == prev { + continue + } + + list = append(list, device) + prev = device + } + + return list +} + +func parseDevices(visibleDevices string) ([]int, error) { + devices := make([]int, 0) + const maxDevice = 128 + + for _, d := range strings.Split(visibleDevices, ",") { + d = strings.TrimSpace(d) + if strings.Contains(d, "-") { + borders := strings.Split(d, "-") + if len(borders) != borderNum { + return nil, fmt.Errorf("invalid device range: %s", d) + } + + borders[0] = strings.TrimSpace(borders[0]) + borders[1] = strings.TrimSpace(borders[1]) + + left, err := strconv.Atoi(borders[0]) + if err != nil || left < 0 { + return nil, fmt.Errorf("invalid left boarder range parameter: %s", borders[0]) + } + + right, err := strconv.Atoi(borders[1]) + if err != nil || right > maxDevice { + return nil, fmt.Errorf("invalid right boarder range parameter: %s", borders[1]) + } + + if left > right { + return nil, fmt.Errorf("left boarder (%d) should not be larger than the right one(%d)", left, right) + } + + for n := left; n <= right; n++ { + devices = append(devices, n) + } + } else { + n, err := strconv.Atoi(d) + if err != nil { + return nil, fmt.Errorf("invalid single device parameter: %s", d) + } + + devices = append(devices, n) + } + } + + sort.Slice(devices, func(i, j int) bool { return i < j }) + return removeDuplication(devices), nil +} + +func parseMounts(mounts string) []string { + if mounts == "" { + return []string{baseConfig} + } + const maxMountLength = 128 + if len(mounts) > maxMountLength { + return []string{baseConfig} + } + + mountConfigs := make([]string, 0) + for _, m := range strings.Split(mounts, ",") { + m = strings.TrimSpace(m) + m = strings.ToLower(m) + mountConfigs = append(mountConfigs, m) + } + + return mountConfigs +} + +func isRuntimeOptionValid(option string) bool { + for _, validOption := range validRuntimeOptions { + if option == validOption { + return true + } + } + + return false +} + +func parseRuntimeOptions(runtimeOptions string) ([]string, error) { + parsedOptions := make([]string, 0) + + if runtimeOptions == "" { + return parsedOptions, nil + } + const maxLength = 128 + if len(runtimeOptions) > maxLength { + return nil, fmt.Errorf("invalid runtime option") + } + + for _, option := range strings.Split(runtimeOptions, ",") { + option = strings.TrimSpace(option) + if !isRuntimeOptionValid(option) { + return nil, fmt.Errorf("invalid runtime option") + } + + parsedOptions = append(parsedOptions, option) + } + + return parsedOptions, nil +} + +func parseOciSpecFile(file string) (*specs.Spec, error) { + f, err := os.Open(file) + if err != nil { + return nil, fmt.Errorf("failed to open the OCI config file: %s", file) + } + defer f.Close() + + spec := new(specs.Spec) + if err := json.NewDecoder(f).Decode(spec); err != nil { + return nil, fmt.Errorf("failed to parse OCI config file: %s, caused by: %v", file, err) + } + + if spec.Process == nil { + return nil, fmt.Errorf("invalid OCI spec for empty process") + } + + if spec.Root == nil { + return nil, fmt.Errorf("invalid OCI spec for empty root") + } + + return spec, nil +} + +var getContainerConfig = func() (*containerConfig, error) { + state := new(specs.State) + decoder := json.NewDecoder(containerConfigInputStream) + + if err := decoder.Decode(state); err != nil { + return nil, fmt.Errorf("failed to parse the container's state") + } + + configPath := path.Join(state.Bundle, "config.json") + if _, err := mindxcheckutils.RealFileChecker(configPath, true, true, mindxcheckutils.DefaultSize); err != nil { + return nil, err + } + + ociSpec, err := parseOciSpecFile(configPath) + if err != nil { + return nil, fmt.Errorf("failed to parse OCI spec: %v", err) + } + if len(ociSpec.Process.Env) > maxCommandLength { + return nil, fmt.Errorf("too many items in spec file") + } + // when use ctr->containerd. the rootfs in config.json is a relative path + rfs := ociSpec.Root.Path + if !filepath.IsAbs(rfs) { + rfs = path.Join(state.Bundle, ociSpec.Root.Path) + } + + ret := &containerConfig{ + Pid: state.Pid, + Rootfs: rfs, + Env: ociSpec.Process.Env, + } + + return ret, nil +} + +func getValueByKey(data []string, name string) string { + for _, s := range data { + p := strings.SplitN(s, "=", 2) + if len(p) != kvPairSize { + log.Panicln("environment error") + } + + if p[0] == name && len(p) == kvPairSize { + return p[1] + } + } + + return "" +} + +func readMountConfig(dir string, name string) ([]string, []string, error) { + configFileName := fmt.Sprintf("%s.%s", name, configFileSuffix) + baseConfigFilePath, err := filepath.Abs(filepath.Join(dir, configFileName)) + if err != nil { + return nil, nil, fmt.Errorf("failed to assemble base config file path: %v", err) + } + + fileInfo, err := os.Stat(baseConfigFilePath) + if _, err := mindxcheckutils.RealFileChecker(baseConfigFilePath, true, false, + mindxcheckutils.DefaultSize); err != nil { + return nil, nil, err + } + if err != nil { + return nil, nil, fmt.Errorf("cannot stat base configuration file %s : %v", baseConfigFilePath, err) + } + + if !fileInfo.Mode().IsRegular() { + return nil, nil, fmt.Errorf("base configuration file damaged because is not a regular file") + } + + f, err := os.Open(baseConfigFilePath) + if err != nil { + return nil, nil, fmt.Errorf("failed to open base configuration file %s: %v", baseConfigFilePath, err) + } + defer f.Close() + + fileMountList, dirMountList := make([]string, 0), make([]string, 0) + const maxEntryNumber = 128 + entryCount := 0 + scanner := bufio.NewScanner(f) + for scanner.Scan() { + mountPath := scanner.Text() + entryCount = entryCount + 1 + if entryCount > maxEntryNumber { + return nil, nil, fmt.Errorf("mount list too long") + } + absMountPath, err := filepath.Abs(mountPath) + if err != nil { + continue // skipping files/dirs with any problems + } + mountPath = absMountPath + + stat, err := os.Stat(mountPath) + if err != nil { + continue // skipping files/dirs with any problems + } + + if stat.Mode().IsRegular() { + fileMountList = append(fileMountList, mountPath) + } else if stat.Mode().IsDir() { + dirMountList = append(dirMountList, mountPath) + } + } + + return fileMountList, dirMountList, nil +} + +func readConfigsOfDir(dir string, configs []string) ([]string, []string, error) { + fileInfo, err := os.Stat(dir) + if err != nil { + return nil, nil, fmt.Errorf("cannot stat configuration directory %s : %v", dir, err) + } + + if !fileInfo.Mode().IsDir() { + return nil, nil, fmt.Errorf("%s should be a dir for ascend docker runtime, but now it is not", dir) + } + + fileMountList := make([]string, 0) + dirMountList := make([]string, 0) + + for _, config := range configs { + fileList, dirList, err := readMountConfig(dir, config) + if err != nil { + return nil, nil, fmt.Errorf("failed to process config %s: %v", config, err) + } + + fileMountList = append(fileMountList, fileList...) + dirMountList = append(dirMountList, dirList...) + } + + return fileMountList, dirMountList, nil +} + +func getArgs(cliPath string, devices []int, containerConfig *containerConfig, + fileMountList []string, dirMountList []string) []string { + args := append([]string{cliPath}, + "--devices", strings.Trim(strings.Join(strings.Fields(fmt.Sprint(devices)), ","), "[]"), + "--pid", fmt.Sprintf("%d", containerConfig.Pid), "--rootfs", containerConfig.Rootfs) + for _, filePath := range fileMountList { + args = append(args, "--mount-file", filePath) + } + for _, dirPath := range dirMountList { + args = append(args, "--mount-dir", dirPath) + } + return args +} + +func doPrestartHook() error { + containerConfig, err := getContainerConfig() + if err != nil { + return fmt.Errorf("failed to get container config: %v", err) + } + + visibleDevices := getValueByKey(containerConfig.Env, ascendVisibleDevices) + if visibleDevices == "" { + return nil + } + + devices, err := parseDevices(visibleDevices) + if err != nil { + return fmt.Errorf("failed to parse device setting: %v", err) + } + + mountConfigs := parseMounts(getValueByKey(containerConfig.Env, ascendRuntimeMounts)) + + fileMountList, dirMountList, err := readConfigsOfDir(configDir, mountConfigs) + if err != nil { + return fmt.Errorf("failed to read configuration from config directory: %v", err) + } + + parsedOptions, err := parseRuntimeOptions(getValueByKey(containerConfig.Env, ascendRuntimeOptions)) + if err != nil { + return fmt.Errorf("failed to parse runtime options: %v", err) + } + + currentExecPath, err := os.Executable() + if err != nil { + return fmt.Errorf("cannot get the path of ascend-docker-hook: %v", err) + } + + cliPath := path.Join(path.Dir(currentExecPath), ascendDockerCliName) + if _, err = os.Stat(cliPath); err != nil { + return fmt.Errorf("cannot find ascend-docker-cli executable file at %s: %v", cliPath, err) + } + if _, err := mindxcheckutils.RealFileChecker(cliPath, true, false, mindxcheckutils.DefaultSize); err != nil { + return err + } + args := getArgs(cliPath, devices, containerConfig, fileMountList, dirMountList) + if len(parsedOptions) > 0 { + args = append(args, "--options", strings.Join(parsedOptions, ",")) + } + hwlog.OpLog.Infof("ascend docker hook success, will start cli") + if err := mindxcheckutils.ChangeRuntimeLogMode("hook-run-", "hook-operate-"); err != nil { + return err + } + if err := doExec(cliPath, args, os.Environ()); err != nil { + return fmt.Errorf("failed to exec ascend-docker-cli %v: %v", args, err) + } + return nil +} + +func main() { + defer func() { + if err := recover(); err != nil { + log.Fatal(err) + } + }() + log.SetPrefix(loggingPrefix) + + ctx, _ := context.WithCancel(context.Background()) + if err := initLogModule(ctx); err != nil { + log.Fatal(err) + } + logPrefixWords, err := mindxcheckutils.GetLogPrefix() + if err != nil { + log.Fatal(err) + } + defer func() { + if err := mindxcheckutils.ChangeRuntimeLogMode("hook-run-", "hook-operate-"); err != nil { + fmt.Println("defer changeFileMode function failed") + } + }() + hwlog.OpLog.Infof("%v ascend docker hook starting, try to setup container", logPrefixWords) + hwlog.RunLog.Infof("ascend docker hook starting") + if !mindxcheckutils.StringChecker(strings.Join(os.Args, " "), 0, + maxCommandLength, mindxcheckutils.DefaultWhiteList+" ") { + hwlog.RunLog.Errorf("ascend docker hook failed") + hwlog.OpLog.Errorf("%v ascend docker hook failed", logPrefixWords) + log.Fatal("command error") + } + if err := doPrestartHook(); err != nil { + hwlog.RunLog.Errorf("ascend docker hook failed") + hwlog.OpLog.Errorf("%v ascend docker hook failed", logPrefixWords) + log.Fatal(fmt.Errorf("failed in runtime.doProcess ")) + } +} diff --git a/hook/main_test.go b/hook/main_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b58ca14a06ebef14c0119dfd013c17ed7f01654f --- /dev/null +++ b/hook/main_test.go @@ -0,0 +1,257 @@ +/* Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package main +package main + +import ( + "github.com/prashantv/gostub" + "os" + "os/exec" + "reflect" + "testing" +) + +const ( + pidSample = 123 +) + +func TestRemoveDuplication(t *testing.T) { + originList := []int{1, 2, 2, 4, 5, 5, 5, 6, 8, 8} + targetList := []int{1, 2, 4, 5, 6, 8} + resultList := removeDuplication(originList) + + if !reflect.DeepEqual(resultList, targetList) { + t.Fail() + } +} + +func TestDoPrestartHookCase1(t *testing.T) { + if err := doPrestartHook(); err != nil { + t.Log("failed") + } +} + +func TestDoPrestartHookCase2(t *testing.T) { + conCfg := containerConfig{ + Pid: pidSample, + Rootfs: ".", + Env: []string{"ASCEND_VISIBLE_DEVICES=0l-3,5,7"}, + } + stub := gostub.StubFunc(&getContainerConfig, &conCfg, nil) + defer stub.Reset() + if err := doPrestartHook(); err != nil { + t.Log("failed") + } +} + +func TestDoPrestartHookCase3(t *testing.T) { + conCfg := containerConfig{ + Pid: pidSample, + Rootfs: ".", + Env: []string{"ASCEND_VISIBLE_DEVICE=0-3,5,7"}, + } + stub := gostub.StubFunc(&getContainerConfig, &conCfg, nil) + defer stub.Reset() + if err := doPrestartHook(); err != nil { + t.Log("failed") + } +} + +func TestDoPrestartHookCase4(t *testing.T) { + conCfg := containerConfig{ + Pid: pidSample, + Rootfs: ".", + Env: []string{"ASCEND_VISIBLE_DEVICES=0-3,5,7"}, + } + stub := gostub.StubFunc(&getContainerConfig, &conCfg, nil) + defer stub.Reset() + stub.Stub(&ascendDockerCliName, "") + stub.StubFunc(&doExec, nil) + if err := doPrestartHook(); err != nil { + t.Log("failed") + } +} + +func TestDoPrestartHookCase5(t *testing.T) { + defer func() { + if err := recover(); err != nil { + t.Log("exception", err) + } + }() + conCfg := containerConfig{ + Pid: pidSample, + Rootfs: ".", + Env: []string{"ASCEND_VISIBLE_DEVICES=0-3,5,7"}, + } + stub := gostub.StubFunc(&getContainerConfig, &conCfg, nil) + defer stub.Reset() + stub.Stub(&ascendDockerCliName, "clii") + stub.Stub(&defaultAscendDockerCliName, "clii") + stub.StubFunc(&doExec, nil) + if err := doPrestartHook(); err != nil { + t.Log("failed") + } +} + +func TestGetValueByKeyCase1(t *testing.T) { + data := []string{"ASCEND_VISIBLE_DEVICES=0-3,5,7"} + word := "ASCEND_VISIBLE_DEVICES" + expectVal := "0-3,5,7" + actualVal := getValueByKey(data, word) + if actualVal != expectVal { + t.Fail() + } +} + +func TestGetValueByKeyCase2(t *testing.T) { + data := []string{"ASCEND_VISIBLE_DEVICES"} + word := "ASCEND_VISIBLE_DEVICES" + expectVal := "" + defer func() { + if err := recover(); err != nil { + t.Log("exception occur") + } + }() + actualVal := getValueByKey(data, word) + if actualVal != expectVal { + t.Fail() + } +} + +func TestGetValueByKeyCase3(t *testing.T) { + data := []string{"ASCEND_VISIBLE_DEVICES=0-3,5,7"} + word := "ASCEND_VISIBLE_DEVICE" + expectVal := "" + actualVal := getValueByKey(data, word) + if actualVal != expectVal { + t.Fail() + } +} + +func TestParseDevicesCase1(t *testing.T) { + visibleDevices := "0-3,5,7" + expectVal := []int{0, 1, 2, 3, 5, 7} + actualVal, err := parseDevices(visibleDevices) + if err != nil || !reflect.DeepEqual(expectVal, actualVal) { + t.Fail() + } +} + +func TestParseDevicesCase2(t *testing.T) { + visibleDevices := "0-3-4,5,7" + _, err := parseDevices(visibleDevices) + if err != nil { + t.Fail() + } +} + +func TestParseDevicesCase3(t *testing.T) { + visibleDevices := "0l-3,5,7" + _, err := parseDevices(visibleDevices) + if err == nil { + t.Fail() + } +} + +func TestParseDevicesCase4(t *testing.T) { + visibleDevices := "0-3o,5,7" + _, err := parseDevices(visibleDevices) + if err == nil { + t.Fail() + } +} + +func TestParseDevicesCase5(t *testing.T) { + visibleDevices := "4-3,5,7" + _, err := parseDevices(visibleDevices) + if err == nil { + t.Fail() + } +} + +func TestParseDevicesCase6(t *testing.T) { + visibleDevices := "3o,5,7" + _, err := parseDevices(visibleDevices) + if err == nil { + t.Fail() + } +} + +func TestParseDevicesCase7(t *testing.T) { + visibleDevices := "0=3,5,7" + _, err := parseDevices(visibleDevices) + if err == nil { + t.Fail() + } +} + +func TestParseOciSpecFileCase1(t *testing.T) { + file := "file" + _, err := parseOciSpecFile(file) + if err == nil { + t.Fail() + } +} + +func TestParseOciSpecFileCase2(t *testing.T) { + file := "file" + f, err := os.Create(file) + defer os.Remove(file) + defer f.Close() + if err != nil { + t.Log("create file failed") + } + _, err = parseOciSpecFile(file) + if err == nil { + t.Fail() + } +} + +func TestParseOciSpecFileCase3(t *testing.T) { + file := "config.json" + cmd := exec.Command("runc", "spec") + if err := cmd.Run(); err != nil { + t.Log("runc spec failed") + } + defer os.Remove(file) + _, err := parseOciSpecFile(file) + if err != nil { + t.Fail() + } +} + +func TestGetContainerConfig(t *testing.T) { + file := "config.json" + cmd := exec.Command("runc", "spec") + if err := cmd.Run(); err != nil { + t.Log("runc spec failed") + } + defer func() { + if err := recover(); err != nil { + t.Log("exception", err) + } + }() + defer os.Remove(file) + stateFile, err := os.Open("config.json") + if err != nil { + t.Log("open file failed") + } + defer stateFile.Close() + + stub := gostub.Stub(&containerConfigInputStream, stateFile) + defer stub.Reset() + + getContainerConfig() +} diff --git a/install/deb/src/go.mod b/install/deb/src/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..0f60a58a087e77942365de142df8559b401b74ab --- /dev/null +++ b/install/deb/src/go.mod @@ -0,0 +1,21 @@ +module main + +go 1.17 + +require ( + huawei.com/npu-exporter/v3 v3.0.0 + mindxcheckutils v1.0.0 +) + +require ( + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gopherjs/gopherjs v1.17.2 // indirect + github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/smartystreets/assertions v1.13.0 // indirect + golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect +) + +replace ( + huawei.com/npu-exporter/v3 => gitee.com/ascend/ascend-npu-exporter/v3 v3.0.0 + mindxcheckutils => ../../../mindxcheckutils +) diff --git a/install/deb/src/go.sum b/install/deb/src/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..91dc4f91b07dc6ad564c9f0b40c86572d8c9eece --- /dev/null +++ b/install/deb/src/go.sum @@ -0,0 +1,665 @@ +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= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gitee.com/ascend/ascend-npu-exporter/v3 v3.0.0 h1:JfB5Kmce3mWEzbtAhybJozGp6+yJH+jU7D6WytEcOzs= +gitee.com/ascend/ascend-npu-exporter/v3 v3.0.0/go.mod h1:78lAYBVM18u8mobeoKqhJP7POvbayTYBk32hnm9IkfQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/agiledragon/gomonkey/v2 v2.8.0 h1:u2K2nNGyk0ippzklz1CWalllEB9ptD+DtSXeCX5O000= +github.com/agiledragon/gomonkey/v2 v2.8.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs= +github.com/smartystreets/assertions v1.13.0/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8= +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/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +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/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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-20190213061140-3a22650c66bd/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/cri-api v0.19.4/go.mod h1:UN/iU9Ua0iYdDREBXNE9vqCJ7MIh/FW3VIL0d8pw7Fw= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/install/deb/src/main.go b/install/deb/src/main.go new file mode 100644 index 0000000000000000000000000000000000000000..5202f759706a5e5118bb1ab598b86d46ab425cfc --- /dev/null +++ b/install/deb/src/main.go @@ -0,0 +1,266 @@ +/* Copyright(C) 2021. Huawei Technologies Co.,Ltd. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package main +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + "huawei.com/npu-exporter/v3/common-utils/hwlog" + + "mindxcheckutils" +) + +const template = `{ + "runtimes": { + "ascend": { + "path": "%s", + "runtimeArgs": [] + } + }, + "default-runtime": "ascend" +}` + +const ( + actionPosition = 0 + srcFilePosition = 1 + destFilePosition = 2 + runtimeFilePosition = 3 + rmCommandLength = 3 + addCommandLength = 4 + addCommand = "add" + maxCommandLength = 65535 + logPath = "/var/log/ascend-docker-runtime/installer.log" + rmCommand = "rm" + maxFileSize = 1024 * 1024 * 10 +) + +func main() { + ctx, _ := context.WithCancel(context.Background()) + if err := initLogModule(ctx); err != nil { + log.Fatal(err) + } + logPrefixWords, err := mindxcheckutils.GetLogPrefix() + if err != nil { + log.Fatal(err) + } + hwlog.OpLog.Infof("%v start running script", logPrefixWords) + + if !mindxcheckutils.StringChecker(strings.Join(os.Args, " "), 0, + maxCommandLength, mindxcheckutils.DefaultWhiteList+" ") { + hwlog.OpLog.Infof("%v run failed", logPrefixWords) + log.Fatal("command error") + } + + err, behavior := process() + if err != nil { + hwlog.OpLog.Errorf("%v run script failed: %v", logPrefixWords, err) + log.Fatal(fmt.Errorf("error in installation")) + } + hwlog.OpLog.Infof("%v run %v success", logPrefixWords, behavior) +} + +func initLogModule(ctx context.Context) error { + const backups = 2 + const logMaxAge = 365 + logConfig := hwlog.LogConfig{ + LogFileName: logPath, + LogLevel: 0, + MaxBackups: backups, + MaxAge: logMaxAge, + FileMaxSize: 2, + } + if err := hwlog.InitOperateLogger(&logConfig, ctx); err != nil { + fmt.Printf("hwlog init failed, error is %v", err) + return err + } + return nil +} + +func process() (error, string) { + const helpMessage = "\tadd \n" + + "\t rm \n" + + "\t -h help command" + helpFlag := flag.Bool("h", false, helpMessage) + flag.Parse() + if *helpFlag { + _, err := fmt.Println(helpMessage) + return err, "" + } + command := flag.Args() + if len(command) == 0 { + return fmt.Errorf("error param"), "" + } + action, behavior := command[actionPosition], "" + correctParam := false + if action == addCommand && len(command) == addCommandLength { + correctParam = true + behavior = "install" + } + if action == rmCommand && len(command) == rmCommandLength { + correctParam = true + behavior = "uninstall" + } + if !correctParam { + return fmt.Errorf("error param"), "" + } + + srcFilePath := command[srcFilePosition] + if _, err := os.Stat(srcFilePath); os.IsNotExist(err) { + if _, err := mindxcheckutils.RealDirChecker(filepath.Dir(srcFilePath), true, false); err != nil { + return err, behavior + } + } else { + if _, err := mindxcheckutils.RealFileChecker(srcFilePath, true, false, mindxcheckutils.DefaultSize); err != nil { + return err, behavior + } + } + + destFilePath := command[destFilePosition] + if _, err := mindxcheckutils.RealDirChecker(filepath.Dir(destFilePath), true, false); err != nil { + return err, behavior + } + runtimeFilePath := "" + if len(command) == addCommandLength { + runtimeFilePath = command[runtimeFilePosition] + } + + // check file permission + writeContent, err := createJsonString(srcFilePath, runtimeFilePath, action) + if err != nil { + return err, behavior + } + return writeJson(destFilePath, writeContent), behavior +} + +func createJsonString(srcFilePath, runtimeFilePath, action string) ([]byte, error) { + var writeContent []byte + if _, err := os.Stat(srcFilePath); err == nil { + daemon, err := modifyDaemon(srcFilePath, runtimeFilePath, action) + if err != nil { + return nil, err + } + writeContent, err = json.MarshalIndent(daemon, "", " ") + if err != nil { + return nil, err + } + } else if os.IsNotExist(err) { + // not existed + writeContent = []byte(fmt.Sprintf(template, runtimeFilePath)) + } else { + return nil, err + } + return writeContent, nil +} + +func writeJson(destFilePath string, writeContent []byte) error { + if _, err := os.Stat(destFilePath); os.IsNotExist(err) { + const perm = 0600 + file, err := os.OpenFile(destFilePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, perm) + if err != nil { + return fmt.Errorf("create target file failed") + } + _, err = file.Write(writeContent) + if err != nil { + closeErr := file.Close() + return fmt.Errorf("write target file failed with close err %v", closeErr) + } + err = file.Close() + if err != nil { + return fmt.Errorf("close target file failed") + } + return nil + } else { + return fmt.Errorf("target file already existed") + } +} + +func modifyDaemon(srcFilePath, runtimeFilePath, action string) (map[string]interface{}, error) { + // existed... + daemon, err := loadOriginJson(srcFilePath) + if err != nil { + return nil, err + } + + if _, ok := daemon["runtimes"]; !ok && action == addCommand { + daemon["runtimes"] = map[string]interface{}{} + } + runtimeValue := daemon["runtimes"] + runtimeConfig, runtimeConfigOk := runtimeValue.(map[string]interface{}) + if !runtimeConfigOk && action == addCommand { + return nil, fmt.Errorf("extract runtime failed") + } + if action == addCommand { + if _, ok := runtimeConfig["ascend"]; !ok { + runtimeConfig["ascend"] = map[string]interface{}{} + } + ascendConfig, ok := runtimeConfig["ascend"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("extract ascend failed") + } + ascendConfig["path"] = runtimeFilePath + if _, ok := ascendConfig["runtimeArgs"]; !ok { + ascendConfig["runtimeArgs"] = []string{} + } + daemon["default-runtime"] = "ascend" + } else if action == rmCommand { + if runtimeConfigOk { + delete(runtimeConfig, "ascend") + } + if value, ok := daemon["default-runtime"]; ok && value == "ascend" { + delete(daemon, "default-runtime") + } + } else { + return nil, fmt.Errorf("param error") + } + return daemon, nil +} + +func loadOriginJson(srcFilePath string) (map[string]interface{}, error) { + if fileInfo, err := os.Stat(srcFilePath); err != nil { + return nil, err + } else if fileInfo.Size() > maxFileSize { + return nil, fmt.Errorf("file size too large") + } + + file, err := os.Open(srcFilePath) + if err != nil { + return nil, fmt.Errorf("open daemon.json failed") + } + content, err := ioutil.ReadAll(file) + if err != nil { + closeErr := file.Close() + return nil, fmt.Errorf("read daemon.json failed, close file err is %v", closeErr) + } + err = file.Close() + if err != nil { + return nil, fmt.Errorf("close daemon.json failed") + } + + var daemon map[string]interface{} + err = json.Unmarshal(content, &daemon) + if err != nil { + return nil, fmt.Errorf("load daemon.json failed") + } + return daemon, nil +} diff --git a/install/deb/src/main_test.go b/install/deb/src/main_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ebecd29ebfd95970f001f32aff9d55155a8a9b1a --- /dev/null +++ b/install/deb/src/main_test.go @@ -0,0 +1,147 @@ +/* Copyright(C) 2021. Huawei Technologies Co.,Ltd. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package main +package main + +import ( + "encoding/json" + "os" + "reflect" + "testing" +) + +const oldString = `{ + "runtimes": { + "ascend": { + "path": "/test/runtime", + "runtimeArgs": [] + } + }, + "default-runtime": "ascend" + }` + +func jSONBytesEqual(a, b []byte) (bool, error) { + var contentA, contentB interface{} + if err := json.Unmarshal(a, &contentA); err != nil { + return false, err + } + if err := json.Unmarshal(b, &contentB); err != nil { + return false, err + } + return reflect.DeepEqual(contentB, contentA), nil +} + +func TestCreateJsonStringWholeNew(t *testing.T) { + data, err := createJsonString("/notExistedFile", "/test/runtime", "add") + if err != nil { + t.Fatalf("create string failed %s", err) + } + + if eq, err := jSONBytesEqual([]byte(oldString), data); err != nil || !eq { + t.Fatalf("empty create equal failed %s, %v", err, string(data)) + } +} + +func TestCreateJsonStringUpdate(t *testing.T) { + const perm = 0600 + if fid, err := os.OpenFile("old.json", os.O_CREATE|os.O_RDWR|os.O_TRUNC, perm); err == nil { + _, err = fid.Write([]byte(oldString)) + closeErr := fid.Close() + if err != nil || closeErr != nil { + t.Fatalf("create old failed %s", err) + } + } + data, err := createJsonString("old.json", "/test/runtime1", "add") + if err != nil { + t.Fatalf("update failed %s", err) + } + expectString := `{ + "runtimes": { + "ascend": { + "path": "/test/runtime1", + "runtimeArgs": [] + } + }, + "default-runtime": "ascend" + }` + if eq, err := jSONBytesEqual([]byte(expectString), data); err != nil || !eq { + t.Fatalf("update failed %s, %v", err, string(data)) + } +} + +func TestCreateJsonStringUpdateWithOtherParam(t *testing.T) { + const perm = 0600 + oldStringWithParam := `{ + "runtimes": { + "ascend": { + "path": "/test/runtime", + "runtimeArgs": [1,2,3] + }, + "runc2": { + "path": "/test/runtime2", + "runtimeArgs": [1,2,3] + } + }, + "default-runtime": "runc" + }` + if fid, err := os.OpenFile("old.json", os.O_CREATE|os.O_RDWR|os.O_TRUNC, perm); err == nil { + _, err = fid.Write([]byte(oldStringWithParam)) + closeErr := fid.Close() + if err != nil || closeErr != nil { + t.Fatalf("create old failed %s", err) + } + } + data, err := createJsonString("old.json", "/test/runtime1", "add") + if err != nil { + t.Fatalf("update failed %s", err) + } + expectString := `{ + "runtimes": { + "ascend": { + "path": "/test/runtime1", + "runtimeArgs": [1,2,3] + }, + "runc2": { + "path": "/test/runtime2", + "runtimeArgs": [1,2,3] + } + }, + "default-runtime": "ascend" + }` + if eq, err := jSONBytesEqual([]byte(expectString), data); err != nil || !eq { + t.Fatalf("update failed %s, %v", err, string(data)) + } +} + +func TestCreateJsonStrinRm(t *testing.T) { + const perm = 0600 + if fid, err := os.OpenFile("old.json", os.O_CREATE|os.O_RDWR|os.O_TRUNC, perm); err == nil { + _, err = fid.Write([]byte(oldString)) + closeErr := fid.Close() + if err != nil || closeErr != nil { + t.Fatalf("create old failed %s", err) + } + } + data, err := createJsonString("old.json", "", "rm") + if err != nil { + t.Fatalf("update failed %s", err) + } + expectString := `{ + "runtimes": {} + }` + if eq, err := jSONBytesEqual([]byte(expectString), data); err != nil || !eq { + t.Fatalf("update failed %s, %v", err, string(data)) + } +} diff --git a/mindxcheckutils/go.mod b/mindxcheckutils/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..7cfb90b4f47400637d6da85c38945e618e1479ce --- /dev/null +++ b/mindxcheckutils/go.mod @@ -0,0 +1,3 @@ +module mindxcheckutils + +go 1.17 diff --git a/mindxcheckutils/mindxcheckutils.go b/mindxcheckutils/mindxcheckutils.go new file mode 100644 index 0000000000000000000000000000000000000000..7fee4d067fa03e612abfc86bf39a36c0ac91e49a --- /dev/null +++ b/mindxcheckutils/mindxcheckutils.go @@ -0,0 +1,257 @@ +/* Copyright(C) 2021. Huawei Technologies Co.,Ltd. All rights reserved. + 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 mindxcheckutils +package mindxcheckutils + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "syscall" +) + +const ( + maxLogFieldLength = 64 + notValidPath = "not-valid-file-path" + maxAllowFileSize = 102400 // in megabytes + oneMegabytes = 1048576 + // DefaultSize default size of file allowed + DefaultSize = 100 // in megabytes + // DefaultWhiteList default white list in string + DefaultWhiteList = "-_./~" + // DefaultStringSize default string max length + DefaultStringSize = 256 + // DefaultPathSize default string max length + DefaultPathSize = 4096 + runLogDir = "/var/log/ascend-docker-runtime/" + backupLogFileMode os.FileMode = 0400 + runLogFileMode os.FileMode = 0750 + maxFileNum = 32 +) + +var logPrefix = "" + +// RealFileChecker check if a file is safe to use +func RealFileChecker(path string, checkParent, allowLink bool, size int) (string, error) { + if !StringChecker(path, 0, DefaultPathSize, DefaultWhiteList) { + return notValidPath, fmt.Errorf("invalid path") + } + _, err := FileChecker(path, false, checkParent, allowLink, 0) + if err != nil { + return notValidPath, err + } + realPath, err := filepath.Abs(path) + if err != nil { + return notValidPath, err + } + realPath, err = filepath.EvalSymlinks(realPath) + if err != nil { + return notValidPath, err + } + fileInfo, err := os.Stat(realPath) + if err != nil { + return notValidPath, err + } + if !fileInfo.Mode().IsRegular() { + return notValidPath, fmt.Errorf("invalid regular file") + } + if size > maxAllowFileSize || size < 0 { + return notValidPath, fmt.Errorf("invalid size") + } + if fileInfo.Size() > int64(size)*int64(oneMegabytes) { + return notValidPath, fmt.Errorf("size too large") + } + return realPath, nil +} + +// RealDirChecker check if a dir is safe to use +func RealDirChecker(path string, checkParent, allowLink bool) (string, error) { + if !StringChecker(path, 0, DefaultPathSize, DefaultWhiteList) { + return notValidPath, fmt.Errorf("invalid path") + } + _, err := FileChecker(path, true, checkParent, allowLink, 0) + if err != nil { + return notValidPath, err + } + realPath, err := filepath.Abs(path) + if err != nil { + return notValidPath, err + } + realPath, err = filepath.EvalSymlinks(realPath) + if err != nil { + return notValidPath, err + } + fileInfo, err := os.Stat(realPath) + if err != nil { + return notValidPath, err + } + if !fileInfo.IsDir() { + return notValidPath, fmt.Errorf("not a dir") + } + return realPath, nil +} + +// FileChecker check if a file/dir is safe to use +func FileChecker(path string, allowDir, checkParent, allowLink bool, deep int) (bool, error) { + const maxDepth, groupWriteIndex, otherWriteIndex, permLength int = 99, 5, 8, 10 + if deep > maxDepth { + return false, fmt.Errorf("over maxDepth %v", maxDepth) + } + if strings.Contains(path, "..") { + return false, fmt.Errorf("err path %v", path) + } + filePath, err := filepath.Abs(path) + if err != nil { + return false, fmt.Errorf("get abs path failed %v", err) + } + if len(filepath.Base(filePath)) > DefaultStringSize { + return false, fmt.Errorf("path too long") + } + fileInfo, ok, err := normalFileCheck(filePath, allowDir, allowLink) + if err != nil { + return ok, err + } + perm := fileInfo.Mode().Perm().String() + if len(perm) != permLength { + return false, fmt.Errorf("permission not right %v %v", filePath, perm) + } + for index, char := range perm { + switch index { + case groupWriteIndex, otherWriteIndex: + if char == 'w' { + return false, fmt.Errorf("write permission not right %v %v", filePath, perm) + } + default: + } + } + stat, ok := fileInfo.Sys().(*syscall.Stat_t) + if !ok { + return false, fmt.Errorf("can not get stat %v", filePath) + } + uid := int(stat.Uid) + if !(uid == 0 || uid == os.Getuid()) { + return false, fmt.Errorf("owner not right %v %v", filePath, uid) + } + if filePath != "/" && checkParent { + return FileChecker(filepath.Dir(filePath), true, true, allowLink, deep+1) + } + return true, nil +} + +func normalFileCheck(filePath string, allowDir bool, allowLink bool) (os.FileInfo, bool, error) { + realPath, err := filepath.EvalSymlinks(filePath) + if err != nil || (realPath != filePath && !allowLink) { + return nil, false, fmt.Errorf("symlinks or not existed, failed %v, %v", filePath, err) + } + fileInfo, err := os.Stat(filePath) + if err != nil { + return nil, false, fmt.Errorf("get file stat failed %v", err) + } + if allowDir { + if !fileInfo.Mode().IsRegular() && !fileInfo.IsDir() { + return nil, false, fmt.Errorf("not regular file/dir %v", filePath) + } + } else { + if !fileInfo.Mode().IsRegular() { + return nil, false, fmt.Errorf("not regular file %v", filePath) + } + } + if fileInfo.Mode()&os.ModeSetuid != 0 { + return nil, false, fmt.Errorf("setuid not allowed %v", filePath) + } + if fileInfo.Mode()&os.ModeSetgid != 0 { + return nil, false, fmt.Errorf("setgid not allowed %v", filePath) + } + return fileInfo, false, nil +} + +// GetLogPrefix get log prefix +func GetLogPrefix() (string, error) { + if logPrefix != "" { + return logPrefix, nil + } + uid := os.Geteuid() + if uid < 0 { + return "", fmt.Errorf("err: uid not right") + } + tty, err := filepath.EvalSymlinks("/proc/self/fd/0") + if err != nil || !StringChecker(tty, 0, DefaultStringSize, "/:") { + tty = "unknown" + } + + logPrefix = fmt.Sprintf("uid: %d tty: %v ", uid, tty) + return logPrefix, nil +} + +func isLetter(c rune) bool { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') +} + +func isNumber(c rune) bool { + return '0' <= c && c <= '9' +} + +func isInWhiteList(c rune, whiteList string) bool { + return strings.Contains(whiteList, string(c)) +} + +// StringChecker check string +func StringChecker(text string, minLength, maxLength int, whiteList string) bool { + if len(text) <= minLength || len(text) >= maxLength { + return false + } + for _, char := range text { + if isLetter(char) || isNumber(char) || isInWhiteList(char, whiteList) { + continue + } + return false + } + return true +} + +// ChangeRuntimeLogMode change log mode +func ChangeRuntimeLogMode(runLog, operLog string) error { + runLogDirLen := len(runLogDir) + var logMode os.FileMode + counter := 0 + err := filepath.Walk(runLogDir, func(fileOrPath string, fileInfo os.FileInfo, err error) error { + counter += 1 + if counter > maxFileNum { + return fmt.Errorf("the counter file is over maxFileNum") + } + if err != nil { + fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", fileOrPath, err) + return err + } + hasLogPrefix := strings.HasPrefix(fileOrPath[runLogDirLen:], + runLog) || strings.HasPrefix(fileOrPath[runLogDirLen:], operLog) + if !hasLogPrefix { + return nil + } + logMode = backupLogFileMode + if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink { + return fmt.Errorf("the file or path is symlink") + } + if errChmod := os.Chmod(fileOrPath, logMode); errChmod != nil { + return fmt.Errorf("set file mode %s failed", fileOrPath) + } + return nil + }) + if err != nil { + return fmt.Errorf("traversal runLogDir failed: %s", err) + } + return nil +} diff --git a/mindxcheckutils/mindxcheckutils_test.go b/mindxcheckutils/mindxcheckutils_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1d19a29c95300eb56491ee6ad0424dbdfb4ee770 --- /dev/null +++ b/mindxcheckutils/mindxcheckutils_test.go @@ -0,0 +1,169 @@ +/* Copyright(C) 2021. Huawei Technologies Co.,Ltd. All rights reserved. + 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 mindxcheckutils +package mindxcheckutils + +import ( + "os" + "strings" + "testing" +) + +func TestNormalFileCheckRegularFile(t *testing.T) { + tmpDir, filePath, err := createTestFile(t, "test_file.txt") + defer removeTmpDir(t, tmpDir) + err = os.Symlink(filePath, tmpDir+"/syslink") + if err != nil { + t.Fatalf("create symlink failed %q: %s", filePath, err) + } + + if _, _, err = normalFileCheck(tmpDir, true, false); err != nil { + t.Fatalf("check allow dir failed %q: %s", tmpDir+"/__test__", err) + } + + if _, _, err = normalFileCheck(tmpDir, false, false); !strings.Contains(err.Error(), "not regular file") { + t.Fatalf("check not allow dir failed %q: %s", tmpDir+"/__test__", err) + } + + if _, _, err = normalFileCheck("/dev/zero", true, false); !strings.Contains(err.Error(), "not regular file/dir") { + t.Fatalf("check /dev/zero failed %q: %s", tmpDir+"/__test__", err) + } + + if _, _, err = normalFileCheck(tmpDir+"/syslink", false, false); !strings.Contains(err.Error(), "symlinks") { + t.Fatalf("check symlinks failed %q: %s", tmpDir+"/syslink", err) + } + + if _, _, err = normalFileCheck(filePath, false, false); err != nil { + t.Fatalf("check failed %q: %s", filePath, err) + } + + if _, _, err = normalFileCheck(tmpDir+"/notexisted", false, false); !strings.Contains(err.Error(), "not existed") { + t.Fatalf("check symlinks failed %q: %s", tmpDir+"/syslink", err) + } +} + +func TestFileCheckRegularFile(t *testing.T) { + tmpDir, filePath, err := createTestFile(t, "test_file.txt") + defer removeTmpDir(t, tmpDir) + err = os.Symlink(filePath, tmpDir+"/syslink") + if err != nil { + t.Fatalf("create symlink failed %q: %s", filePath, err) + } + + if _, err = FileChecker(tmpDir, true, false, false, 0); err != nil { + t.Fatalf("check allow dir failed %q: %s", tmpDir+"/__test__", err) + } + + if _, err = FileChecker(tmpDir, false, false, false, 0); err != nil && + !strings.Contains(err.Error(), "not regular file") { + t.Fatalf("check not allow dir failed %q: %s", tmpDir+"/__test__", err) + } + + if _, err = FileChecker("/dev/zero", true, false, false, 0); err != nil && + !strings.Contains(err.Error(), "not regular file/dir") { + t.Fatalf("check /dev/zero failed %q: %s", tmpDir+"/__test__", err) + } +} + +func TestGetLogPrefix(t *testing.T) { + logPrefix = "" + prefix, err := GetLogPrefix() + if err != nil { + t.Fatalf("get log prefix failed %v %v", prefix, err) + } + if logPrefix == "" || prefix != logPrefix { + t.Fatalf("get log prefix failed 2 %v %v", prefix, prefix) + } +} + +func TestRealFileChecker(t *testing.T) { + tmpDir, filePath, err := createTestFile(t, "test_file.txt") + if err != nil { + t.Fatalf("create file failed %q: %s", filePath, err) + } + defer removeTmpDir(t, tmpDir) + const permission os.FileMode = 0700 + err = os.WriteFile(filePath, []byte("hello\n"), permission) + if err != nil { + t.Fatalf("create file failed %q: %s", filePath, err) + } + if _, err = RealFileChecker(filePath, false, true, 0); err == nil { + t.Fatalf("size check wrong 0 %q: %s", filePath, err) + } + if _, err = RealFileChecker(filePath, false, true, 1); err != nil { + t.Fatalf("size check wrong 1 %q: %s", filePath, err) + } +} + +func TestRealDirChecker(t *testing.T) { + tmpDir, filePath, err := createTestFile(t, "test_file.txt") + if err != nil { + t.Fatalf("create file failed %q: %s", filePath, err) + } + defer removeTmpDir(t, tmpDir) + if _, err = RealDirChecker(filePath, false, true); err == nil { + t.Fatalf("should be dir 0 %q: %s", filePath, err) + } + if _, err = RealDirChecker(tmpDir, false, true); err != nil { + t.Fatalf("should be dir 1 %q: %s", filePath, err) + } +} + +func TestStringChecker(t *testing.T) { + if ok := StringChecker("0123456789abcABC", 0, DefaultStringSize, ""); !ok { + t.Fatalf("failed on regular letters") + } + const testSize = 3 + if ok := StringChecker("123", 0, testSize, ""); ok { + t.Fatalf("failed on max length") + } + if ok := StringChecker("1234", 0, testSize, ""); ok { + t.Fatalf("failed on max length") + } + if ok := StringChecker("12", 0, testSize, ""); !ok { + t.Fatalf("failed on max length") + } + if ok := StringChecker("", 0, testSize, ""); ok { + t.Fatalf("failed on min length") + } + if ok := StringChecker("123", testSize, DefaultStringSize, ""); ok { + t.Fatalf("failed on min length") + } + if ok := StringChecker("123%", 0, DefaultStringSize, ""); ok { + t.Fatalf("failed on strange words") + } + if ok := StringChecker("123.-/~", 0, DefaultStringSize, DefaultWhiteList); !ok { + t.Fatalf("failed on strange words") + } +} + +func createTestFile(t *testing.T, fileName string) (string, string, error) { + tmpDir := os.TempDir() + const permission os.FileMode = 0700 + if os.MkdirAll(tmpDir+"/__test__", permission) != nil { + t.Fatalf("MkdirAll failed %q", tmpDir+"/__test__") + } + _, err := os.Create(tmpDir + "/__test__" + fileName) + if err != nil { + t.Fatalf("create file failed %q: %s", tmpDir+"/__test__", err) + } + return tmpDir + "/__test__", tmpDir + "/__test__" + fileName, err +} + +func removeTmpDir(t *testing.T, tmpDir string) { + if os.RemoveAll(tmpDir) != nil { + t.Logf("removeall %v", tmpDir) + } +} diff --git a/opensource/README.MD b/opensource/README.MD new file mode 100644 index 0000000000000000000000000000000000000000..7f17201049e5a5c729e97e0b0802de60909127bc --- /dev/null +++ b/opensource/README.MD @@ -0,0 +1 @@ +put 3rd-party dependency here \ No newline at end of file diff --git a/output/README.MD b/output/README.MD new file mode 100644 index 0000000000000000000000000000000000000000..09b35f56b3f0709c60d29dd49f5a9699e6989eee --- /dev/null +++ b/output/README.MD @@ -0,0 +1 @@ +locate the output files \ No newline at end of file diff --git a/platform/README.MD b/platform/README.MD new file mode 100644 index 0000000000000000000000000000000000000000..0e1b50b594f99695ea326ce613655c482490a1d0 --- /dev/null +++ b/platform/README.MD @@ -0,0 +1 @@ +put platform dependency here \ No newline at end of file diff --git a/runtime/.gitignore b/runtime/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..eba7e909bded429a72c5a883eecbe8127f54f038 --- /dev/null +++ b/runtime/.gitignore @@ -0,0 +1,3 @@ +.vscode +.idea +ascend-docker-runtime \ No newline at end of file diff --git a/runtime/dcmi/dcmi.go b/runtime/dcmi/dcmi.go new file mode 100644 index 0000000000000000000000000000000000000000..f11ad75001ee46befada3e4dea29bc2e5d244586 --- /dev/null +++ b/runtime/dcmi/dcmi.go @@ -0,0 +1,197 @@ +/* Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. + 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 dcmi +package dcmi + +// #cgo LDFLAGS: -ldl +// #include "dcmi_interface_api.h" +import "C" +import ( + "fmt" + "math" + "unsafe" + + "mindxcheckutils" +) + +const ( + // RetError return error when the function failed + retError = -1 + // hiAIMaxCardNum is the max number of cards + hiAIMaxCardNum = 64 + // hiAIMaxDeviceNum is the max number of devices in a card + hiAIMaxDeviceNum = 4 + + coreNumLen = 32 + vfgID = 4294967295 // vfg_id表示指定虚拟设备所属的虚拟分组ID,默认自动分配,默认值为0xFFFFFFFF,转换成10进制为4294967295。 +) + +// NpuWorker Dcmi worker +type NpuWorker struct { +} + +// Initialize dcmi lib init +func (w *NpuWorker) Initialize() error { + cDlPath := C.CString(string(make([]byte, int32(C.PATH_MAX)))) + defer C.free(unsafe.Pointer(cDlPath)) + if err := C.dcmiInit_dl(cDlPath); err != C.SUCCESS { + errInfo := fmt.Errorf("dcmi lib load failed, , error code: %d", int32(err)) + return errInfo + } + dlPath := C.GoString(cDlPath) + if _, err := mindxcheckutils.RealFileChecker(dlPath, true, false, mindxcheckutils.DefaultSize); err != nil { + return err + } + if err := C.dcmi_init(); err != C.SUCCESS { + errInfo := fmt.Errorf("dcmi init failed, , error code: %d", int32(err)) + return errInfo + } + return nil +} + +// ShutDown shutdown dcmi lib +func (w *NpuWorker) ShutDown() { + if err := C.dcmiShutDown(); err != C.SUCCESS { + println(fmt.Errorf("dcmi shut down failed, error code: %d", int32(err))) + } +} + +// GetCardList list all cards on system +func GetCardList() (int32, []int32, error) { + var ids [hiAIMaxCardNum]C.int + var cNum C.int + if err := C.dcmi_get_card_num_list(&cNum, &ids[0], hiAIMaxCardNum); err != 0 { + errInfo := fmt.Errorf("get card list failed, error code: %d", int32(err)) + return retError, nil, errInfo + } + // checking card's quantity + if cNum <= 0 || cNum > hiAIMaxCardNum { + errInfo := fmt.Errorf("get error card quantity: %d", int32(cNum)) + return retError, nil, errInfo + } + var cardNum = int32(cNum) + var cardIDList []int32 + for i := int32(0); i < cardNum; i++ { + cardID := int32(ids[i]) + if cardID < 0 { + continue + } + cardIDList = append(cardIDList, cardID) + } + return cardNum, cardIDList, nil +} + +// GetDeviceNumInCard get device number in the npu card +func GetDeviceNumInCard(cardID int32) (int32, error) { + var deviceNum C.int + if err := C.dcmi_get_device_num_in_card(C.int(cardID), &deviceNum); err != 0 { + errInfo := fmt.Errorf("get device count on the card failed, error code: %d", int32(err)) + return retError, errInfo + } + if deviceNum <= 0 || deviceNum > hiAIMaxDeviceNum { + errInfo := fmt.Errorf("the number of chips obtained is invalid, the number is: %d", int32(deviceNum)) + return retError, errInfo + } + return int32(deviceNum), nil +} + +// GetDeviceLogicID get device logicID +func GetDeviceLogicID(cardID, deviceID int32) (int32, error) { + var logicID C.int + if err := C.dcmi_get_device_logic_id(&logicID, C.int(cardID), C.int(deviceID)); err != 0 { + errInfo := fmt.Errorf("get logicID failed, error code: %d", int32(err)) + return retError, errInfo + } + + // check whether phyID is too big + if logicID < 0 || uint32(logicID) > uint32(math.MaxInt8) { + errInfo := fmt.Errorf("the logicID value is invalid, logicID is: %d", logicID) + return retError, errInfo + } + return int32(logicID), nil +} + +// CreateVDevice create virtual device +func (w *NpuWorker) CreateVDevice(cardID, deviceID int32, coreNum string) (int32, error) { + var createInfo C.struct_dcmi_create_vdev_out + createInfo.vdev_id = C.uint(math.MaxUint32) + var deviceCreateStr C.struct_dcmi_create_vdev_res_stru + deviceCreateStr = C.struct_dcmi_create_vdev_res_stru{ + vdev_id: C.uint(vfgID), + vfg_id: C.uint(vfgID), + } + deviceCreateStrArr := [coreNumLen]C.char{0} + for i := 0; i < len(coreNum); i++ { + if i >= coreNumLen { + return math.MaxInt32, fmt.Errorf("wrong template") + } + deviceCreateStrArr[i] = C.char(coreNum[i]) + } + deviceCreateStr.template_name = deviceCreateStrArr + err := C.dcmi_create_vdevice(C.int(cardID), C.int(deviceID), &deviceCreateStr, &createInfo) + if err != 0 { + errInfo := fmt.Errorf("create virtual device failed, error code: %d", int32(err)) + return math.MaxInt32, errInfo + } + if createInfo.vdev_id > math.MaxInt32 { + return math.MaxInt32, fmt.Errorf("create virtual device failed, vdeviceId too large") + } + return int32(createInfo.vdev_id), nil +} + +// DestroyVDevice destroy virtual device +func (w *NpuWorker) DestroyVDevice(cardID, deviceID int32, vDevID int32) error { + if vDevID < 0 { + return fmt.Errorf("param error on vDevID") + } + if err := C.dcmi_set_destroy_vdevice(C.int(cardID), C.int(deviceID), C.uint(vDevID)); err != 0 { + errInfo := fmt.Errorf("destroy virtual device failed, error code: %d", int32(err)) + return errInfo + } + return nil +} + +// FindDevice find device by phyical id +func (w *NpuWorker) FindDevice(visibleDevice int32) (int32, int32, error) { + var dcmiLogicID C.uint + if err := C.dcmi_get_device_logicid_from_phyid(C.uint(visibleDevice), &dcmiLogicID); err != 0 { + return 0, 0, fmt.Errorf("phy id can not be converted to logic id : %v", err) + } + if int32(dcmiLogicID) < 0 || int32(dcmiLogicID) >= hiAIMaxCardNum*hiAIMaxDeviceNum { + return 0, 0, fmt.Errorf("logic id too large") + } + targetLogicID := int32(dcmiLogicID) + _, cardList, err := GetCardList() + if err != nil { + return 0, 0, fmt.Errorf("get card list err : %v", err) + } + targetDeviceID, targetCardID := int32(math.MaxInt32), int32(math.MaxInt32) + for _, cardID := range cardList { + deviceCount, err := GetDeviceNumInCard(cardID) + if err != nil { + return 0, 0, fmt.Errorf("cannot get device num in card : %v", err) + } + for deviceID := int32(0); deviceID < deviceCount; deviceID++ { + logicID, err := GetDeviceLogicID(cardID, deviceID) + if err != nil { + return 0, 0, fmt.Errorf("cannot get logic id : %v", err) + } + if logicID == int32(targetLogicID) { + targetCardID, targetDeviceID = cardID, deviceID + } + } + } + return targetDeviceID, targetCardID, nil +} diff --git a/runtime/dcmi/dcmi_api.go b/runtime/dcmi/dcmi_api.go new file mode 100644 index 0000000000000000000000000000000000000000..677888f4d01e1f7d8f09ad370687d4d267d54c9c --- /dev/null +++ b/runtime/dcmi/dcmi_api.go @@ -0,0 +1,101 @@ +/* Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. + 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 dcmi is used to work with Ascend devices +package dcmi + +import ( + "fmt" + "strconv" + "strings" + + "github.com/opencontainers/runtime-spec/specs-go" +) + +// VDeviceInfo vdevice created info +type VDeviceInfo struct { + CardID int32 + DeviceID int32 + VdeviceID int32 +} + +// WorkerInterface worker interface +type WorkerInterface interface { + Initialize() error + ShutDown() + FindDevice(visibleDevice int32) (int32, int32, error) + CreateVDevice(cardID, deviceID int32, coreNum string) (int32, error) + DestroyVDevice(cardID, deviceID int32, vDevID int32) error +} + +// CreateVDevice will create virtual device +func CreateVDevice(w WorkerInterface, spec *specs.Spec) (VDeviceInfo, error) { + visibleDevice, splitDevice, err := extractVpuParam(spec) + invalidVDevice := VDeviceInfo{CardID: -1, DeviceID: -1, VdeviceID: -1} + if err != nil || visibleDevice < 0 { + return invalidVDevice, err + } + if err := w.Initialize(); err != nil { + return invalidVDevice, fmt.Errorf("cannot init dcmi : %v", err) + } + defer w.ShutDown() + targetDeviceID, targetCardID, err := w.FindDevice(visibleDevice) + if err != nil { + return invalidVDevice, err + } + + vdeviceID, err := w.CreateVDevice(targetCardID, targetDeviceID, splitDevice) + if err != nil || vdeviceID < 0 { + return invalidVDevice, fmt.Errorf("cannot create vd or vdevice is wrong: %v %v", vdeviceID, err) + } + return VDeviceInfo{CardID: targetCardID, DeviceID: targetDeviceID, VdeviceID: int32(vdeviceID)}, nil +} + +func extractVpuParam(spec *specs.Spec) (int32, string, error) { + splitDevice, needSplit, visibleDeviceLine := "", false, "" + allowSplit := map[string]string{ + "vir01": "vir01", "vir02": "vir02", "vir04": "vir04", "vir08": "vir08", "vir16": "vir16", + "vir04_3c": "vir04_3c", "vir02_1c": "vir02_1c", "vir04_4c_dvpp": "vir04_4c_dvpp", + "vir04_3c_ndvpp": "vir04_3c_ndvpp", + } + + for _, line := range spec.Process.Env { + words := strings.Split(line, "=") + const LENGTH int = 2 + if len(words) != LENGTH { + continue + } + if strings.TrimSpace(words[0]) == "ASCEND_VISIBLE_DEVICES" { + visibleDeviceLine = words[1] + } + if strings.TrimSpace(words[0]) == "ASCEND_VNPU_SPECS" { + if split, ok := allowSplit[words[1]]; split != "" && ok { + splitDevice = split + needSplit = true + } else { + return -1, "", fmt.Errorf("cannot parse param : %v", words[1]) + } + } + } + if !needSplit { + return -1, "", nil + } + visibleDevice, err := strconv.Atoi(visibleDeviceLine) + if err != nil || visibleDevice < 0 || visibleDevice >= hiAIMaxCardNum*hiAIMaxDeviceNum { + return -1, "", fmt.Errorf("cannot parse param : %v %s", err, visibleDeviceLine) + + } + + return int32(visibleDevice), splitDevice, nil +} diff --git a/runtime/dcmi/dcmi_api_test.go b/runtime/dcmi/dcmi_api_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e609d6fceafc932ba68eec15979f0e08de981056 --- /dev/null +++ b/runtime/dcmi/dcmi_api_test.go @@ -0,0 +1,82 @@ +/* Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. + 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. +*/ + +// Description: dcmi DT Test +package dcmi + +import ( + "testing" + + "github.com/opencontainers/runtime-spec/specs-go" +) + +const mockDeviceID = 100 + +type mockWorker struct{} + +func (w *mockWorker) Initialize() error { + return nil +} + +// ShutDown shutdown mock lib +func (w *mockWorker) ShutDown() { + return +} + +// CreateVDevice create v device +func (w *mockWorker) CreateVDevice(_, _ int32, _ string) (int32, error) { + + return int32(mockDeviceId), nil +} + +// DestroyVDevice destroy virtual device +func (w *mockWorker) DestroyVDevice(_, _, _ int32) error { + return nil +} + +// FindDevice find device by phyical id +func (w *mockWorker) FindDevice(_ int32) (int32, int32, error) { + return 0, 0, nil +} + +func TestCreateVDevice(t *testing.T) { + t.Log("TestCreateVDevice start") + process := specs.Process{} + spec := specs.Spec{Process: &process} + spec.Process.Env = []string{} + + // no split, all ok + vdevice, err := CreateVDevice(&mockWorker{}, &spec) + if err != nil { + t.Fatalf("%v %v", vdevice, err) + } + + // no npu assigin for split + spec.Process.Env = []string{"ASCEND_VNPU_SPECS=vir04"} + vdevice, err = CreateVDevice(&mockWorker{}, &spec) + if err == nil { + t.Fatalf("%v %v", vdevice, err) + } + + // split ok + spec.Process.Env = []string{"ASCEND_VNPU_SPECS=vir04", "ASCEND_VISIBLE_DEVICES=0"} + vdevice, err = CreateVDevice(&mockWorker{}, &spec) + if err != nil { + t.Fatalf("%v %v", vdevice, err) + } + if vdevice.VdeviceID != mockDeviceId { + t.Fatalf("%v %v", vdevice, err) + } + +} diff --git a/runtime/dcmi/dcmi_interface_api.h b/runtime/dcmi/dcmi_interface_api.h new file mode 100644 index 0000000000000000000000000000000000000000..43233ed0b3f93b98fe17ddd230f1ede7bbd25198 --- /dev/null +++ b/runtime/dcmi/dcmi_interface_api.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2020-2022. All rights reserved. + * 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. + */ +#ifndef __DCMI_INTERFACE_API_H__ +#define __DCMI_INTERFACE_API_H__ +#include +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +void *dcmiHandle; +#define SO_NOT_FOUND (-99999) +#define FUNCTION_NOT_FOUND (-99998) +#define SUCCESS (0) +#define ERROR_UNKNOWN (-99997) +#define SO_NOT_CORRECT (-99996) +#define CALL_FUNC(name, ...) if (name##_func == NULL) {return FUNCTION_NOT_FOUND;}return name##_func(__VA_ARGS__) +#define DCMI_VDEV_FOR_RESERVE (32) +struct dcmi_create_vdev_out { + unsigned int vdev_id; + unsigned int pcie_bus; + unsigned int pcie_device; + unsigned int pcie_func; + unsigned int vfg_id; + unsigned char reserved[DCMI_VDEV_FOR_RESERVE]; +}; +struct dcmi_create_vdev_res_stru { + unsigned int vdev_id; + unsigned int vfg_id; + char template_name[32]; + unsigned char reserved[64]; +}; + +// dcmi +int (*dcmi_init_func)(); +int dcmi_init() +{ + CALL_FUNC(dcmi_init); +} + +int (*dcmi_get_card_num_list_func)(int *card_num, int *card_list, int list_length); +int dcmi_get_card_num_list(int *card_num, int *card_list, int list_length) +{ + CALL_FUNC(dcmi_get_card_num_list, card_num, card_list, list_length); +} + +int (*dcmi_get_device_num_in_card_func)(int card_id, int *device_num); +int dcmi_get_device_num_in_card(int card_id, int *device_num) +{ + CALL_FUNC(dcmi_get_device_num_in_card, card_id, device_num); +} + +int (*dcmi_get_device_logic_id_func)(int *device_logic_id, int card_id, int device_id); +int dcmi_get_device_logic_id(int *device_logic_id, int card_id, int device_id) +{ + CALL_FUNC(dcmi_get_device_logic_id, device_logic_id, card_id, device_id); +} + +int (*dcmi_create_vdevice_func)(int card_id, int device_id, + struct dcmi_create_vdev_res_stru *vdev, + struct dcmi_create_vdev_out *out); +int dcmi_create_vdevice(int card_id, int device_id, + struct dcmi_create_vdev_res_stru *vdev, + struct dcmi_create_vdev_out *out) +{ + CALL_FUNC(dcmi_create_vdevice, card_id, device_id, vdev, out); +} + +int (*dcmi_set_destroy_vdevice_func)(int card_id, int device_id, unsigned int VDevid); +int dcmi_set_destroy_vdevice(int card_id, int device_id, unsigned int VDevid) +{ + CALL_FUNC(dcmi_set_destroy_vdevice, card_id, device_id, VDevid); +} + +int (*dcmi_get_device_logicid_from_phyid_func)(unsigned int phyid, unsigned int *logicid); +int dcmi_get_device_logicid_from_phyid(unsigned int phyid, unsigned int *logicid) +{ + CALL_FUNC(dcmi_get_device_logicid_from_phyid, phyid, logicid); +} + +// load .so files and functions +int dcmiInit_dl(char *dl_path) +{ + dcmiHandle = dlopen("libdcmi.so", RTLD_LAZY | RTLD_GLOBAL); + if (dcmiHandle == NULL) { + fprintf (stderr, "%s\n", dlerror()); + return SO_NOT_FOUND; + } + struct link_map *pLinkMap; + int ret = dlinfo(dcmiHandle, RTLD_DI_LINKMAP, &pLinkMap); + if (ret != 0) { + fprintf(stderr, "dlinfo sofile failed :%s\n", dlerror()); + return SO_NOT_CORRECT; + } + + size_t path_size = strlen(pLinkMap->l_name); + for (int i = 0; i < path_size && i < PATH_MAX; i++) { + dl_path[i] = pLinkMap->l_name[i]; + } + + dcmi_init_func = dlsym(dcmiHandle, "dcmi_init"); + + dcmi_get_card_num_list_func = dlsym(dcmiHandle, "dcmi_get_card_num_list"); + + dcmi_get_device_num_in_card_func = dlsym(dcmiHandle, "dcmi_get_device_num_in_card"); + + dcmi_get_device_logic_id_func = dlsym(dcmiHandle, "dcmi_get_device_logic_id"); + + dcmi_create_vdevice_func = dlsym(dcmiHandle, "dcmi_create_vdevice"); + + dcmi_set_destroy_vdevice_func = dlsym(dcmiHandle, "dcmi_set_destroy_vdevice"); + + dcmi_get_device_logicid_from_phyid_func = dlsym(dcmiHandle, "dcmi_get_device_logicid_from_phyid"); + + return SUCCESS; +} + +int dcmiShutDown(void) +{ + if (dcmiHandle == NULL) { + return SUCCESS; + } + return (dlclose(dcmiHandle) ? ERROR_UNKNOWN : SUCCESS); +} + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* __DCMI_INTERFACE_API_H__ */ diff --git a/runtime/go.mod b/runtime/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..695ff700adaea31ba66ee77334b15ed2c3905f57 --- /dev/null +++ b/runtime/go.mod @@ -0,0 +1,25 @@ +module main + +go 1.17 + +require ( + github.com/opencontainers/runtime-spec v1.0.3-0.20220718201635-a8106e99982b + github.com/prashantv/gostub v1.1.0 + huawei.com/npu-exporter/v3 v3.0.0 + mindxcheckutils v1.0.0 +) + +require ( + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gopherjs/gopherjs v1.17.2 // indirect + github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/smartystreets/assertions v1.13.0 // indirect + github.com/stretchr/testify v1.8.0 // indirect + golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect +) + +replace ( + github.com/prashantv/gostub => github.com/prashantv/gostub v1.0.1-0.20191007164320-bbe3712b9c4a + huawei.com/npu-exporter/v3 => gitee.com/ascend/ascend-npu-exporter/v3 v3.0.0 + mindxcheckutils => ../mindxcheckutils +) diff --git a/runtime/go.sum b/runtime/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..47586b035c9d87d16a7ee2b36b39586eb2a9d44b --- /dev/null +++ b/runtime/go.sum @@ -0,0 +1,677 @@ +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= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gitee.com/ascend/ascend-npu-exporter/v3 v3.0.0 h1:JfB5Kmce3mWEzbtAhybJozGp6+yJH+jU7D6WytEcOzs= +gitee.com/ascend/ascend-npu-exporter/v3 v3.0.0/go.mod h1:78lAYBVM18u8mobeoKqhJP7POvbayTYBk32hnm9IkfQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/agiledragon/gomonkey/v2 v2.8.0 h1:u2K2nNGyk0ippzklz1CWalllEB9ptD+DtSXeCX5O000= +github.com/agiledragon/gomonkey/v2 v2.8.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/opencontainers/runtime-spec v1.0.3-0.20220718201635-a8106e99982b h1:udwtfS44rxYE/ViMLchHQBjfE60GZSB1arY7BFbyxLs= +github.com/opencontainers/runtime-spec v1.0.3-0.20220718201635-a8106e99982b/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prashantv/gostub v1.0.1-0.20191007164320-bbe3712b9c4a h1:tkiehFUSAXqIwMzuwKutcjSIZhpc3OKax/c9oKDz5mY= +github.com/prashantv/gostub v1.0.1-0.20191007164320-bbe3712b9c4a/go.mod h1:dP1v6T1QzyGJJKFocwAU0lSZKpfjstjH8TlhkEU0on0= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs= +github.com/smartystreets/assertions v1.13.0/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8= +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/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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-20190213061140-3a22650c66bd/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/cri-api v0.19.4/go.mod h1:UN/iU9Ua0iYdDREBXNE9vqCJ7MIh/FW3VIL0d8pw7Fw= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/runtime/main.go b/runtime/main.go new file mode 100644 index 0000000000000000000000000000000000000000..df1f5f973a1037ccf79062a62296c78d6aba6697 --- /dev/null +++ b/runtime/main.go @@ -0,0 +1,331 @@ +/* Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package main +package main + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "path" + "path/filepath" + "strings" + "syscall" + + "github.com/opencontainers/runtime-spec/specs-go" + "huawei.com/npu-exporter/v3/common-utils/hwlog" + + "main/dcmi" + "mindxcheckutils" +) + +const ( + runLogPath = "/var/log/ascend-docker-runtime/runtime-run.log" + operateLogPath = "/var/log/ascend-docker-runtime/runtime-operate.log" + maxLogLength = 1024 + maxCommandLength = 65535 + hookCli = "ascend-docker-hook" + destroyHookCli = "ascend-docker-destroy" + hookDefaultFilePath = "/usr/local/bin/ascend-docker-hook" + dockerRuncFile = "docker-runc" + runcFile = "runc" + envLength = 2 +) + +var ( + hookCliPath = hookCli + hookDefaultFile = hookDefaultFilePath + dockerRuncName = dockerRuncFile + runcName = runcFile +) + +type args struct { + bundleDirPath string + cmd string +} + +func getArgs() (*args, error) { + args := &args{} + + for i, param := range os.Args { + if param == "--bundle" || param == "-b" { + if len(os.Args)-i <= 1 { + return nil, fmt.Errorf("bundle option needs an argument") + } + args.bundleDirPath = os.Args[i+1] + } else if param == "create" { + args.cmd = param + } + } + + return args, nil +} + +func initLogModule(ctx context.Context) error { + const backups = 2 + const logMaxAge = 365 + runLogConfig := hwlog.LogConfig{ + LogFileName: runLogPath, + LogLevel: 0, + MaxBackups: backups, + MaxAge: logMaxAge, + OnlyToFile: true, + FileMaxSize: 2, + } + if err := hwlog.InitRunLogger(&runLogConfig, ctx); err != nil { + fmt.Printf("hwlog init failed, error is %v", err) + return err + } + operateLogConfig := hwlog.LogConfig{ + LogFileName: operateLogPath, + LogLevel: 0, + MaxBackups: backups, + MaxAge: logMaxAge, + OnlyToFile: true, + FileMaxSize: 2, + } + if err := hwlog.InitOperateLogger(&operateLogConfig, ctx); err != nil { + fmt.Printf("hwlog init failed, error is %v", err) + return err + } + return nil +} + +var execRunc = func() error { + tempRuncPath, err := exec.LookPath(dockerRuncName) + if err != nil { + tempRuncPath, err = exec.LookPath(runcName) + if err != nil { + return fmt.Errorf("failed to find the path of runc: %v", err) + } + } + runcPath, err := filepath.EvalSymlinks(tempRuncPath) + if err != nil { + return fmt.Errorf("failed to find realpath of runc %v", err) + } + if _, err := mindxcheckutils.RealFileChecker(runcPath, true, false, mindxcheckutils.DefaultSize); err != nil { + return err + } + + hwlog.OpLog.Infof("ascend docker runtime success, will start runc") + if err := mindxcheckutils.ChangeRuntimeLogMode("runtime-run-", "runtime-operate-"); err != nil { + return err + } + if err = syscall.Exec(runcPath, append([]string{runcPath}, os.Args[1:]...), os.Environ()); err != nil { + return fmt.Errorf("failed to exec runc: %v", err) + } + + return nil +} + +func addHook(spec *specs.Spec) error { + currentExecPath, err := os.Executable() + if err != nil { + return fmt.Errorf("cannot get the path of ascend-docker-runtime: %v", err) + } + + hookCliPath = path.Join(path.Dir(currentExecPath), hookCli) + if _, err := mindxcheckutils.RealFileChecker(hookCliPath, true, false, mindxcheckutils.DefaultSize); err != nil { + return err + } + if _, err = os.Stat(hookCliPath); err != nil { + return fmt.Errorf("cannot find ascend-docker-hook executable file at %s: %v", hookCliPath, err) + } + + if spec.Hooks == nil { + spec.Hooks = &specs.Hooks{} + } + + needUpdate := true + if len(spec.Hooks.Prestart) > maxCommandLength { + return fmt.Errorf("too many items in Prestart ") + } + for _, hook := range spec.Hooks.Prestart { + if strings.Contains(hook.Path, hookCli) { + needUpdate = false + break + } + } + if needUpdate { + spec.Hooks.Prestart = append(spec.Hooks.Prestart, specs.Hook{ + Path: hookCliPath, + Args: []string{hookCliPath}, + }) + } + + if len(spec.Process.Env) > maxCommandLength { + return fmt.Errorf("too many items in Env ") + } + for _, line := range spec.Process.Env { + words := strings.Split(line, "=") + if len(words) == envLength && strings.TrimSpace(words[0]) == "ASCEND_RUNTIME_OPTIONS" && + strings.Contains(words[1], "VIRTUAL") { + return nil + } + } + + vdevice, err := dcmi.CreateVDevice(&dcmi.NpuWorker{}, spec) + if err != nil { + return err + } + hwlog.RunLog.Infof("vnpu split done: vdevice: %v", vdevice.VdeviceID) + + if vdevice.VdeviceID != -1 { + updateEnvAndPostHook(spec, vdevice) + } + + return nil +} + +func updateEnvAndPostHook(spec *specs.Spec, vdevice dcmi.VDeviceInfo) { + newEnv := make([]string, 0) + needAddVirtualFlag := true + for _, line := range spec.Process.Env { + words := strings.Split(line, "=") + if len(words) == envLength && strings.TrimSpace(words[0]) == "ASCEND_VISIBLE_DEVICES" { + newEnv = append(newEnv, fmt.Sprintf("ASCEND_VISIBLE_DEVICES=%d", vdevice.VdeviceID)) + continue + } + if len(words) == envLength && strings.TrimSpace(words[0]) == "ASCEND_RUNTIME_OPTIONS" { + needAddVirtualFlag = false + if strings.Contains(words[1], "VIRTUAL") { + newEnv = append(newEnv, line) + continue + } else { + newEnv = append(newEnv, strings.TrimSpace(line)+",VIRTUAL") + continue + } + } + newEnv = append(newEnv, line) + } + if needAddVirtualFlag { + newEnv = append(newEnv, fmt.Sprintf("ASCEND_RUNTIME_OPTIONS=VIRTUAL")) + } + spec.Process.Env = newEnv + if currentExecPath, err := os.Executable(); err == nil { + postHookCliPath := path.Join(path.Dir(currentExecPath), destroyHookCli) + spec.Hooks.Poststop = append(spec.Hooks.Poststop, specs.Hook{ + Path: postHookCliPath, + Args: []string{postHookCliPath, fmt.Sprintf("%d", vdevice.CardID), fmt.Sprintf("%d", vdevice.DeviceID), + fmt.Sprintf("%d", vdevice.VdeviceID)}, + }) + } +} + +func modifySpecFile(path string) error { + stat, err := os.Stat(path) + if err != nil { + return fmt.Errorf("spec file doesnt exist %s: %v", path, err) + } + if _, err = mindxcheckutils.RealFileChecker(path, true, true, mindxcheckutils.DefaultSize); err != nil { + return err + } + + jsonFile, err := os.OpenFile(path, os.O_RDWR, stat.Mode()) + if err != nil { + return fmt.Errorf("cannot open oci spec file %s: %v", path, err) + } + + defer jsonFile.Close() + + jsonContent, err := ioutil.ReadAll(jsonFile) + if err != nil { + return fmt.Errorf("failed to read oci spec file %s: %v", path, err) + } + + var spec specs.Spec + if err = json.Unmarshal(jsonContent, &spec); err != nil { + return fmt.Errorf("failed to unmarshal oci spec file %s: %v", path, err) + } + + if err = addHook(&spec); err != nil { + return fmt.Errorf("failed to inject hook: %v", err) + } + + jsonOutput, err := json.Marshal(spec) + if err != nil { + return fmt.Errorf("failed to marshal OCI spec file: %v", err) + } + + if _, err = jsonFile.WriteAt(jsonOutput, 0); err != nil { + return fmt.Errorf("failed to write OCI spec file: %v", err) + } + + return nil +} + +func doProcess() error { + args, err := getArgs() + if err != nil { + return fmt.Errorf("failed to get args: %v", err) + } + + if args.cmd != "create" { + return execRunc() + } + + if args.bundleDirPath == "" { + args.bundleDirPath, err = os.Getwd() + if err != nil { + return fmt.Errorf("failed to get current working dir: %v", err) + } + } + + specFilePath := args.bundleDirPath + "/config.json" + + if err = modifySpecFile(specFilePath); err != nil { + return fmt.Errorf("failed to modify spec file %s: %v", specFilePath, err) + } + + return execRunc() +} + +func main() { + defer func() { + if err := recover(); err != nil { + log.Fatal(err) + } + }() + ctx, _ := context.WithCancel(context.Background()) + if err := initLogModule(ctx); err != nil { + log.Fatal(err) + } + logPrefixWords, err := mindxcheckutils.GetLogPrefix() + if err != nil { + log.Fatal(err) + } + defer func() { + if err = mindxcheckutils.ChangeRuntimeLogMode("runtime-run-", "runtime-operate-"); err != nil { + fmt.Println("defer changeFileMode function failed") + } + }() + hwlog.RunLog.Infof("ascend docker runtime starting") + hwlog.OpLog.Infof("%v ascend docker runtime starting, try to setup container", logPrefixWords) + if !mindxcheckutils.StringChecker(strings.Join(os.Args, " "), 0, + maxCommandLength, mindxcheckutils.DefaultWhiteList+" ") { + hwlog.RunLog.Errorf("%v ascend docker runtime args check failed", logPrefixWords) + hwlog.OpLog.Errorf("%v ascend docker runtime args check failed", logPrefixWords) + log.Fatal("command error") + } + if err = doProcess(); err != nil { + hwlog.RunLog.Errorf("%v docker runtime failed: %v", logPrefixWords, err) + hwlog.OpLog.Errorf("%v docker runtime failed: %v", logPrefixWords, err) + log.Fatal(err) + } +} diff --git a/runtime/main_test.go b/runtime/main_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8838313213968a3842ec261775c7194ebfda5e69 --- /dev/null +++ b/runtime/main_test.go @@ -0,0 +1,213 @@ +/* Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package main +package main + +import ( + "os" + "testing" + + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/prashantv/gostub" +) + +func TestArgsIsCreate(t *testing.T) { + t.Log("进入测试用例") + + testArgs := []string{"create", "--bundle", "."} + stub := gostub.Stub(&os.Args, testArgs) + defer stub.Reset() + + stub.Stub(&execRunc, func() error { + t.Log("execute stub") + return nil + }) + + if err := doProcess(); err != nil { + t.Fatalf("%v", err) + } +} + +func TestArgsIsCreateCase1(t *testing.T) { + t.Log("进入测试用例") + + testArgs := []string{"create", "--bundle"} + stub := gostub.Stub(&os.Args, testArgs) + defer stub.Reset() + + stub.Stub(&execRunc, func() error { + t.Log("execute stub") + return nil + }) + + if err := doProcess(); err == nil { + t.Fatalf("%v", err) + } +} + +func TestArgsIsCreateCase2(t *testing.T) { + t.Log("进入测试用例") + + testArgs := []string{"create", "--bundle", ""} + stub := gostub.Stub(&os.Args, testArgs) + defer stub.Reset() + + stub.Stub(&execRunc, func() error { + t.Log("execute stub") + return nil + }) + + if err := doProcess(); err != nil { + t.Fatalf("%v", err) + } +} + +func TestArgsIsCreateCase3(t *testing.T) { + t.Log("进入测试用例") + + if err := os.Mkdir("./test", 0655); err != nil { + } + f, err := os.Create("./test/config.json") + defer f.Close() + if err != nil { + } + testArgs := []string{"create", "--bundle", "./test"} + stub := gostub.Stub(&os.Args, testArgs) + defer stub.Reset() + + stub.Stub(&execRunc, func() error { + t.Log("execute stub") + return nil + }) + + if err := doProcess(); err == nil { + t.Fatalf("%v", err) + } +} + +func TestArgsIsCreateCase4(t *testing.T) { + t.Log("进入测试用例") + + if err := os.Mkdir("./test", 0655); err != nil { + } + f, err := os.Create("./test/config.json") + defer f.Close() + if err != nil { + } + testArgs := []string{"spec", "--bundle", "./test"} + stub := gostub.Stub(&os.Args, testArgs) + defer stub.Reset() + + stub.Stub(&execRunc, func() error { + t.Log("execute stub") + return nil + }) + + if err := doProcess(); err == nil { + t.Fatalf("%v", err) + } +} + +func TestModifySpecFile(t *testing.T) { + if err := modifySpecFile("./test/config.json"); err != nil { + t.Log("run modifySpecFile failed") + } +} + +func TestModifySpecFileCase1(t *testing.T) { + file := "./test" + if err := os.Mkdir("./test", 0400); err != nil { + + } + + if err := modifySpecFile(file); err != nil { + t.Log("run modifySpecFile failed") + } + if err := os.Remove(file); err != nil { + + } +} + +func TestModifySpecFileCase2(t *testing.T) { + file := "./test.json" + f, err := os.Create(file) + defer f.Close() + if err != nil { + t.Log("create file error") + } + + if err := modifySpecFile(file); err != nil { + t.Log("run modifySpecFile failed") + } + if err := os.Remove(file); err != nil { + + } +} + +func TestModifySpecFileCase3(t *testing.T) { + file := "./test_spec.json" + if err := modifySpecFile(file); err != nil { + t.Log("run modifySpecFile failed") + } +} + +func TestAddHook(t *testing.T) { + var specArgs = &specs.Spec{} + if err := addHook(specArgs); err != nil { + } +} + +func TestAddHookCase1(t *testing.T) { + var specArgs = &specs.Spec{} + stub := gostub.Stub(&hookCliPath, ".") + defer stub.Reset() + + if err := addHook(specArgs); err != nil { + } +} + +func TestAddHookCase2(t *testing.T) { + var specArgs = &specs.Spec{} + stub := gostub.Stub(&hookCliPath, ".") + defer stub.Reset() + stub.Stub(&hookDefaultFile, ".") + if err := addHook(specArgs); err != nil { + } +} + +func TestAddHookCase3(t *testing.T) { + file := "/usr/local/bin/ascend-docker-hook" + filenew := "/usr/local/bin/ascend-docker-hook-1" + + if err := os.Rename(file, filenew); err != nil { + t.Log("rename ", file) + } + var specArgs = &specs.Spec{} + if err := addHook(specArgs); err != nil { + } + if err := os.Rename(filenew, file); err != nil { + t.Log("rename ", file) + } +} + +func TestExecRunc(t *testing.T) { + stub := gostub.Stub(&dockerRuncName, "abc-runc") + stub.Stub(&runcName, "runc123") + defer stub.Reset() + + if err := execRunc(); err != nil { + + } +}