From 34d4aff7cf3746582d698ee9e4a42046e89a0704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B8=A3=E6=B2=BC?= Date: Wed, 17 Jul 2024 14:55:19 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=20=E3=80=90=E4=BF=AE=E6=94=B9=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20Modification=E3=80=91=E4=BF=AE=E5=A4=8Dhook=20dt?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hook/main.go | 357 +--------------- hook/process/process.go | 381 ++++++++++++++++++ .../{main_test.go => process/process_test.go} | 42 +- 3 files changed, 411 insertions(+), 369 deletions(-) create mode 100644 hook/process/process.go rename hook/{main_test.go => process/process_test.go} (82%) diff --git a/hook/main.go b/hook/main.go index 8355ca8..74f572c 100644 --- a/hook/main.go +++ b/hook/main.go @@ -12,371 +12,26 @@ limitations under the License. */ -// Package main +// Package main is the main entry. package main import ( - "bufio" "context" - "encoding/json" "fmt" "log" "os" - "path" - "path/filepath" "strings" - "syscall" - "github.com/opencontainers/runtime-spec/specs-go" "huawei.com/npu-exporter/v5/common-utils/hwlog" + "ascend-docker-runtime/hook/process" "ascend-docker-runtime/mindxcheckutils" ) const ( - loggingPrefix = "ascend-docker-hook" - runLogPath = "/var/log/ascend-docker-runtime/hook-run.log" - ascendRuntimeOptions = "ASCEND_RUNTIME_OPTIONS" - ascendRuntimeMounts = "ASCEND_RUNTIME_MOUNTS" - ascendVisibleDevices = "ASCEND_VISIBLE_DEVICES" - ascendAllowLink = "ASCEND_ALLOW_LINK" - ascendDockerCli = "ascend-docker-cli" - defaultAscendDockerCli = "/usr/local/bin/ascend-docker-cli" - configDir = "/etc/ascend-docker-runtime.d" - baseConfig = "base" - configFileSuffix = "list" - - kvPairSize = 2 - maxCommandLength = 65535 -) - -var ( - containerConfigInputStream = os.Stdin - doExec = syscall.Exec - ascendDockerCliName = ascendDockerCli - defaultAscendDockerCliName = defaultAscendDockerCli + loggingPrefix = "ascend-docker-hook" ) -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 - const fileMaxSize = 2 - runLogConfig := hwlog.LogConfig{ - LogFileName: runLogPath, - LogLevel: 0, - MaxBackups: backups, - MaxAge: logMaxAge, - OnlyToFile: true, - FileMaxSize: fileMaxSize, - } - if err := hwlog.InitRunLogger(&runLogConfig, ctx); err != nil { - fmt.Printf("hwlog init failed, error is %v", err) - return err - } - return 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 { - hwlog.RunLog.Errorf("length of ASCEND_RUNTIME_OPTIONS value is invalid, its length: %v", len(runtimeOptions)) - return nil, fmt.Errorf("invalid runtime option, the length exceeds 128 characters") - } - - for _, option := range strings.Split(runtimeOptions, ",") { - option = strings.TrimSpace(option) - if !isRuntimeOptionValid(option) { - hwlog.RunLog.Errorf("value of ASCEND_RUNTIME_OPTIONS is not in valid option list, value: %v", option) - return nil, fmt.Errorf("invalid runtime option of invalid input value") - } - - parsedOptions = append(parsedOptions, option) - } - - return parsedOptions, nil -} - -func parseSoftLinkMode(allowLink string) (string, error) { - if allowLink == "True" { - return "True", nil - } - if allowLink == "" || allowLink == "False" { - return "False", nil - } - - return "", fmt.Errorf("invalid soft link option") -} - -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 { - splitNumber := 2 - for _, s := range data { - p := strings.SplitN(s, "=", splitNumber) - if len(p) != kvPairSize { - hwlog.RunLog.Errorf("env is not key-value mode, env: %v", s) - 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, containerConfig *containerConfig, fileMountList []string, - dirMountList []string, allowLink string) []string { - args := append([]string{cliPath}, - "--allow-link", allowLink, "--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) - } - - if visibleDevices := getValueByKey(containerConfig.Env, ascendVisibleDevices); visibleDevices == "" { - return nil - } - - 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) - } - - allowLink, err := parseSoftLinkMode(getValueByKey(containerConfig.Env, ascendAllowLink)) - if err != nil { - return fmt.Errorf("failed to parse soft link mode: %#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, containerConfig, fileMountList, dirMountList, allowLink) - if len(parsedOptions) > 0 { - args = append(args, "--options", strings.Join(parsedOptions, ",")) - } - hwlog.RunLog.Info("ascend docker hook success, will start cli") - if err := mindxcheckutils.ChangeRuntimeLogMode("hook-run-"); 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 { @@ -386,7 +41,7 @@ func main() { log.SetPrefix(loggingPrefix) ctx, _ := context.WithCancel(context.Background()) - if err := initLogModule(ctx); err != nil { + if err := process.InitLogModule(ctx); err != nil { log.Fatal(err) } logPrefixWords, err := mindxcheckutils.GetLogPrefix() @@ -400,11 +55,11 @@ func main() { }() hwlog.RunLog.Infof("%v ascend docker hook starting, try to setup container", logPrefixWords) if !mindxcheckutils.StringChecker(strings.Join(os.Args, " "), 0, - maxCommandLength, mindxcheckutils.DefaultWhiteList+" ") { + process.MaxCommandLength, mindxcheckutils.DefaultWhiteList+" ") { hwlog.RunLog.Errorf("%v ascend docker hook failed", logPrefixWords) log.Fatal("command error") } - if err := doPrestartHook(); err != nil { + if err := process.DoPrestartHook(); err != nil { hwlog.RunLog.Errorf("%v ascend docker hook failed: %#v", logPrefixWords, err) log.Fatal(fmt.Errorf("failed in runtime.doProcess: %#v", err)) } diff --git a/hook/process/process.go b/hook/process/process.go new file mode 100644 index 0000000..1f8a48c --- /dev/null +++ b/hook/process/process.go @@ -0,0 +1,381 @@ +/* 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 process arses the environment variables to obtain the files and directories to be mounted +// and transfers the files and directories to the CLI for mounting before the container is started. +package process + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "log" + "os" + "path" + "path/filepath" + "strings" + "syscall" + + "github.com/opencontainers/runtime-spec/specs-go" + "huawei.com/npu-exporter/v5/common-utils/hwlog" + + "ascend-docker-runtime/mindxcheckutils" +) + +const ( + runLogPath = "/var/log/ascend-docker-runtime/hook-run.log" + ascendRuntimeOptions = "ASCEND_RUNTIME_OPTIONS" + ascendRuntimeMounts = "ASCEND_RUNTIME_MOUNTS" + ascendVisibleDevices = "ASCEND_VISIBLE_DEVICES" + ascendAllowLink = "ASCEND_ALLOW_LINK" + ascendDockerCli = "ascend-docker-cli" + defaultAscendDockerCli = "/usr/local/bin/ascend-docker-cli" + configDir = "/etc/ascend-docker-runtime.d" + baseConfig = "base" + configFileSuffix = "list" + + kvPairSize = 2 + // MaxCommandLength is the max length of command. + 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 +} + +// InitLogModule initializes some logging configuration. +func InitLogModule(ctx context.Context) error { + const backups = 2 + const logMaxAge = 365 + const fileMaxSize = 2 + runLogConfig := hwlog.LogConfig{ + LogFileName: runLogPath, + LogLevel: 0, + MaxBackups: backups, + MaxAge: logMaxAge, + OnlyToFile: true, + FileMaxSize: fileMaxSize, + } + if err := hwlog.InitRunLogger(&runLogConfig, ctx); err != nil { + fmt.Printf("hwlog init failed, error is %v", err) + return err + } + return 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 { + hwlog.RunLog.Errorf("length of ASCEND_RUNTIME_OPTIONS value is invalid, its length: %v", len(runtimeOptions)) + return nil, fmt.Errorf("invalid runtime option, the length exceeds 128 characters") + } + + for _, option := range strings.Split(runtimeOptions, ",") { + option = strings.TrimSpace(option) + if !isRuntimeOptionValid(option) { + hwlog.RunLog.Errorf("value of ASCEND_RUNTIME_OPTIONS is not in valid option list, value: %v", option) + return nil, fmt.Errorf("invalid runtime option of invalid input value") + } + + parsedOptions = append(parsedOptions, option) + } + + return parsedOptions, nil +} + +func parseSoftLinkMode(allowLink string) (string, error) { + if allowLink == "True" { + return "True", nil + } + if allowLink == "" || allowLink == "False" { + return "False", nil + } + + return "", fmt.Errorf("invalid soft link option") +} + +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 { + splitNumber := 2 + for _, s := range data { + p := strings.SplitN(s, "=", splitNumber) + if len(p) != kvPairSize { + hwlog.RunLog.Errorf("env is not key-value mode, env: %v", s) + 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, containerConfig *containerConfig, fileMountList []string, + dirMountList []string, allowLink string) []string { + args := append([]string{cliPath}, + "--allow-link", allowLink, "--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 +} + +// DoPrestartHook parses the environment variables in the container to obtain the files and directories to be mounted. +func DoPrestartHook() error { + containerConfig, err := getContainerConfig() + if err != nil { + return fmt.Errorf("failed to get container config: %#v", err) + } + + if visibleDevices := getValueByKey(containerConfig.Env, ascendVisibleDevices); visibleDevices == "" { + return nil + } + + 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) + } + + allowLink, err := parseSoftLinkMode(getValueByKey(containerConfig.Env, ascendAllowLink)) + if err != nil { + return fmt.Errorf("failed to parse soft link mode: %#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, containerConfig, fileMountList, dirMountList, allowLink) + if len(parsedOptions) > 0 { + args = append(args, "--options", strings.Join(parsedOptions, ",")) + } + hwlog.RunLog.Info("ascend docker hook success, will start cli") + if err := mindxcheckutils.ChangeRuntimeLogMode("hook-run-"); 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 +} diff --git a/hook/main_test.go b/hook/process/process_test.go similarity index 82% rename from hook/main_test.go rename to hook/process/process_test.go index 8f52ca5..aa2a824 100644 --- a/hook/main_test.go +++ b/hook/process/process_test.go @@ -13,22 +13,26 @@ */ // Package main -package main +package process import ( - "github.com/prashantv/gostub" + "context" "os" "os/exec" "testing" + + "github.com/prashantv/gostub" ) const ( - pidSample = 123 - fileMode0600 os.FileMode = 0600 + pidSample = 123 + fileMode0600 os.FileMode = 0600 + ascendVisibleDeviceTestStr = "ASCEND_VISIBLE_DEVICES=0-3,5,7" + configFile = "config.json" ) func TestDoPrestartHookCase1(t *testing.T) { - if err := doPrestartHook(); err != nil { + if err := DoPrestartHook(); err != nil { t.Log("failed") } } @@ -41,7 +45,7 @@ func TestDoPrestartHookCase2(t *testing.T) { } stub := gostub.StubFunc(&getContainerConfig, &conCfg, nil) defer stub.Reset() - if err := doPrestartHook(); err != nil { + if err := DoPrestartHook(); err != nil { t.Log("failed") } } @@ -54,7 +58,7 @@ func TestDoPrestartHookCase3(t *testing.T) { } stub := gostub.StubFunc(&getContainerConfig, &conCfg, nil) defer stub.Reset() - if err := doPrestartHook(); err != nil { + if err := DoPrestartHook(); err != nil { t.Log("failed") } } @@ -68,11 +72,15 @@ func TestDoPrestartHookCase4(t *testing.T) { "ASCEND_RUNTIME_OPTIONS=VIRTUAL,NODRV", }, } + err := InitLogModule(context.Background()) + if err != nil { + t.Log("failed") + } stub := gostub.StubFunc(&getContainerConfig, &conCfg, nil) defer stub.Reset() stub.Stub(&ascendDockerCliName, "") stub.StubFunc(&doExec, nil) - if err := doPrestartHook(); err != nil { + if err := DoPrestartHook(); err != nil { t.Log("failed") } } @@ -86,20 +94,20 @@ func TestDoPrestartHookCase5(t *testing.T) { conCfg := containerConfig{ Pid: pidSample, Rootfs: ".", - Env: []string{"ASCEND_VISIBLE_DEVICES=0-3,5,7"}, + Env: []string{ascendVisibleDeviceTestStr}, } 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 { + if err := DoPrestartHook(); err != nil { t.Log("failed") } } func TestGetValueByKeyCase1(t *testing.T) { - data := []string{"ASCEND_VISIBLE_DEVICES=0-3,5,7"} + data := []string{ascendVisibleDeviceTestStr} word := "ASCEND_VISIBLE_DEVICES" expectVal := "0-3,5,7" actualVal := getValueByKey(data, word) @@ -124,7 +132,7 @@ func TestGetValueByKeyCase2(t *testing.T) { } func TestGetValueByKeyCase3(t *testing.T) { - data := []string{"ASCEND_VISIBLE_DEVICES=0-3,5,7"} + data := []string{ascendVisibleDeviceTestStr} word := "ASCEND_VISIBLE_DEVICE" expectVal := "" actualVal := getValueByKey(data, word) @@ -160,20 +168,18 @@ func TestParseOciSpecFileCase2(t *testing.T) { } 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) + defer os.Remove(configFile) + _, err := parseOciSpecFile(configFile) 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") @@ -183,8 +189,8 @@ func TestGetContainerConfig(t *testing.T) { t.Log("exception", err) } }() - defer os.Remove(file) - stateFile, err := os.Open("config.json") + defer os.Remove(configFile) + stateFile, err := os.Open(configFile) if err != nil { t.Log("open file failed") } -- Gitee From 33d3c10422a5722aa25c1ccfaeb48e90ec84b99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B8=A3=E6=B2=BC?= Date: Wed, 17 Jul 2024 15:59:17 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=20=E3=80=90=E4=BF=AE=E6=94=B9=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20Modification=E3=80=91=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hook/process/process_test.go | 38 ++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/hook/process/process_test.go b/hook/process/process_test.go index aa2a824..2398e54 100644 --- a/hook/process/process_test.go +++ b/hook/process/process_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/prashantv/gostub" + "github.com/stretchr/testify/assert" ) const ( @@ -31,12 +32,13 @@ const ( configFile = "config.json" ) +// TestDoPrestartHookCase1 test function DoPrestartHook func TestDoPrestartHookCase1(t *testing.T) { - if err := DoPrestartHook(); err != nil { - t.Log("failed") - } + err := DoPrestartHook() + assert.NotNil(t, err) } +// TestDoPrestartHookCase2 test function DoPrestartHook func TestDoPrestartHookCase2(t *testing.T) { conCfg := containerConfig{ Pid: pidSample, @@ -45,11 +47,11 @@ func TestDoPrestartHookCase2(t *testing.T) { } stub := gostub.StubFunc(&getContainerConfig, &conCfg, nil) defer stub.Reset() - if err := DoPrestartHook(); err != nil { - t.Log("failed") - } + err := DoPrestartHook() + assert.NotNil(t, err) } +// TestDoPrestartHookCase3 test function DoPrestartHook func TestDoPrestartHookCase3(t *testing.T) { conCfg := containerConfig{ Pid: pidSample, @@ -58,11 +60,11 @@ func TestDoPrestartHookCase3(t *testing.T) { } stub := gostub.StubFunc(&getContainerConfig, &conCfg, nil) defer stub.Reset() - if err := DoPrestartHook(); err != nil { - t.Log("failed") - } + err := DoPrestartHook() + assert.Nil(t, err) } +// TestDoPrestartHookCase4 test function DoPrestartHook func TestDoPrestartHookCase4(t *testing.T) { conCfg := containerConfig{ Pid: pidSample, @@ -80,11 +82,11 @@ func TestDoPrestartHookCase4(t *testing.T) { defer stub.Reset() stub.Stub(&ascendDockerCliName, "") stub.StubFunc(&doExec, nil) - if err := DoPrestartHook(); err != nil { - t.Log("failed") - } + err = DoPrestartHook() + assert.NotNil(t, err) } +// TestDoPrestartHookCase5 test function DoPrestartHook func TestDoPrestartHookCase5(t *testing.T) { defer func() { if err := recover(); err != nil { @@ -101,11 +103,11 @@ func TestDoPrestartHookCase5(t *testing.T) { stub.Stub(&ascendDockerCliName, "clii") stub.Stub(&defaultAscendDockerCliName, "clii") stub.StubFunc(&doExec, nil) - if err := DoPrestartHook(); err != nil { - t.Log("failed") - } + err := DoPrestartHook() + assert.NotNil(t, err) } +// TestGetValueByKeyCase1 test the function getValueByKey func TestGetValueByKeyCase1(t *testing.T) { data := []string{ascendVisibleDeviceTestStr} word := "ASCEND_VISIBLE_DEVICES" @@ -116,6 +118,7 @@ func TestGetValueByKeyCase1(t *testing.T) { } } +// TestGetValueByKeyCase2 test the function getValueByKey func TestGetValueByKeyCase2(t *testing.T) { data := []string{"ASCEND_VISIBLE_DEVICES"} word := "ASCEND_VISIBLE_DEVICES" @@ -131,6 +134,7 @@ func TestGetValueByKeyCase2(t *testing.T) { } } +// TestGetValueByKeyCase3 test the function getValueByKey func TestGetValueByKeyCase3(t *testing.T) { data := []string{ascendVisibleDeviceTestStr} word := "ASCEND_VISIBLE_DEVICE" @@ -141,6 +145,7 @@ func TestGetValueByKeyCase3(t *testing.T) { } } +// TestParseOciSpecFileCase1 test the function parseOciSpecFile func TestParseOciSpecFileCase1(t *testing.T) { file := "file" _, err := parseOciSpecFile(file) @@ -149,6 +154,7 @@ func TestParseOciSpecFileCase1(t *testing.T) { } } +// TestParseOciSpecFileCase2 test the function parseOciSpecFile func TestParseOciSpecFileCase2(t *testing.T) { file := "file" f, err := os.Create(file) @@ -167,6 +173,7 @@ func TestParseOciSpecFileCase2(t *testing.T) { } } +// TestParseOciSpecFileCase3 test the function parseOciSpecFile func TestParseOciSpecFileCase3(t *testing.T) { cmd := exec.Command("runc", "spec") if err := cmd.Run(); err != nil { @@ -179,6 +186,7 @@ func TestParseOciSpecFileCase3(t *testing.T) { } } +// TestGetContainerConfig test the function getContainerConfig func TestGetContainerConfig(t *testing.T) { cmd := exec.Command("runc", "spec") if err := cmd.Run(); err != nil { -- Gitee From 78f91c6f8d72bc814ca5e8fa8e0b767278b77308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B8=A3=E6=B2=BC?= Date: Thu, 18 Jul 2024 10:13:48 +0800 Subject: [PATCH 03/11] =?UTF-8?q?=20=E3=80=90=E4=BF=AE=E6=94=B9=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20Modification=E3=80=91=E6=B7=BB=E5=8A=A0dt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/process/docker_process_test.go | 101 +++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/install/process/docker_process_test.go b/install/process/docker_process_test.go index 2742b87..1a85ccc 100644 --- a/install/process/docker_process_test.go +++ b/install/process/docker_process_test.go @@ -17,9 +17,16 @@ package process import ( "encoding/json" + "fmt" + "io" + "io/ioutil" "os" "reflect" "testing" + + "github.com/agiledragon/gomonkey/v2" + + "ascend-docker-runtime/mindxcheckutils" ) const ( @@ -152,3 +159,97 @@ func TestCreateJsonStrinRm(t *testing.T) { t.Fatalf(updateFailAndData, err, string(data)) } } + +type testDockerProcessArg struct { + Name string + Command []string + Want error + Want1 string +} + +func TestDockerProcess(t *testing.T) { + tests := getTestDockerProcessCases() + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + if tt.Name == "success case 4" { + patch := gomonkey.ApplyFunc(os.Stat, func(name string) (os.FileInfo, error) { + return &FileInfoMock{}, nil + }) + defer patch.Reset() + patchRealDirCheck := gomonkey.ApplyFunc(mindxcheckutils.RealDirChecker, func(path string, + checkParent, allowLink bool) (string, error) { + return "", nil + }) + defer patchRealDirCheck.Reset() + patchRealFileCheck := gomonkey.ApplyFunc(mindxcheckutils.RealFileChecker, func(path string, + checkParent, allowLink bool, size int) (string, error) { + fmt.Println("aaaaaa") + return "", nil + }) + defer patchRealFileCheck.Reset() + patchSize := gomonkey.ApplyMethod(reflect.TypeOf(&FileInfoMock{}), "Size", func(f *FileInfoMock) int64 { + return 1 + }) + defer patchSize.Reset() + patchClose := gomonkey.ApplyMethod(reflect.TypeOf(&os.File{}), "Close", func(_ *os.File) error { + return nil + }) + defer patchClose.Reset() + patchReadAll := gomonkey.ApplyFunc(ioutil.ReadAll, func(r io.Reader) ([]byte, error) { + testMap := map[string]interface{}{} + jsonBytes, err := json.Marshal(testMap) + if err != nil { + fmt.Println("Error marshaling map:", err) + return nil, nil + } + return jsonBytes, nil + }) + defer patchReadAll.Reset() + } + got, got1 := DockerProcess(tt.Command) + if !reflect.DeepEqual(got, tt.Want) { + t.Errorf("DockerProcess() got = %v, want %v", got, tt.Want) + } + if got1 != tt.Want1 { + t.Errorf("DockerProcess() got1 = %v, want %v", got1, tt.Want1) + } + }) + } +} + +func getTestDockerProcessCases() []testDockerProcessArg { + emptyStr := "" + addBehavior := "install" + rmBehavior := "uninstall" + srcFileTest := "old.json" + destFileTest := "aaa.txt.pid" + return []testDockerProcessArg{ + { + Name: "error param case 1", + Command: []string{"ins"}, + Want: fmt.Errorf("error param"), + }, + { + Name: "error param case 2", + Command: []string{"add"}, + Want: fmt.Errorf("error param"), + }, + { + Name: "file not exist case 3", + Command: []string{"rm", srcFileTest, emptyStr, emptyStr, emptyStr, emptyStr}, + Want: fmt.Errorf("create target file failed"), + Want1: rmBehavior, + }, + { + Name: "success case 4", + Command: []string{"add", srcFileTest, destFileTest, emptyStr, emptyStr, emptyStr, emptyStr}, + Want: fmt.Errorf("target file already existed"), + Want1: addBehavior, + }, + } +} + +// FileInfoMock is used to test +type FileInfoMock struct { + os.FileInfo +} -- Gitee From 146c836eabdefbe0162404f304abb19253953f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B8=A3=E6=B2=BC?= Date: Thu, 18 Jul 2024 10:17:59 +0800 Subject: [PATCH 04/11] =?UTF-8?q?=20=E3=80=90=E4=BF=AE=E6=94=B9=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20Modification=E3=80=91=E6=B7=BB=E5=8A=A0dt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/process/docker_process_test.go | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/install/process/docker_process_test.go b/install/process/docker_process_test.go index 1a85ccc..de015cc 100644 --- a/install/process/docker_process_test.go +++ b/install/process/docker_process_test.go @@ -161,10 +161,10 @@ func TestCreateJsonStrinRm(t *testing.T) { } type testDockerProcessArg struct { - Name string - Command []string - Want error - Want1 string + Name string + Command []string + WantErr error + WantResult string } func TestDockerProcess(t *testing.T) { @@ -207,11 +207,11 @@ func TestDockerProcess(t *testing.T) { defer patchReadAll.Reset() } got, got1 := DockerProcess(tt.Command) - if !reflect.DeepEqual(got, tt.Want) { - t.Errorf("DockerProcess() got = %v, want %v", got, tt.Want) + if !reflect.DeepEqual(got, tt.WantErr) { + t.Errorf("DockerProcess() got = %v, want %v", got, tt.WantErr) } - if got1 != tt.Want1 { - t.Errorf("DockerProcess() got1 = %v, want %v", got1, tt.Want1) + if got1 != tt.WantResult { + t.Errorf("DockerProcess() got1 = %v, want %v", got1, tt.WantResult) } }) } @@ -227,24 +227,24 @@ func getTestDockerProcessCases() []testDockerProcessArg { { Name: "error param case 1", Command: []string{"ins"}, - Want: fmt.Errorf("error param"), + WantErr: fmt.Errorf("error param"), }, { Name: "error param case 2", Command: []string{"add"}, - Want: fmt.Errorf("error param"), + WantErr: fmt.Errorf("error param"), }, { - Name: "file not exist case 3", - Command: []string{"rm", srcFileTest, emptyStr, emptyStr, emptyStr, emptyStr}, - Want: fmt.Errorf("create target file failed"), - Want1: rmBehavior, + Name: "file not exist case 3", + Command: []string{"rm", srcFileTest, emptyStr, emptyStr, emptyStr, emptyStr}, + WantErr: fmt.Errorf("create target file failed"), + WantResult: rmBehavior, }, { - Name: "success case 4", - Command: []string{"add", srcFileTest, destFileTest, emptyStr, emptyStr, emptyStr, emptyStr}, - Want: fmt.Errorf("target file already existed"), - Want1: addBehavior, + Name: "success case 4", + Command: []string{"add", srcFileTest, destFileTest, emptyStr, emptyStr, emptyStr, emptyStr}, + WantErr: fmt.Errorf("target file already existed"), + WantResult: addBehavior, }, } } -- Gitee From d7648f504cdbe500eec919f5770906a50ca0c946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B8=A3=E6=B2=BC?= Date: Thu, 18 Jul 2024 10:33:10 +0800 Subject: [PATCH 05/11] =?UTF-8?q?=20=E3=80=90=E4=BF=AE=E6=94=B9=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20Modification=E3=80=91=E6=B7=BB=E5=8A=A0dt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/process/docker_process_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/install/process/docker_process_test.go b/install/process/docker_process_test.go index de015cc..6bf92d5 100644 --- a/install/process/docker_process_test.go +++ b/install/process/docker_process_test.go @@ -110,7 +110,7 @@ func TestCreateJsonStringUpdateWithOtherParam(t *testing.T) { }, ` + defaultRuntime + `: "runc" }` - if fid, err := os.OpenFile("old.json", os.O_CREATE|os.O_RDWR|os.O_TRUNC, perm); err == nil { + if fid, err := os.OpenFile(oldJson, 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 { @@ -221,7 +221,6 @@ func getTestDockerProcessCases() []testDockerProcessArg { emptyStr := "" addBehavior := "install" rmBehavior := "uninstall" - srcFileTest := "old.json" destFileTest := "aaa.txt.pid" return []testDockerProcessArg{ { @@ -236,13 +235,13 @@ func getTestDockerProcessCases() []testDockerProcessArg { }, { Name: "file not exist case 3", - Command: []string{"rm", srcFileTest, emptyStr, emptyStr, emptyStr, emptyStr}, + Command: []string{"rm", oldJson, emptyStr, emptyStr, emptyStr, emptyStr}, WantErr: fmt.Errorf("create target file failed"), WantResult: rmBehavior, }, { Name: "success case 4", - Command: []string{"add", srcFileTest, destFileTest, emptyStr, emptyStr, emptyStr, emptyStr}, + Command: []string{"add", oldJson, destFileTest, emptyStr, emptyStr, emptyStr, emptyStr}, WantErr: fmt.Errorf("target file already existed"), WantResult: addBehavior, }, -- Gitee From 3bf81c2c53ea2b83784f7a89fd786b78961ac8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B8=A3=E6=B2=BC?= Date: Thu, 18 Jul 2024 15:49:07 +0800 Subject: [PATCH 06/11] =?UTF-8?q?=20=E3=80=90=E4=BF=AE=E6=94=B9=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20Modification=E3=80=91add=20dt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/process/containerd_process_test.go | 287 +++++++++++++++++++++ install/process/docker_process_test.go | 20 +- mindxcheckutils/mindxcheckutils_test.go | 21 ++ 3 files changed, 318 insertions(+), 10 deletions(-) create mode 100644 install/process/containerd_process_test.go diff --git a/install/process/containerd_process_test.go b/install/process/containerd_process_test.go new file mode 100644 index 0000000..8f4a825 --- /dev/null +++ b/install/process/containerd_process_test.go @@ -0,0 +1,287 @@ +package process + +import ( + "context" + "github.com/containerd/containerd/services/server/config" + "github.com/pelletier/go-toml" + "os" + "reflect" + "testing" + + "github.com/agiledragon/gomonkey/v2" + "huawei.com/npu-exporter/v5/common-utils/hwlog" + + "ascend-docker-runtime/mindxcheckutils" +) + +// TestContainerdProcess tests the function ContainerdProcess +func TestContainerdProcess(t *testing.T) { + tests := getTestDockerProcessCases() + initTestLog(t) + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + if tt.Name == "success case 4" { + patch := gomonkey.ApplyFunc(os.Stat, func(name string) (os.FileInfo, error) { + return &FileInfoMock{}, nil + }) + defer patch.Reset() + patchRealDirCheck := gomonkey.ApplyFunc(mindxcheckutils.RealDirChecker, func(path string, + checkParent, allowLink bool) (string, error) { + return "", nil + }) + defer patchRealDirCheck.Reset() + patchRealFileCheck := gomonkey.ApplyFunc(mindxcheckutils.RealFileChecker, func(path string, + checkParent, allowLink bool, size int) (string, error) { + return "", nil + }) + defer patchRealFileCheck.Reset() + } + got, got1 := ContainerdProcess(tt.Command) + if (got == nil) == tt.WantErr { + t.Errorf("ContainerdProcess() got = %v, want %v", got, tt.WantErr) + } + if got1 != tt.WantResult { + t.Errorf("ContainerdProcess() got1 = %v, want %v", got1, tt.WantResult) + } + }) + } +} + +func initTestLog(t *testing.T) { + backups := 2 + logMaxAge := 365 + fileMaxSize := 2 + runLogConfig := hwlog.LogConfig{ + LogFileName: "./test/run.log", + LogLevel: 0, + MaxBackups: backups, + FileMaxSize: fileMaxSize, + MaxAge: logMaxAge, + } + if err := hwlog.InitRunLogger(&runLogConfig, context.Background()); err != nil { + t.Fatalf("hwlog init failed, error is %v", err) + } +} + +// TestIsCgroupV2 tests the function isCgroupV2 +func TestIsCgroupV2(t *testing.T) { + tests := []struct { + name string + cgroupInfo string + want bool + wantErr bool + }{ + { + name: "v2 case 1", + cgroupInfo: cgroupV2InfoStr, + want: true, + wantErr: false, + }, + { + name: "v1 case 2", + cgroupInfo: "", + want: false, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := isCgroupV2(tt.cgroupInfo) + if (err != nil) != tt.wantErr { + t.Errorf("isCgroupV2() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("isCgroupV2() got = %v, want %v", got, tt.want) + } + }) + } +} + +// TestEditContainerdConfig tests the function editContainerdConfig +func TestEditContainerdConfig(t *testing.T) { + tests := []struct { + name string + srcFilePath string + runtimeFilePath string + destFilePath string + action string + cgroupInfo string + wantErr bool + }{ + { + name: "v2 failed case 1", + action: addCommand, + cgroupInfo: cgroupV2InfoStr, + wantErr: true, + }, + { + name: "v1 failed case 2", + action: addCommand, + cgroupInfo: "", + wantErr: true, + }, + } + patch := gomonkey.ApplyFunc(config.LoadConfig, func(path string, out *config.Config) error { + return nil + }) + defer patch.Reset() + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := editContainerdConfig(tt.srcFilePath, tt.runtimeFilePath, tt.destFilePath, tt.action, tt.cgroupInfo); (err != nil) != tt.wantErr { + t.Errorf("editContainerdConfig() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +// TestChangeCgroupV2BinaryNameConfig tests the function changeCgroupV2BinaryNameConfig +func TestChangeCgroupV2BinaryNameConfig(t *testing.T) { + testMap := map[string]interface{}{ + containerdKey: map[string]interface{}{ + runtimesKey: map[string]interface{}{ + runcKey: map[string]interface{}{ + runcOptionsKey: map[string]interface{}{ + binaryNameKey: "", + }, + }, + }, + }, + } + testTree, err := toml.TreeFromMap(testMap) + if err != nil { + t.Fatalf("convert map to tree failed, error: %v", err) + } + tests := []struct { + name string + cfg *config.Config + binaryName string + wantErr bool + }{ + { + name: "success case 1", + cfg: &config.Config{ + Plugins: map[string]toml.Tree{ + v1RuntimeTypeFisrtLevelPlugin: *testTree, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := changeCgroupV2BinaryNameConfig(tt.cfg, tt.binaryName); (err != nil) != tt.wantErr { + t.Errorf("changeCgroupV2BinaryNameConfig() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +// TestChangeCgroupV1Config tests the function changeCgroupV1Config +func TestChangeCgroupV1Config(t *testing.T) { + testMapV1RuntimeTypeFisrtLevelPlugin := map[string]interface{}{ + containerdKey: map[string]interface{}{ + runtimesKey: map[string]interface{}{ + runcKey: map[string]interface{}{}, + }, + }, + } + testTreeV1RuntimeTypeFisrtLevelPlugin, err := toml.TreeFromMap(testMapV1RuntimeTypeFisrtLevelPlugin) + if err != nil { + t.Fatalf("convert map to tree failed, error: %v", err) + } + testMapV1RuntimeType := map[string]interface{}{} + testTreeV1RuntimeType, err := toml.TreeFromMap(testMapV1RuntimeType) + if err != nil { + t.Fatalf("convert map to tree failed, error: %v", err) + } + tests := []struct { + name string + cfg *config.Config + runtimeValue string + runtimeType string + wantErr bool + }{ + { + name: "success case 1", + cfg: &config.Config{ + Plugins: map[string]toml.Tree{ + v1RuntimeType: *testTreeV1RuntimeType, + v1RuntimeTypeFisrtLevelPlugin: *testTreeV1RuntimeTypeFisrtLevelPlugin, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := changeCgroupV1Config(tt.cfg, tt.runtimeValue, tt.runtimeType); (err != nil) != tt.wantErr { + t.Errorf("changeCgroupV1Config() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +// TestGetMap tests the function getMap +func TestGetMap(t *testing.T) { + tests := []struct { + name string + input interface{} + key string + want interface{} + wantErr bool + }{ + { + name: "invalid case 1", + input: "", + wantErr: true, + }, + { + name: "invalid case 2", + input: map[string]interface{}{}, + key: v1NeedChangeKeyRuntimeType, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := getMap(tt.input, tt.key) + if (err != nil) != tt.wantErr { + t.Errorf("getMap() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("getMap() got = %v, want %v", got, tt.want) + } + }) + } +} + +// TestWriteContainerdConfigToFile tests the function writeContainerdConfigToFile +func TestWriteContainerdConfigToFile(t *testing.T) { + tests := []struct { + name string + cfg config.Config + destFilePath string + wantErr bool + }{ + { + name: "marshal failed case 1", + cfg: config.Config{}, + wantErr: true, + }, + { + name: "success case 2", + cfg: config.Config{}, + destFilePath: "config.toml", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := writeContainerdConfigToFile(tt.cfg, tt.destFilePath); (err != nil) != tt.wantErr { + t.Errorf("writeContainerdConfigToFile() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/install/process/docker_process_test.go b/install/process/docker_process_test.go index 6bf92d5..994abb9 100644 --- a/install/process/docker_process_test.go +++ b/install/process/docker_process_test.go @@ -160,13 +160,14 @@ func TestCreateJsonStrinRm(t *testing.T) { } } -type testDockerProcessArg struct { +type testProcessArg struct { Name string Command []string - WantErr error + WantErr bool WantResult string } +// TestDockerProcess tests the function DockerProcess func TestDockerProcess(t *testing.T) { tests := getTestDockerProcessCases() for _, tt := range tests { @@ -183,7 +184,6 @@ func TestDockerProcess(t *testing.T) { defer patchRealDirCheck.Reset() patchRealFileCheck := gomonkey.ApplyFunc(mindxcheckutils.RealFileChecker, func(path string, checkParent, allowLink bool, size int) (string, error) { - fmt.Println("aaaaaa") return "", nil }) defer patchRealFileCheck.Reset() @@ -207,7 +207,7 @@ func TestDockerProcess(t *testing.T) { defer patchReadAll.Reset() } got, got1 := DockerProcess(tt.Command) - if !reflect.DeepEqual(got, tt.WantErr) { + if (got == nil) == tt.WantErr { t.Errorf("DockerProcess() got = %v, want %v", got, tt.WantErr) } if got1 != tt.WantResult { @@ -217,32 +217,32 @@ func TestDockerProcess(t *testing.T) { } } -func getTestDockerProcessCases() []testDockerProcessArg { +func getTestDockerProcessCases() []testProcessArg { emptyStr := "" addBehavior := "install" rmBehavior := "uninstall" destFileTest := "aaa.txt.pid" - return []testDockerProcessArg{ + return []testProcessArg{ { Name: "error param case 1", Command: []string{"ins"}, - WantErr: fmt.Errorf("error param"), + WantErr: true, }, { Name: "error param case 2", Command: []string{"add"}, - WantErr: fmt.Errorf("error param"), + WantErr: true, }, { Name: "file not exist case 3", Command: []string{"rm", oldJson, emptyStr, emptyStr, emptyStr, emptyStr}, - WantErr: fmt.Errorf("create target file failed"), + WantErr: true, WantResult: rmBehavior, }, { Name: "success case 4", Command: []string{"add", oldJson, destFileTest, emptyStr, emptyStr, emptyStr, emptyStr}, - WantErr: fmt.Errorf("target file already existed"), + WantErr: true, WantResult: addBehavior, }, } diff --git a/mindxcheckutils/mindxcheckutils_test.go b/mindxcheckutils/mindxcheckutils_test.go index 15d4b0a..9788653 100644 --- a/mindxcheckutils/mindxcheckutils_test.go +++ b/mindxcheckutils/mindxcheckutils_test.go @@ -176,3 +176,24 @@ func removeTmpDir(t *testing.T, tmpDir string) { t.Logf("removeall %v", tmpDir) } } + +// TestChangeRuntimeLogMode tests the function ChangeRuntimeLogMode +func TestChangeRuntimeLogMode(t *testing.T) { + tests := []struct { + name string + runLog string + wantErr bool + }{ + { + name: "case 1", + runLog: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ChangeRuntimeLogMode(tt.runLog); (err != nil) != tt.wantErr { + t.Errorf("ChangeRuntimeLogMode() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} -- Gitee From d748178dbe2afe4d41adb7de725d7ff09c7564f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B8=A3=E6=B2=BC?= Date: Thu, 18 Jul 2024 16:11:08 +0800 Subject: [PATCH 07/11] =?UTF-8?q?=20=E3=80=90=E4=BF=AE=E6=94=B9=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20Modification=E3=80=91add=20dt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime/process/process_test.go | 75 ++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/runtime/process/process_test.go b/runtime/process/process_test.go index a2d98a5..09c29a5 100644 --- a/runtime/process/process_test.go +++ b/runtime/process/process_test.go @@ -27,6 +27,7 @@ import ( "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/assert" + "ascend-docker-runtime/mindxcheckutils" "ascend-docker-runtime/runtime/dcmi" ) @@ -190,13 +191,6 @@ func TestModifySpecFileCase3(t *testing.T) { } } -func TestAddHook(t *testing.T) { - var specArgs = &specs.Spec{} - if err := addHook(specArgs, &deviceList); err != nil { - t.Logf("addHook failed, error: %v", err) - } -} - func TestAddHookCase1(t *testing.T) { var specArgs = &specs.Spec{} stub := gomonkey.ApplyGlobalVar(&hookCliPath, ".") @@ -544,3 +538,70 @@ func TestAddDevice(t *testing.T) { assert.Nil(t, err) assert.Contains(t, spec.Linux.Devices[0].Path, devPath) } + +// TestAddHook tests the function addHook +func TestAddHook(t *testing.T) { + patch := gomonkey.ApplyFunc(os.Stat, func(name string) (os.FileInfo, error) { + return nil, nil + }) + defer patch.Reset() + patchRealFileCheck := gomonkey.ApplyFunc(mindxcheckutils.RealFileChecker, func(path string, + checkParent, allowLink bool, size int) (string, error) { + return "", nil + }) + defer patchRealFileCheck.Reset() + tests := []struct { + name string + spec *specs.Spec + deviceIdList *[]int + wantErr bool + }{ + { + name: "success case 1", + deviceIdList: &[]int{0}, + spec: &specs.Spec{ + Process: &specs.Process{ + Env: []string{ascendRuntimeOptions + "=VIRTUAL"}, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := addHook(tt.spec, tt.deviceIdList); (err != nil) != tt.wantErr { + t.Errorf("addHook() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_parseAscendDevices(t *testing.T) { + patchRealFileCheck := gomonkey.ApplyFunc(dcmi.GetChipName, func() (string, error) { + return "910", nil + }) + defer patchRealFileCheck.Reset() + tests := []struct { + name string + visibleDevices string + want []int + wantErr bool + }{ + { + name: "parseAscendDevices success case 1", + visibleDevices: "Ascend910-0", + want: []int{0}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parseAscendDevices(tt.visibleDevices) + if (err != nil) != tt.wantErr { + t.Errorf("parseAscendDevices() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equalf(t, tt.want, got, "parseAscendDevices(%v)", tt.visibleDevices) + }) + } +} -- Gitee From e1011c677834e4dc51718d1ba453f85029aed260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B8=A3=E6=B2=BC?= Date: Thu, 18 Jul 2024 16:14:13 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=20=E3=80=90=E4=BF=AE=E6=94=B9=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20Modification=E3=80=91add=20dt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime/process/process_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/process/process_test.go b/runtime/process/process_test.go index 09c29a5..7c2df48 100644 --- a/runtime/process/process_test.go +++ b/runtime/process/process_test.go @@ -576,7 +576,8 @@ func TestAddHook(t *testing.T) { } } -func Test_parseAscendDevices(t *testing.T) { +// TestParseAscendDevices tests the function parseAscendDevices +func TestParseAscendDevices(t *testing.T) { patchRealFileCheck := gomonkey.ApplyFunc(dcmi.GetChipName, func() (string, error) { return "910", nil }) -- Gitee From 3c2010528aeb1467548458bae7644d4477e22334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B8=A3=E6=B2=BC?= Date: Thu, 18 Jul 2024 16:23:02 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=20=E3=80=90=E4=BF=AE=E6=94=B9=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20Modification=E3=80=91add=20dt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/process/containerd_process_test.go | 10 +++-- runtime/process/process_test.go | 51 ++++++++++++++++++++-- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/install/process/containerd_process_test.go b/install/process/containerd_process_test.go index 8f4a825..45303fc 100644 --- a/install/process/containerd_process_test.go +++ b/install/process/containerd_process_test.go @@ -14,6 +14,10 @@ import ( "ascend-docker-runtime/mindxcheckutils" ) +const ( + convertMapToTreeFailedStr = "convert map to tree failed, error: %v" +) + // TestContainerdProcess tests the function ContainerdProcess func TestContainerdProcess(t *testing.T) { tests := getTestDockerProcessCases() @@ -150,7 +154,7 @@ func TestChangeCgroupV2BinaryNameConfig(t *testing.T) { } testTree, err := toml.TreeFromMap(testMap) if err != nil { - t.Fatalf("convert map to tree failed, error: %v", err) + t.Fatalf(convertMapToTreeFailedStr, err) } tests := []struct { name string @@ -188,12 +192,12 @@ func TestChangeCgroupV1Config(t *testing.T) { } testTreeV1RuntimeTypeFisrtLevelPlugin, err := toml.TreeFromMap(testMapV1RuntimeTypeFisrtLevelPlugin) if err != nil { - t.Fatalf("convert map to tree failed, error: %v", err) + t.Fatalf(convertMapToTreeFailedStr, err) } testMapV1RuntimeType := map[string]interface{}{} testTreeV1RuntimeType, err := toml.TreeFromMap(testMapV1RuntimeType) if err != nil { - t.Fatalf("convert map to tree failed, error: %v", err) + t.Fatalf(convertMapToTreeFailedStr, err) } tests := []struct { name string diff --git a/runtime/process/process_test.go b/runtime/process/process_test.go index 7c2df48..086e652 100644 --- a/runtime/process/process_test.go +++ b/runtime/process/process_test.go @@ -44,6 +44,7 @@ const ( bundleArgStr = "--bundle" execStubLog = "execute stub" configPath = "./test/config.json" + chipName = "910" ) var ( @@ -334,7 +335,6 @@ func TestGetDeviceTypeByChipName2(t *testing.T) { } func TestGetDeviceTypeByChipName3(t *testing.T) { - chipName := "910" devType := GetDeviceTypeByChipName(chipName) assert.EqualValues(t, Ascend910, devType) } @@ -478,7 +478,7 @@ func TestAddManagerDevice(t *testing.T) { defer statStub.Reset() dcmiStub := gomonkey.ApplyFunc(dcmi.GetChipName, func() (string, error) { - return "910", nil + return chipName, nil }) defer dcmiStub.Reset() @@ -579,7 +579,7 @@ func TestAddHook(t *testing.T) { // TestParseAscendDevices tests the function parseAscendDevices func TestParseAscendDevices(t *testing.T) { patchRealFileCheck := gomonkey.ApplyFunc(dcmi.GetChipName, func() (string, error) { - return "910", nil + return chipName, nil }) defer patchRealFileCheck.Reset() tests := []struct { @@ -606,3 +606,48 @@ func TestParseAscendDevices(t *testing.T) { }) } } + +// TestCheckVisibleDevice tests the function checkVisibleDevice +func TestCheckVisibleDevice(t *testing.T) { + patchRealFileCheck := gomonkey.ApplyFunc(dcmi.GetChipName, func() (string, error) { + return chipName, nil + }) + defer patchRealFileCheck.Reset() + tests := []struct { + name string + spec *specs.Spec + want []int + wantErr bool + }{ + { + name: "checkVisibleDevice success case 1", + spec: &specs.Spec{ + Process: &specs.Process{ + Env: []string{ascendVisibleDevices + "=Ascend910-0"}, + }, + }, + wantErr: false, + want: []int{0}, + }, + { + name: "checkVisibleDevice success case 2", + spec: &specs.Spec{ + Process: &specs.Process{ + Env: []string{ascendVisibleDevices + "=0"}, + }, + }, + wantErr: false, + want: []int{0}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := checkVisibleDevice(tt.spec) + if (err != nil) != tt.wantErr { + t.Errorf("checkVisibleDevice() error = %v, wantErr %v", err, tt.wantErr) + return + } + assert.Equalf(t, tt.want, got, "checkVisibleDevice(%v)", tt.spec) + }) + } +} -- Gitee From 159f1749cb7f99f8347697cd2229f7affaa10fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B8=A3=E6=B2=BC?= Date: Thu, 25 Jul 2024 14:28:18 +0800 Subject: [PATCH 10/11] =?UTF-8?q?=20=E3=80=90=E4=BF=AE=E6=94=B9=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20Modification=E3=80=91=E4=BF=AE=E6=94=B9dt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/process/containerd_process_test.go | 4 ++-- install/process/docker_process_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/install/process/containerd_process_test.go b/install/process/containerd_process_test.go index 45303fc..118d7a7 100644 --- a/install/process/containerd_process_test.go +++ b/install/process/containerd_process_test.go @@ -41,10 +41,10 @@ func TestContainerdProcess(t *testing.T) { defer patchRealFileCheck.Reset() } got, got1 := ContainerdProcess(tt.Command) - if (got == nil) == tt.WantErr { + if (got1 == nil) == tt.WantErr { t.Errorf("ContainerdProcess() got = %v, want %v", got, tt.WantErr) } - if got1 != tt.WantResult { + if got != tt.WantResult { t.Errorf("ContainerdProcess() got1 = %v, want %v", got1, tt.WantResult) } }) diff --git a/install/process/docker_process_test.go b/install/process/docker_process_test.go index 994abb9..5ae454e 100644 --- a/install/process/docker_process_test.go +++ b/install/process/docker_process_test.go @@ -207,10 +207,10 @@ func TestDockerProcess(t *testing.T) { defer patchReadAll.Reset() } got, got1 := DockerProcess(tt.Command) - if (got == nil) == tt.WantErr { + if (got1 == nil) == tt.WantErr { t.Errorf("DockerProcess() got = %v, want %v", got, tt.WantErr) } - if got1 != tt.WantResult { + if got != tt.WantResult { t.Errorf("DockerProcess() got1 = %v, want %v", got1, tt.WantResult) } }) -- Gitee From 06ba902f850cb969e5d051d586d86042bcb786a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B8=A3=E6=B2=BC?= Date: Fri, 26 Jul 2024 17:19:58 +0800 Subject: [PATCH 11/11] =?UTF-8?q?=20=E3=80=90=E4=BF=AE=E6=94=B9=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=20Modification=E3=80=91=E4=BF=AE=E6=94=B9=E6=A3=80?= =?UTF-8?q?=E8=A7=86=E6=84=8F=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install/process/containerd_process_test.go | 35 ---------------------- 1 file changed, 35 deletions(-) diff --git a/install/process/containerd_process_test.go b/install/process/containerd_process_test.go index 118d7a7..6859e47 100644 --- a/install/process/containerd_process_test.go +++ b/install/process/containerd_process_test.go @@ -67,41 +67,6 @@ func initTestLog(t *testing.T) { } } -// TestIsCgroupV2 tests the function isCgroupV2 -func TestIsCgroupV2(t *testing.T) { - tests := []struct { - name string - cgroupInfo string - want bool - wantErr bool - }{ - { - name: "v2 case 1", - cgroupInfo: cgroupV2InfoStr, - want: true, - wantErr: false, - }, - { - name: "v1 case 2", - cgroupInfo: "", - want: false, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := isCgroupV2(tt.cgroupInfo) - if (err != nil) != tt.wantErr { - t.Errorf("isCgroupV2() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("isCgroupV2() got = %v, want %v", got, tt.want) - } - }) - } -} - // TestEditContainerdConfig tests the function editContainerdConfig func TestEditContainerdConfig(t *testing.T) { tests := []struct { -- Gitee