From 36fa4a762ea1d5ae7ee19542d5df3f435ef8fdc2 Mon Sep 17 00:00:00 2001 From: 19974907894 Date: Tue, 1 Aug 2023 10:52:37 +0800 Subject: [PATCH] support systemd cgroup driver Signed-off-by: 19974907894 --- Dockerfile | 3 +- go.mod | 1 + go.sum | 2 + hack/rubik-daemonset.yaml | 1 + pkg/common/constant/constant.go | 14 ++ pkg/common/util/file.go | 13 ++ pkg/core/typedef/cgroup_driver.go | 188 ++++++++++++++++++ pkg/core/typedef/rawpod.go | 94 +++++++-- pkg/rubik/rubik.go | 10 +- .../github.com/alessio/shellescape/.gitignore | 28 +++ .../alessio/shellescape/.golangci.yml | 59 ++++++ .../alessio/shellescape/.goreleaser.yml | 54 +++++ vendor/github.com/alessio/shellescape/AUTHORS | 1 + .../alessio/shellescape/CODE_OF_CONDUCT.md | 76 +++++++ vendor/github.com/alessio/shellescape/LICENSE | 21 ++ .../github.com/alessio/shellescape/README.md | 61 ++++++ .../alessio/shellescape/shellescape.go | 66 ++++++ vendor/modules.txt | 3 + 18 files changed, 672 insertions(+), 23 deletions(-) create mode 100644 pkg/core/typedef/cgroup_driver.go create mode 100644 vendor/github.com/alessio/shellescape/.gitignore create mode 100644 vendor/github.com/alessio/shellescape/.golangci.yml create mode 100644 vendor/github.com/alessio/shellescape/.goreleaser.yml create mode 100644 vendor/github.com/alessio/shellescape/AUTHORS create mode 100644 vendor/github.com/alessio/shellescape/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/alessio/shellescape/LICENSE create mode 100644 vendor/github.com/alessio/shellescape/README.md create mode 100644 vendor/github.com/alessio/shellescape/shellescape.go diff --git a/Dockerfile b/Dockerfile index bbaf598..046bc36 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ -FROM scratch +# need nsenter cmd to determine kubelet cgroup driver is systemd or cgroupfs +FROM ubuntu:20.04 COPY ./build/rubik /rubik ENTRYPOINT ["/rubik"] diff --git a/go.mod b/go.mod index fe03728..caf7a67 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module isula.org/rubik go 1.17 require ( + github.com/alessio/shellescape v1.4.2 github.com/cyphar/filepath-securejoin v0.2.3 github.com/google/cadvisor v0.47.1 github.com/google/uuid v1.1.2 diff --git a/go.sum b/go.sum index 2eeecdd..7ab521c 100644 --- a/go.sum +++ b/go.sum @@ -66,6 +66,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy 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/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0= +github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= diff --git a/hack/rubik-daemonset.yaml b/hack/rubik-daemonset.yaml index 246d45e..24134c0 100644 --- a/hack/rubik-daemonset.yaml +++ b/hack/rubik-daemonset.yaml @@ -86,6 +86,7 @@ spec: capabilities: add: - SYS_ADMIN + - SYS_PTRACE resources: limits: memory: 200Mi diff --git a/pkg/common/constant/constant.go b/pkg/common/constant/constant.go index 2df0b81..8f05bce 100644 --- a/pkg/common/constant/constant.go +++ b/pkg/common/constant/constant.go @@ -34,8 +34,22 @@ const ( const ( // KubepodsCgroup is kubepods root cgroup KubepodsCgroup = "kubepods" + // KubepodSystemd is kubepods root systemd + KubepodSystemd = "kubepods.slice/" + // KubeBurstableSystemd is kubepods burstable systemd + KubeBurstableSystemd = "kubepods-burstable.slice/" + // KubeBesteffortSystemd is kubepods besteffort systemd + KubeBesteffortSystemd = "kubepods-besteffort.slice/" + //KubePodSystemdPrefix is pod systemd guaranteed name prefix + KubePodSystemdPrefix = "kubepods-pod" + //KubeBesteffortPodSystemdPrefix is pod systemd bestEffort name prefix + KubeBesteffortPodSystemdPrefix = "kubepods-besteffort-pod" + //KubeBurstablePodSystemdPrefix is pod systemd burstable name prefix + KubeBurstablePodSystemdPrefix = "kubepods-burstable-pod" // PodCgroupNamePrefix is pod cgroup name prefix PodCgroupNamePrefix = "pod" + // PodSystemdSuffix is pod systemd name suffix + PodSystemdSuffix = ".slice" // NodeNameEnvKey is node name environment variable key NodeNameEnvKey = "RUBIK_NODE_NAME" ) diff --git a/pkg/common/util/file.go b/pkg/common/util/file.go index 970a181..a57922e 100644 --- a/pkg/common/util/file.go +++ b/pkg/common/util/file.go @@ -16,6 +16,7 @@ package util import ( "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -116,6 +117,18 @@ func ReadFile(path string) ([]byte, error) { return ioutil.ReadFile(path) } +// ReadFileNoStat uses io.ReadAll to read the entire file's content. +func ReadFileNoStat(path string) ([]byte, error) { + const maxSize = 1024 * 512 + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + reader := io.LimitReader(f, maxSize) + return io.ReadAll(reader) +} + // WriteFile writes a file, if the file does not exist, create the file (including the upper directory) func WriteFile(path, content string) error { if IsDir(path) { diff --git a/pkg/core/typedef/cgroup_driver.go b/pkg/core/typedef/cgroup_driver.go new file mode 100644 index 0000000..0a89e2b --- /dev/null +++ b/pkg/core/typedef/cgroup_driver.go @@ -0,0 +1,188 @@ +// Copyright (C) 2022, KylinSoft Co., Ltd. +// rubik licensed under the Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +// PURPOSE. +// See the Mulan PSL v2 for more details. +// Author: Xilong Pan +// Create: 2023-08-01 +// Description: This file defines cgroup driver + +// Package typedef defines core struct and methods for rubik +package typedef + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "os/exec" + "path" + "path/filepath" + "strconv" + "strings" + + "github.com/alessio/shellescape" + + "isula.org/rubik/pkg/common/constant" + "isula.org/rubik/pkg/common/util" +) + +const ( + Cgroupfs = "cgroupfs" + Systemd = "systemd" + ProcRootDir = "/proc/" +) + +var cgroupDriver string + +// Analyse Kubelet's cgroup driver +// 1. obtain cgroup driver from compare cgroupname +// 2. obtain cgroup driver from proc arg --cgroup-driver +// 3. obtain cgroup driver from proc arg config file +func AnalyseCgroupDriver() (string, error) { + isCgroupKubeDir := util.PathExist(filepath.Join(constant.DefaultCgroupRoot, "cpu", constant.KubepodsCgroup)) + isSystemdKubeDir := util.PathExist(filepath.Join(constant.DefaultCgroupRoot, "cpu", constant.KubepodSystemd)) + if isCgroupKubeDir != isSystemdKubeDir { + if isSystemdKubeDir { + return Systemd, nil + } + return Cgroupfs, nil + } + + pids, err := getPids("kubelet") + if err != nil || len(pids) == 0 { + return "", fmt.Errorf("could not find pid for kubelet:%v", err) + } + procArgs, err := ProcArgsFromCmdLine(ProcRootDir, pids[0]) + if err != nil || len(procArgs) <= 1 { + return "", fmt.Errorf("failed to obtain parameters for kubelet: %v", err) + } + if CgroupDriver := ParseProcArgs(procArgs, "--cgroup-driver"); CgroupDriver != "" { + return CgroupDriver, nil + } + Config := ParseProcArgs(procArgs, "--config") + if Config == "" { + return Cgroupfs, nil + } + + fileBuf, err := ExecCmdByNsenter(Config) + if err != nil { + return "", fmt.Errorf("unable to read the configuration file for kubelet(%s): %v", Config, err) + } + r := bufio.NewReader(bytes.NewBuffer(fileBuf)) + for { + buf, err := r.ReadBytes('\n') + if err != nil { + if err == io.EOF { + break + } + fmt.Println("err = ", err) + } + fields := strings.Fields(string(buf)) + key := fields[0][:len(fields[0])-1] + if key == "cgroupDriver" { + return strings.TrimSpace(fields[1]), nil + } + } + fmt.Printf("The Cgroup driver is not specified in the kubelet configuration file, use the default value: '%s'", Cgroupfs) + return Cgroupfs, nil +} + +func ParseProcArgs(args []string, part string) string { + var data string + for _, e := range args { + if strings.Contains(e, part) { + fmt.Printf("\nstring:%v\n", strings.Split(e, "=")[1]) + data = strings.Split(e, "=")[1] + break + } + } + return data +} + +// ProcArgsFromCmdLine returns the command line parameters of the process. +func ProcArgsFromCmdLine(proc string, pid int) ([]string, error) { + bs, err := util.ReadFileNoStat(path.Join(proc, strconv.Itoa(pid), "cmdline")) + if err != nil { + return nil, err + } + if len(bs) < 1 { + return []string{}, nil + } + return strings.Split(string(bytes.TrimRight(bs, string("\x00"))), string(byte(0))), nil +} + +// If running in a container, execute the command through "nsenter -- mount=/proc/1/ns/mnt ${cmds}". +func ExecCmdByNsenter(filename string) ([]byte, error) { + prefix := []string{} + //Escape arbitrary strings for safe use as command line arguments + safefile := strings.Join([]string{shellescape.Quote(filename)}, " ") + prefix = append(prefix, "nsenter", fmt.Sprintf("--mount=%s", path.Join(ProcRootDir, "/1/ns/mnt")), "cat", safefile) + var errBuf bytes.Buffer + cmd := exec.Command(prefix[0], prefix[1:]...) + cmd.Stderr = &errBuf + val, err := cmd.Output() + if err != nil { + return val, fmt.Errorf("nsenter command('cat %s') failed: %v, stderr: %s", cmd, err, errBuf.String()) + } + return val, nil +} + +func getPids(name string) ([]int, error) { + pids := []int{} + dir, err := os.ReadDir("/proc") + if err != nil { + return nil, err + } + for _, proc := range dir { + procName := proc.Name() + pid, err := strconv.ParseUint(procName, 10, 64) + if err != nil { + continue + } + cmd, err := os.ReadFile(filepath.Join("/proc", procName, "cmdline")) + if err == nil { + cmdbytes := bytes.Split(cmd, []byte{0}) + cmdname := filepath.Base(string(cmdbytes[0])) + if cmdname == name { + pids = append(pids, int(pid)) + continue + } + } + exe, err := os.Readlink(filepath.Join("/proc", procName, "exe")) + if err != nil { + continue + } + if filepath.Base(exe) == name { + pids = append(pids, int(pid)) + } + } + return pids, nil +} + +func DetectCgroupDriver(nodeName string) error { + driver, err := AnalyseCgroupDriver() + if err != nil { + fmt.Println(fmt.Errorf("guess Kubelet cgroup driver failed: %v", err).Error()) + return err + } + if Validate(driver) { + cgroupDriver = driver + fmt.Printf("cgroupDriver: %v", cgroupDriver) + return nil + } + return nil +} + +func GetCgroupDriver() string { + return cgroupDriver +} + +func Validate(s string) bool { + return s == Cgroupfs || s == Systemd +} diff --git a/pkg/core/typedef/rawpod.go b/pkg/core/typedef/rawpod.go index 59dfb59..5fa6ce8 100644 --- a/pkg/core/typedef/rawpod.go +++ b/pkg/core/typedef/rawpod.go @@ -26,11 +26,19 @@ import ( ) const ( - configHashAnnotationKey = "kubernetes.io/config.hash" + configHashAnnotationKey = "kubernetes.io/config.hash" + dockerPrefix = "docker://" + containerdPrefix = "containerd://" + dockerContainerPrefix = "docker-" + containerdContainerPrefix = "cri-containerd-" + containerSuffix = ".scope" + // RUNNING means the Pod is in the running phase RUNNING = corev1.PodRunning ) +var containerEngine string + type ( // RawContainer is kubernetes contaienr structure RawContainer struct { @@ -93,25 +101,51 @@ func (pod *RawPod) CgroupPath() string { } qosClassPath := "" - switch pod.Status.QOSClass { - case corev1.PodQOSGuaranteed: - case corev1.PodQOSBurstable: - qosClassPath = strings.ToLower(string(corev1.PodQOSBurstable)) - case corev1.PodQOSBestEffort: - qosClassPath = strings.ToLower(string(corev1.PodQOSBestEffort)) - default: - return "" + if GetCgroupDriver() == Cgroupfs { + switch pod.Status.QOSClass { + case corev1.PodQOSGuaranteed: + case corev1.PodQOSBurstable: + qosClassPath = strings.ToLower(string(corev1.PodQOSBurstable)) + case corev1.PodQOSBestEffort: + qosClassPath = strings.ToLower(string(corev1.PodQOSBestEffort)) + default: + return "" + } + /* + example: + 1. Burstable: pod requests are less than the value of limits and not 0; + kubepods/burstable/pod34152897-dbaf-11ea-8cb9-0653660051c3 + 2. BestEffort: pod requests and limits are both 0; + kubepods/bestEffort/pod34152897-dbaf-11ea-8cb9-0653660051c3 + 3. Guaranteed: pod requests are equal to the value set by limits; + kubepods/pod34152897-dbaf-11ea-8cb9-0653660051c3 + */ + return filepath.Join(constant.KubepodsCgroup, qosClassPath, constant.PodCgroupNamePrefix+id) + } else { + podId := "" + switch pod.Status.QOSClass { + case corev1.PodQOSGuaranteed: + podId = constant.KubePodSystemdPrefix + strings.ReplaceAll(id, "-", "_") + constant.PodSystemdSuffix + case corev1.PodQOSBurstable: + qosClassPath = strings.ToLower(string(constant.KubeBurstableSystemd)) + podId = constant.KubeBurstablePodSystemdPrefix + strings.ReplaceAll(id, "-", "_") + constant.PodSystemdSuffix + case corev1.PodQOSBestEffort: + qosClassPath = strings.ToLower(string(constant.KubeBesteffortSystemd)) + podId = constant.KubeBesteffortPodSystemdPrefix + strings.ReplaceAll(id, "-", "_") + constant.PodSystemdSuffix + default: + return "" + } + /* + example: + 1. Burstable: pod requests are less than the value of limits and not 0; + kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod09a60690_404b_4ba1_a4a0_5c24bab98275.slice + 2. BestEffort: pod requests and limits are both 0; + kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod09a60690_404b_4ba1_a4a0_5c24bab98275.slice + 3. Guaranteed: pod requests are equal to the value set by limits; + kubepods.slice/kubepods-pod34152897_dbaf_11ea_8cb9_0653660051c3.slice + */ + return filepath.Join(constant.KubepodSystemd, qosClassPath, podId) } - /* - example: - 1. Burstable: pod requests are less than the value of limits and not 0; - kubepods/burstable/pod34152897-dbaf-11ea-8cb9-0653660051c3 - 2. BestEffort: pod requests and limits are both 0; - kubepods/bestEffort/pod34152897-dbaf-11ea-8cb9-0653660051c3 - 3. Guaranteed: pod requests are equal to the value set by limits; - kubepods/pod34152897-dbaf-11ea-8cb9-0653660051c3 - */ - return filepath.Join(constant.KubepodsCgroup, qosClassPath, constant.PodCgroupNamePrefix+id) } // ListRawContainers returns all RawContainers in the RawPod @@ -125,6 +159,14 @@ func (pod *RawPod) ListRawContainers() map[string]*RawContainer { nameRawContainersMap[containerStatus.Name] = &RawContainer{ status: containerStatus, } + if containerEngine == "" { + if strings.HasPrefix(containerStatus.ContainerID, dockerPrefix) { + containerEngine = "docker" + } else if strings.HasPrefix(containerStatus.ContainerID, containerdPrefix) { + containerEngine = "containerd" + } + } + } for _, container := range pod.Spec.Containers { cont, ok := nameRawContainersMap[container.Name] @@ -152,7 +194,19 @@ func (pod *RawPod) ExtractContainerInfos() map[string]*ContainerInfo { if id == "" || err != nil { continue } - idContainersMap[id] = NewContainerInfo(id, podCgroupPath, rawContainer) + if GetCgroupDriver() == Cgroupfs { + idContainersMap[id] = NewContainerInfo(id, podCgroupPath, rawContainer) + } else { + var containerdId string + switch containerEngine { + case "docker": + containerdId = dockerContainerPrefix + id + containerSuffix + case "containerd": + containerdId = containerdContainerPrefix + id + containerSuffix + } + idContainersMap[id] = NewContainerInfo(containerdId, podCgroupPath, rawContainer) + } + } return idContainersMap } diff --git a/pkg/rubik/rubik.go b/pkg/rubik/rubik.go index 3864956..e9715a1 100644 --- a/pkg/rubik/rubik.go +++ b/pkg/rubik/rubik.go @@ -30,6 +30,7 @@ import ( "isula.org/rubik/pkg/config" "isula.org/rubik/pkg/core/publisher" "isula.org/rubik/pkg/core/trigger" + "isula.org/rubik/pkg/core/typedef" "isula.org/rubik/pkg/core/typedef/cgroup" "isula.org/rubik/pkg/informer" "isula.org/rubik/pkg/podmanager" @@ -127,10 +128,15 @@ func runAgent(ctx context.Context) error { // 3. enable cgroup system cgroup.InitMountDir(c.Agent.CgroupRoot) - // 4. init service components + // 4. determine cgroupdriver is systemd or cgroupfs + nodeName := os.Getenv(constant.NodeNameEnvKey) + typedef.DetectCgroupDriver(nodeName) + + // 5. init service components services.InitServiceComponents(defaultRubikFeature) - // 5. Create and run the agent + // 6. Create and run the agent + agent, err := NewAgent(c) if err != nil { return fmt.Errorf("error new agent: %v", err) diff --git a/vendor/github.com/alessio/shellescape/.gitignore b/vendor/github.com/alessio/shellescape/.gitignore new file mode 100644 index 0000000..4ba7c2d --- /dev/null +++ b/vendor/github.com/alessio/shellescape/.gitignore @@ -0,0 +1,28 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +.idea/ + +escargs diff --git a/vendor/github.com/alessio/shellescape/.golangci.yml b/vendor/github.com/alessio/shellescape/.golangci.yml new file mode 100644 index 0000000..836dabb --- /dev/null +++ b/vendor/github.com/alessio/shellescape/.golangci.yml @@ -0,0 +1,59 @@ +# run: +# # timeout for analysis, e.g. 30s, 5m, default is 1m +# timeout: 5m + +linters: + disable-all: true + enable: + - bodyclose + - dogsled + - goconst + - gocritic + - gofmt + - goimports + - gosec + - gosimple + - govet + - ineffassign + - misspell + - prealloc + - exportloopref + - revive + - staticcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - misspell + - wsl + +issues: + exclude-rules: + - text: "Use of weak random number generator" + linters: + - gosec + - text: "comment on exported var" + linters: + - golint + - text: "don't use an underscore in package name" + linters: + - golint + - text: "ST1003:" + linters: + - stylecheck + # FIXME: Disabled until golangci-lint updates stylecheck with this fix: + # https://github.com/dominikh/go-tools/issues/389 + - text: "ST1016:" + linters: + - stylecheck + +linters-settings: + dogsled: + max-blank-identifiers: 3 + maligned: + # print struct with more effective memory layout or not, false by default + suggest-new: true + +run: + tests: false diff --git a/vendor/github.com/alessio/shellescape/.goreleaser.yml b/vendor/github.com/alessio/shellescape/.goreleaser.yml new file mode 100644 index 0000000..0915eb8 --- /dev/null +++ b/vendor/github.com/alessio/shellescape/.goreleaser.yml @@ -0,0 +1,54 @@ +# This is an example goreleaser.yaml file with some sane defaults. +# Make sure to check the documentation at http://goreleaser.com +before: + hooks: + # You may remove this if you don't use go modules. + - go mod download + # you may remove this if you don't need go generate + - go generate ./... +builds: + - env: + - CGO_ENABLED=0 + - >- + {{- if eq .Os "darwin" }} + {{- if eq .Arch "amd64"}}CC=o64-clang{{- end }} + {{- if eq .Arch "arm64"}}CC=aarch64-apple-darwin20.2-clang{{- end }} + {{- end }} + {{- if eq .Os "windows" }} + {{- if eq .Arch "amd64" }}CC=x86_64-w64-mingw32-gcc{{- end }} + {{- end }} + main: ./cmd/escargs + goos: + - linux + - windows + - darwin + - freebsd + goarch: + - amd64 + - arm64 + - arm + goarm: + - 6 + - 7 + goamd64: + - v2 + - v3 + ignore: + - goos: darwin + goarch: 386 + - goos: linux + goarch: arm + goarm: 7 + - goarm: mips64 + - gomips: hardfloat + - goamd64: v4 +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ .Tag }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/vendor/github.com/alessio/shellescape/AUTHORS b/vendor/github.com/alessio/shellescape/AUTHORS new file mode 100644 index 0000000..4a647a6 --- /dev/null +++ b/vendor/github.com/alessio/shellescape/AUTHORS @@ -0,0 +1 @@ +Alessio Treglia diff --git a/vendor/github.com/alessio/shellescape/CODE_OF_CONDUCT.md b/vendor/github.com/alessio/shellescape/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..e8eda60 --- /dev/null +++ b/vendor/github.com/alessio/shellescape/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at alessio@debian.org. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/vendor/github.com/alessio/shellescape/LICENSE b/vendor/github.com/alessio/shellescape/LICENSE new file mode 100644 index 0000000..9f76067 --- /dev/null +++ b/vendor/github.com/alessio/shellescape/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Alessio Treglia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/alessio/shellescape/README.md b/vendor/github.com/alessio/shellescape/README.md new file mode 100644 index 0000000..910bb25 --- /dev/null +++ b/vendor/github.com/alessio/shellescape/README.md @@ -0,0 +1,61 @@ +![Build](https://github.com/alessio/shellescape/workflows/Build/badge.svg) +[![GoDoc](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/alessio/shellescape?tab=overview) +[![sourcegraph](https://sourcegraph.com/github.com/alessio/shellescape/-/badge.svg)](https://sourcegraph.com/github.com/alessio/shellescape) +[![codecov](https://codecov.io/gh/alessio/shellescape/branch/master/graph/badge.svg)](https://codecov.io/gh/alessio/shellescape) +[![Coverage](https://gocover.io/_badge/github.com/alessio/shellescape)](https://gocover.io/github.com/alessio/shellescape) +[![Go Report Card](https://goreportcard.com/badge/github.com/alessio/shellescape)](https://goreportcard.com/report/github.com/alessio/shellescape) + +# shellescape +Escape arbitrary strings for safe use as command line arguments. +## Contents of the package + +This package provides the `shellescape.Quote()` function that returns a +shell-escaped copy of a string. This functionality could be helpful +in those cases where it is known that the output of a Go program will +be appended to/used in the context of shell programs' command line arguments. + +This work was inspired by the Python original package +[shellescape](https://pypi.python.org/pypi/shellescape). + +## Usage + +The following snippet shows a typical unsafe idiom: + +```go +package main + +import ( + "fmt" + "os" +) + +func main() { + fmt.Printf("ls -l %s\n", os.Args[1]) +} +``` +_[See in Go Playground](https://play.golang.org/p/Wj2WoUfH_d)_ + +Especially when creating pipeline of commands which might end up being +executed by a shell interpreter, it is particularly unsafe to not +escape arguments. + +`shellescape.Quote()` comes in handy and to safely escape strings: + +```go +package main + +import ( + "fmt" + "os" + + "gopkg.in/alessio/shellescape.v1" +) + +func main() { + fmt.Printf("ls -l %s\n", shellescape.Quote(os.Args[1])) +} +``` +_[See in Go Playground](https://play.golang.org/p/HJ_CXgSrmp)_ + +## The escargs utility +__escargs__ reads lines from the standard input and prints shell-escaped versions. Unlinke __xargs__, blank lines on the standard input are not discarded. diff --git a/vendor/github.com/alessio/shellescape/shellescape.go b/vendor/github.com/alessio/shellescape/shellescape.go new file mode 100644 index 0000000..dc34a55 --- /dev/null +++ b/vendor/github.com/alessio/shellescape/shellescape.go @@ -0,0 +1,66 @@ +/* +Package shellescape provides the shellescape.Quote to escape arbitrary +strings for a safe use as command line arguments in the most common +POSIX shells. + +The original Python package which this work was inspired by can be found +at https://pypi.python.org/pypi/shellescape. +*/ +package shellescape // "import gopkg.in/alessio/shellescape.v1" + +/* +The functionality provided by shellescape.Quote could be helpful +in those cases where it is known that the output of a Go program will +be appended to/used in the context of shell programs' command line arguments. +*/ + +import ( + "regexp" + "strings" + "unicode" +) + +var pattern *regexp.Regexp + +func init() { + pattern = regexp.MustCompile(`[^\w@%+=:,./-]`) +} + +// Quote returns a shell-escaped version of the string s. The returned value +// is a string that can safely be used as one token in a shell command line. +func Quote(s string) string { + if len(s) == 0 { + return "''" + } + + if pattern.MatchString(s) { + return "'" + strings.ReplaceAll(s, "'", "'\"'\"'") + "'" + } + + return s +} + +// QuoteCommand returns a shell-escaped version of the slice of strings. +// The returned value is a string that can safely be used as shell command arguments. +func QuoteCommand(args []string) string { + l := make([]string, len(args)) + + for i, s := range args { + l[i] = Quote(s) + } + + return strings.Join(l, " ") +} + +// StripUnsafe remove non-printable runes, e.g. control characters in +// a string that is meant for consumption by terminals that support +// control characters. +func StripUnsafe(s string) string { + return strings.Map(func(r rune) rune { + if unicode.IsPrint(r) { + return r + } + + return -1 + }, s) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f9e8a91..4a49f3a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,3 +1,6 @@ +# github.com/alessio/shellescape v1.4.2 +## explicit; go 1.14 +github.com/alessio/shellescape # github.com/checkpoint-restore/go-criu/v5 v5.3.0 ## explicit; go 1.13 github.com/checkpoint-restore/go-criu/v5 -- Gitee