From 90d99bcea64d84262065696b6119e7cd0ca853ce Mon Sep 17 00:00:00 2001 From: Kiefer Chang Date: Mon, 7 Jun 2021 11:47:33 +0800 Subject: [PATCH] Bump kind to 0.11.1 Skip setting `net.netfilter.nf_conntrack_max` sysctl parameter. Setting this parameter causes Kind cluster initialization failure on newer kernels. Please see https://github.com/kubernetes-sigs/kind/pull/2241 for more information. Signed-off-by: Kiefer Chang --- go.mod | 2 +- go.sum | 21 +- pkg/genswagger/main.go | 1 + .../github.com/alessio/shellescape/.gitignore | 4 + .../alessio/shellescape/.golangci.yml | 64 +++ .../alessio/shellescape/.goreleaser.yml | 33 ++ .../alessio/shellescape/.travis.yml | 4 - .../alessio/shellescape/CODE_OF_CONDUCT.md | 76 +++ .../github.com/alessio/shellescape/README.md | 15 +- .../alessio/shellescape/shellescape.go | 29 +- .../github.com/evanphx/json-patch/v5/LICENSE | 2 +- .../github.com/evanphx/json-patch/v5/merge.go | 44 +- .../github.com/evanphx/json-patch/v5/patch.go | 449 +++++++++++++++--- .../pelletier/go-toml/azure-pipelines.yml | 40 +- .../pelletier/go-toml/benchmark.json | 164 ------- .../github.com/pelletier/go-toml/benchmark.sh | 4 + .../pelletier/go-toml/benchmark.toml | 244 ---------- .../pelletier/go-toml/benchmark.yml | 121 ----- vendor/github.com/pelletier/go-toml/go.mod | 6 +- vendor/github.com/pelletier/go-toml/lexer.go | 100 ++-- .../github.com/pelletier/go-toml/marshal.go | 35 +- vendor/github.com/pelletier/go-toml/toml.go | 132 ++++- .../pelletier/go-toml/tomltree_create.go | 13 + .../pelletier/go-toml/tomltree_write.go | 2 +- vendor/gopkg.in/yaml.v3/.travis.yml | 17 - vendor/gopkg.in/yaml.v3/decode.go | 6 +- vendor/gopkg.in/yaml.v3/emitterc.go | 34 +- vendor/gopkg.in/yaml.v3/encode.go | 5 + vendor/gopkg.in/yaml.v3/scannerc.go | 28 +- vendor/gopkg.in/yaml.v3/yaml.go | 5 + vendor/modules.txt | 14 +- .../kind/pkg/apis/config/defaults/image.go | 2 +- .../kind/pkg/apis/config/v1alpha4/default.go | 20 +- .../kind/pkg/apis/config/v1alpha4/types.go | 19 +- .../config/v1alpha4/zz_generated.deepcopy.go | 7 + .../internal/create/actions/config/config.go | 61 ++- .../internal/create/actions/installcni/cni.go | 36 ++ .../create/actions/installstorage/storage.go | 2 +- .../create/actions/kubeadminit/init.go | 22 +- .../create/actions/kubeadmjoin/join.go | 4 +- .../actions/waitforready/waitforready.go | 4 +- .../pkg/cluster/internal/create/create.go | 38 +- .../pkg/cluster/internal/kubeadm/config.go | 85 +++- .../kubeconfig/internal/kubeconfig/encode.go | 2 +- .../kubeconfig/internal/kubeconfig/types.go | 6 +- .../cluster/internal/kubeconfig/kubeconfig.go | 2 +- .../pkg/cluster/internal/patch/mergepatch.go | 9 + .../cluster/internal/providers/common/logs.go | 1 + .../internal/providers/docker/network.go | 50 +- .../internal/providers/docker/provider.go | 65 ++- .../internal/providers/docker/provision.go | 15 +- .../cluster/internal/providers/docker/util.go | 37 +- .../internal/providers/podman/network.go | 135 ++++++ .../cluster/internal/providers/podman/node.go | 2 +- .../internal/providers/podman/provider.go | 136 +++++- .../internal/providers/podman/provision.go | 42 +- .../cluster/internal/providers/podman/util.go | 18 +- .../cluster/internal/providers/provider.go | 11 + .../kind/pkg/cluster/nodeutils/util.go | 2 +- .../sigs.k8s.io/kind/pkg/cluster/provider.go | 69 ++- vendor/sigs.k8s.io/kind/pkg/cmd/doc.go | 15 + vendor/sigs.k8s.io/kind/pkg/cmd/iostreams.go | 41 ++ .../kind/pkg/cmd/kind/version/version.go | 89 ++++ vendor/sigs.k8s.io/kind/pkg/cmd/logger.go | 47 ++ vendor/sigs.k8s.io/kind/pkg/errors/errors.go | 8 + .../internal/apis/config/convert_v1alpha4.go | 1 + .../kind/pkg/internal/apis/config/default.go | 25 +- .../kind/pkg/internal/apis/config/types.go | 17 +- .../kind/pkg/internal/apis/config/validate.go | 89 +++- .../apis/config/zz_generated.deepcopy.go | 7 + .../sigs.k8s.io/kind/pkg/internal/env/term.go | 8 +- 71 files changed, 2044 insertions(+), 919 deletions(-) create mode 100644 vendor/github.com/alessio/shellescape/.golangci.yml create mode 100644 vendor/github.com/alessio/shellescape/.goreleaser.yml delete mode 100644 vendor/github.com/alessio/shellescape/.travis.yml create mode 100644 vendor/github.com/alessio/shellescape/CODE_OF_CONDUCT.md delete mode 100644 vendor/github.com/pelletier/go-toml/benchmark.json delete mode 100644 vendor/github.com/pelletier/go-toml/benchmark.toml delete mode 100644 vendor/github.com/pelletier/go-toml/benchmark.yml delete mode 100644 vendor/gopkg.in/yaml.v3/.travis.yml create mode 100644 vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/network.go create mode 100644 vendor/sigs.k8s.io/kind/pkg/cmd/doc.go create mode 100644 vendor/sigs.k8s.io/kind/pkg/cmd/iostreams.go create mode 100644 vendor/sigs.k8s.io/kind/pkg/cmd/kind/version/version.go create mode 100644 vendor/sigs.k8s.io/kind/pkg/cmd/logger.go diff --git a/go.mod b/go.mod index 022ce66c6d..1096320ab5 100644 --- a/go.mod +++ b/go.mod @@ -100,6 +100,6 @@ require ( kubevirt.io/client-go v0.40.0 kubevirt.io/containerized-data-importer v1.31.0 kubevirt.io/kubevirt v0.40.0 - sigs.k8s.io/kind v0.9.0 + sigs.k8s.io/kind v0.11.1 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index e33d2567a5..55383a692e 100644 --- a/go.sum +++ b/go.sum @@ -133,8 +133,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alessio/shellescape v1.2.2 h1:8LnL+ncxhWT2TR00dfJRT25JWWrhkMZXneHVWnetDZg= -github.com/alessio/shellescape v1.2.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= @@ -366,8 +366,8 @@ github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.1.0 h1:B0aXl1o/1cP8NbviYiBMkcHBtUjIJ1/Ccg6b+SwCLQg= -github.com/evanphx/json-patch/v5 v5.1.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.2.0 h1:8ozOH5xxoMYDt5/u+yMTsVXydVCbTORFnOOoq2lumco= +github.com/evanphx/json-patch/v5 v5.2.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/exoscale/egoscale v0.12.3/go.mod h1:SHSox0l8ud/I8Q6joR7Oj96DFer0mdo1cQzb7dmZgro= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= @@ -1010,8 +1010,8 @@ github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= -github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= +github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= @@ -1567,7 +1567,6 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1801,8 +1800,8 @@ gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= @@ -1917,8 +1916,8 @@ sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZw sigs.k8s.io/controller-runtime v0.6.2/go.mod h1:vhcq/rlnENJ09SIRp3EveTaZ0yqH526hjf9iJdbUJ/E= sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA= sigs.k8s.io/controller-tools v0.4.0/go.mod h1:G9rHdZMVlBDocIxGkK3jHLWqcTMNvveypYJwrvYKjWU= -sigs.k8s.io/kind v0.9.0 h1:SoDlXq6pEc7dGagHULNRCCBYrLH6xOi7lqXTRXeAlg4= -sigs.k8s.io/kind v0.9.0/go.mod h1:cxKQWwmbtRDzQ+RNKnR6gZG6fjbeTtItp5cGf+ww+1Y= +sigs.k8s.io/kind v0.11.1 h1:pVzOkhUwMBrCB0Q/WllQDO3v14Y+o2V0tFgjTqIUjwA= +sigs.k8s.io/kind v0.11.1/go.mod h1:fRpgVhtqAWrtLB9ED7zQahUimpUXuG/iHT88xYqEGIA= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/kustomize/kyaml v0.4.0/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw= diff --git a/pkg/genswagger/main.go b/pkg/genswagger/main.go index 5eb2518ffd..02f4c5bf56 100644 --- a/pkg/genswagger/main.go +++ b/pkg/genswagger/main.go @@ -11,6 +11,7 @@ import ( "github.com/go-openapi/spec" "k8s.io/kube-openapi/pkg/builder" "k8s.io/kube-openapi/pkg/common" + _ "kubevirt.io/client-go/apis/snapshot/v1alpha1" "github.com/harvester/harvester/pkg/apis/harvesterhci.io/v1beta1" "github.com/harvester/harvester/pkg/genswagger/rest" diff --git a/vendor/github.com/alessio/shellescape/.gitignore b/vendor/github.com/alessio/shellescape/.gitignore index daf913b1b3..4ba7c2d137 100644 --- a/vendor/github.com/alessio/shellescape/.gitignore +++ b/vendor/github.com/alessio/shellescape/.gitignore @@ -22,3 +22,7 @@ _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 0000000000..cd4a17e442 --- /dev/null +++ b/vendor/github.com/alessio/shellescape/.golangci.yml @@ -0,0 +1,64 @@ +# run: +# # timeout for analysis, e.g. 30s, 5m, default is 1m +# timeout: 5m + +linters: + disable-all: true + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - goconst + - gocritic + - gofmt + - goimports + - golint + - gosec + - gosimple + - govet + - ineffassign + - interfacer + - maligned + - misspell + - prealloc + - scopelint + - staticcheck + - structcheck + - 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 0000000000..064c9374d7 --- /dev/null +++ b/vendor/github.com/alessio/shellescape/.goreleaser.yml @@ -0,0 +1,33 @@ +# 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 + main: ./cmd/escargs + goos: + - linux + - windows + - darwin +archives: + - replacements: + darwin: Darwin + linux: Linux + windows: Windows + 386: i386 + amd64: x86_64 +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/.travis.yml b/vendor/github.com/alessio/shellescape/.travis.yml deleted file mode 100644 index f5763c5aad..0000000000 --- a/vendor/github.com/alessio/shellescape/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -language: go - -go: - - 1.14 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 0000000000..e8eda60620 --- /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/README.md b/vendor/github.com/alessio/shellescape/README.md index ce9e33f42c..910bb253b9 100644 --- a/vendor/github.com/alessio/shellescape/README.md +++ b/vendor/github.com/alessio/shellescape/README.md @@ -1,7 +1,10 @@ -[![GoDoc](https://godoc.org/github.com/alessio/shellescape?status.svg)](https://godoc.org/github.com/alessio/shellescape) -[![Travis-CI Status](https://api.travis-ci.org/alessio/shellescape.png?branch=master)](http://travis-ci.org/#!/alessio/shellescape) +![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) -[![Coverage Status](https://coveralls.io/repos/github/alessio/shellescape/badge.svg?branch=master)](https://coveralls.io/github/alessio/shellescape?branch=master) +[![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 @@ -11,8 +14,8 @@ 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). +This work was inspired by the Python original package +[shellescape](https://pypi.python.org/pypi/shellescape). ## Usage @@ -33,7 +36,7 @@ func main() { _[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, tt is particularly unsafe to not +executed by a shell interpreter, it is particularly unsafe to not escape arguments. `shellescape.Quote()` comes in handy and to safely escape strings: diff --git a/vendor/github.com/alessio/shellescape/shellescape.go b/vendor/github.com/alessio/shellescape/shellescape.go index 56725d182a..dc34a556ae 100644 --- a/vendor/github.com/alessio/shellescape/shellescape.go +++ b/vendor/github.com/alessio/shellescape/shellescape.go @@ -17,6 +17,7 @@ be appended to/used in the context of shell programs' command line arguments. import ( "regexp" "strings" + "unicode" ) var pattern *regexp.Regexp @@ -31,9 +32,35 @@ func Quote(s string) string { if len(s) == 0 { return "''" } + if pattern.MatchString(s) { - return "'" + strings.Replace(s, "'", "'\"'\"'", -1) + "'" + 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/github.com/evanphx/json-patch/v5/LICENSE b/vendor/github.com/evanphx/json-patch/v5/LICENSE index 0eb9b72d84..df76d7d771 100644 --- a/vendor/github.com/evanphx/json-patch/v5/LICENSE +++ b/vendor/github.com/evanphx/json-patch/v5/LICENSE @@ -6,7 +6,7 @@ modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Evan Phoenix nor the names of its contributors diff --git a/vendor/github.com/evanphx/json-patch/v5/merge.go b/vendor/github.com/evanphx/json-patch/v5/merge.go index 14e8bb5ce3..7219316d66 100644 --- a/vendor/github.com/evanphx/json-patch/v5/merge.go +++ b/vendor/github.com/evanphx/json-patch/v5/merge.go @@ -27,21 +27,31 @@ func merge(cur, patch *lazyNode, mergeMerge bool) *lazyNode { } func mergeDocs(doc, patch *partialDoc, mergeMerge bool) { - for k, v := range *patch { + for k, v := range patch.obj { if v == nil { if mergeMerge { - (*doc)[k] = nil + idx := -1 + for i, key := range doc.keys { + if key == k { + idx = i + break + } + } + if idx == -1 { + doc.keys = append(doc.keys, k) + } + doc.obj[k] = nil } else { - delete(*doc, k) + _ = doc.remove(k, &ApplyOptions{}) } } else { - cur, ok := (*doc)[k] + cur, ok := doc.obj[k] if !ok || cur == nil { pruneNulls(v) - (*doc)[k] = v + _ = doc.set(k, v, &ApplyOptions{}) } else { - (*doc)[k] = merge(cur, v, mergeMerge) + _ = doc.set(k, merge(cur, v, mergeMerge), &ApplyOptions{}) } } } @@ -62,9 +72,9 @@ func pruneNulls(n *lazyNode) { } func pruneDocNulls(doc *partialDoc) *partialDoc { - for k, v := range *doc { + for k, v := range doc.obj { if v == nil { - delete(*doc, k) + _ = doc.remove(k, &ApplyOptions{}) } else { pruneNulls(v) } @@ -113,19 +123,19 @@ func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) { patchErr := json.Unmarshal(patchData, patch) - if _, ok := docErr.(*json.SyntaxError); ok { + if isSyntaxError(docErr) { return nil, errBadJSONDoc } - if _, ok := patchErr.(*json.SyntaxError); ok { + if isSyntaxError(patchErr) { return nil, errBadJSONPatch } - if docErr == nil && *doc == nil { + if docErr == nil && doc.obj == nil { return nil, errBadJSONDoc } - if patchErr == nil && *patch == nil { + if patchErr == nil && patch.obj == nil { return nil, errBadJSONPatch } @@ -162,6 +172,16 @@ func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) { return json.Marshal(doc) } +func isSyntaxError(err error) bool { + if _, ok := err.(*json.SyntaxError); ok { + return true + } + if _, ok := err.(*syntaxError); ok { + return true + } + return false +} + // resemblesJSONArray indicates whether the byte-slice "appears" to be // a JSON array or not. // False-positives are possible, as this function does not check the internal diff --git a/vendor/github.com/evanphx/json-patch/v5/patch.go b/vendor/github.com/evanphx/json-patch/v5/patch.go index e736759872..fc860aca38 100644 --- a/vendor/github.com/evanphx/json-patch/v5/patch.go +++ b/vendor/github.com/evanphx/json-patch/v5/patch.go @@ -24,6 +24,10 @@ var ( // AccumulatedCopySizeLimit limits the total size increase in bytes caused by // "copy" operations in a patch. AccumulatedCopySizeLimit int64 = 0 + startObject = json.Delim('{') + endObject = json.Delim('}') + startArray = json.Delim('[') + endArray = json.Delim(']') ) var ( @@ -32,11 +36,15 @@ var ( ErrUnknownType = errors.New("unknown object type") ErrInvalid = errors.New("invalid state detected") ErrInvalidIndex = errors.New("invalid index referenced") + + rawJSONArray = []byte("[]") + rawJSONObject = []byte("{}") + rawJSONNull = []byte("null") ) type lazyNode struct { raw *json.RawMessage - doc partialDoc + doc *partialDoc ary partialArray which int } @@ -47,20 +55,58 @@ type Operation map[string]*json.RawMessage // Patch is an ordered collection of Operations. type Patch []Operation -type partialDoc map[string]*lazyNode +type partialDoc struct { + keys []string + obj map[string]*lazyNode +} + type partialArray []*lazyNode type container interface { - get(key string) (*lazyNode, error) - set(key string, val *lazyNode) error - add(key string, val *lazyNode) error - remove(key string) error + get(key string, options *ApplyOptions) (*lazyNode, error) + set(key string, val *lazyNode, options *ApplyOptions) error + add(key string, val *lazyNode, options *ApplyOptions) error + remove(key string, options *ApplyOptions) error +} + +// ApplyOptions specifies options for calls to ApplyWithOptions. +// Use NewApplyOptions to obtain default values for ApplyOptions. +type ApplyOptions struct { + // SupportNegativeIndices decides whether to support non-standard practice of + // allowing negative indices to mean indices starting at the end of an array. + // Default to true. + SupportNegativeIndices bool + // AccumulatedCopySizeLimit limits the total size increase in bytes caused by + // "copy" operations in a patch. + AccumulatedCopySizeLimit int64 + // AllowMissingPathOnRemove indicates whether to fail "remove" operations when the target path is missing. + // Default to false. + AllowMissingPathOnRemove bool + // EnsurePathExistsOnAdd instructs json-patch to recursively create the missing parts of path on "add" operation. + // Default to false. + EnsurePathExistsOnAdd bool +} + +// NewApplyOptions creates a default set of options for calls to ApplyWithOptions. +func NewApplyOptions() *ApplyOptions { + return &ApplyOptions{ + SupportNegativeIndices: SupportNegativeIndices, + AccumulatedCopySizeLimit: AccumulatedCopySizeLimit, + AllowMissingPathOnRemove: false, + EnsurePathExistsOnAdd: false, + } } func newLazyNode(raw *json.RawMessage) *lazyNode { return &lazyNode{raw: raw, doc: nil, ary: nil, which: eRaw} } +func newRawMessage(buf []byte) *json.RawMessage { + ra := make(json.RawMessage, len(buf)) + copy(ra, buf) + return &ra +} + func (n *lazyNode) MarshalJSON() ([]byte, error) { switch n.which { case eRaw: @@ -82,6 +128,109 @@ func (n *lazyNode) UnmarshalJSON(data []byte) error { return nil } +func (n *partialDoc) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + if _, err := buf.WriteString("{"); err != nil { + return nil, err + } + for i, k := range n.keys { + if i > 0 { + if _, err := buf.WriteString(", "); err != nil { + return nil, err + } + } + key, err := json.Marshal(k) + if err != nil { + return nil, err + } + if _, err := buf.Write(key); err != nil { + return nil, err + } + if _, err := buf.WriteString(": "); err != nil { + return nil, err + } + value, err := json.Marshal(n.obj[k]) + if err != nil { + return nil, err + } + if _, err := buf.Write(value); err != nil { + return nil, err + } + } + if _, err := buf.WriteString("}"); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +type syntaxError struct { + msg string +} + +func (err *syntaxError) Error() string { + return err.msg +} + +func (n *partialDoc) UnmarshalJSON(data []byte) error { + if err := json.Unmarshal(data, &n.obj); err != nil { + return err + } + buffer := bytes.NewBuffer(data) + d := json.NewDecoder(buffer) + if t, err := d.Token(); err != nil { + return err + } else if t != startObject { + return &syntaxError{fmt.Sprintf("unexpected JSON token in document node: %s", t)} + } + for d.More() { + k, err := d.Token() + if err != nil { + return err + } + key, ok := k.(string) + if !ok { + return &syntaxError{fmt.Sprintf("unexpected JSON token as document node key: %s", k)} + } + if err := skipValue(d); err != nil { + return err + } + n.keys = append(n.keys, key) + } + return nil +} + +func skipValue(d *json.Decoder) error { + t, err := d.Token() + if err != nil { + return err + } + if t != startObject && t != startArray { + return nil + } + for d.More() { + if t == startObject { + // consume key token + if _, err := d.Token(); err != nil { + return err + } + } + if err := skipValue(d); err != nil { + return err + } + } + end, err := d.Token() + if err != nil { + return err + } + if t == startObject && end != endObject { + return &syntaxError{msg: "expected close object token"} + } + if t == startArray && end != endArray { + return &syntaxError{msg: "expected close object token"} + } + return nil +} + func deepCopy(src *lazyNode) (*lazyNode, int, error) { if src == nil { return nil, 0, nil @@ -91,14 +240,12 @@ func deepCopy(src *lazyNode) (*lazyNode, int, error) { return nil, 0, err } sz := len(a) - ra := make(json.RawMessage, sz) - copy(ra, a) - return newLazyNode(&ra), sz, nil + return newLazyNode(newRawMessage(a)), sz, nil } func (n *lazyNode) intoDoc() (*partialDoc, error) { if n.which == eDoc { - return &n.doc, nil + return n.doc, nil } if n.raw == nil { @@ -112,7 +259,7 @@ func (n *lazyNode) intoDoc() (*partialDoc, error) { } n.which = eDoc - return &n.doc, nil + return n.doc, nil } func (n *lazyNode) intoAry() (*partialArray, error) { @@ -202,12 +349,12 @@ func (n *lazyNode) equal(o *lazyNode) bool { return false } - if len(n.doc) != len(o.doc) { + if len(n.doc.obj) != len(o.doc.obj) { return false } - for k, v := range n.doc { - ov, ok := o.doc[k] + for k, v := range n.doc.obj { + ov, ok := o.doc.obj[k] if !ok { return false @@ -340,7 +487,7 @@ Loop: return false } -func findObject(pd *container, path string) (container, string) { +func findObject(pd *container, path string, options *ApplyOptions) (container, string) { doc := *pd split := strings.Split(path, "/") @@ -357,7 +504,7 @@ func findObject(pd *container, path string) (container, string) { for _, part := range parts { - next, ok := doc.get(decodePatchKey(part)) + next, ok := doc.get(decodePatchKey(part), options) if next == nil || ok != nil { return nil, "" @@ -381,46 +528,76 @@ func findObject(pd *container, path string) (container, string) { return doc, decodePatchKey(key) } -func (d *partialDoc) set(key string, val *lazyNode) error { - (*d)[key] = val +func (d *partialDoc) set(key string, val *lazyNode, options *ApplyOptions) error { + found := false + for _, k := range d.keys { + if k == key { + found = true + break + } + } + if !found { + d.keys = append(d.keys, key) + } + d.obj[key] = val return nil } -func (d *partialDoc) add(key string, val *lazyNode) error { - (*d)[key] = val - return nil +func (d *partialDoc) add(key string, val *lazyNode, options *ApplyOptions) error { + return d.set(key, val, options) } -func (d *partialDoc) get(key string) (*lazyNode, error) { - v, ok := (*d)[key] +func (d *partialDoc) get(key string, options *ApplyOptions) (*lazyNode, error) { + v, ok := d.obj[key] if !ok { return v, errors.Wrapf(ErrMissing, "unable to get nonexistent key: %s", key) } return v, nil } -func (d *partialDoc) remove(key string) error { - _, ok := (*d)[key] +func (d *partialDoc) remove(key string, options *ApplyOptions) error { + _, ok := d.obj[key] if !ok { + if options.AllowMissingPathOnRemove { + return nil + } return errors.Wrapf(ErrMissing, "unable to remove nonexistent key: %s", key) } - - delete(*d, key) + idx := -1 + for i, k := range d.keys { + if k == key { + idx = i + break + } + } + d.keys = append(d.keys[0:idx], d.keys[idx+1:]...) + delete(d.obj, key) return nil } // set should only be used to implement the "replace" operation, so "key" must // be an already existing index in "d". -func (d *partialArray) set(key string, val *lazyNode) error { +func (d *partialArray) set(key string, val *lazyNode, options *ApplyOptions) error { idx, err := strconv.Atoi(key) if err != nil { return err } + + if idx < 0 { + if !options.SupportNegativeIndices { + return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) + } + if idx < -len(*d) { + return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) + } + idx += len(*d) + } + (*d)[idx] = val return nil } -func (d *partialArray) add(key string, val *lazyNode) error { +func (d *partialArray) add(key string, val *lazyNode, options *ApplyOptions) error { if key == "-" { *d = append(*d, val) return nil @@ -442,7 +619,7 @@ func (d *partialArray) add(key string, val *lazyNode) error { } if idx < 0 { - if !SupportNegativeIndices { + if !options.SupportNegativeIndices { return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } if idx < -len(ary) { @@ -459,13 +636,23 @@ func (d *partialArray) add(key string, val *lazyNode) error { return nil } -func (d *partialArray) get(key string) (*lazyNode, error) { +func (d *partialArray) get(key string, options *ApplyOptions) (*lazyNode, error) { idx, err := strconv.Atoi(key) if err != nil { return nil, err } + if idx < 0 { + if !options.SupportNegativeIndices { + return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) + } + if idx < -len(*d) { + return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) + } + idx += len(*d) + } + if idx >= len(*d) { return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } @@ -473,7 +660,7 @@ func (d *partialArray) get(key string) (*lazyNode, error) { return (*d)[idx], nil } -func (d *partialArray) remove(key string) error { +func (d *partialArray) remove(key string, options *ApplyOptions) error { idx, err := strconv.Atoi(key) if err != nil { return err @@ -482,14 +669,20 @@ func (d *partialArray) remove(key string) error { cur := *d if idx >= len(cur) { + if options.AllowMissingPathOnRemove { + return nil + } return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } if idx < 0 { - if !SupportNegativeIndices { + if !options.SupportNegativeIndices { return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } if idx < -len(cur) { + if options.AllowMissingPathOnRemove { + return nil + } return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } idx += len(cur) @@ -502,22 +695,29 @@ func (d *partialArray) remove(key string) error { *d = ary return nil - } -func (p Patch) add(doc *container, op Operation) error { +func (p Patch) add(doc *container, op Operation, options *ApplyOptions) error { path, err := op.Path() if err != nil { return errors.Wrapf(ErrMissing, "add operation failed to decode path") } - con, key := findObject(doc, path) + if options.EnsurePathExistsOnAdd { + err = ensurePathExists(doc, path, options) + + if err != nil { + return err + } + } + + con, key := findObject(doc, path, options) if con == nil { return errors.Wrapf(ErrMissing, "add operation does not apply: doc is missing path: \"%s\"", path) } - err = con.add(key, op.value()) + err = con.add(key, op.value(), options) if err != nil { return errors.Wrapf(err, "error in add for path: '%s'", path) } @@ -525,19 +725,113 @@ func (p Patch) add(doc *container, op Operation) error { return nil } -func (p Patch) remove(doc *container, op Operation) error { +// Given a document and a path to a key, walk the path and create all missing elements +// creating objects and arrays as needed. +func ensurePathExists(pd *container, path string, options *ApplyOptions) error { + doc := *pd + + var err error + var arrIndex int + + split := strings.Split(path, "/") + + if len(split) < 2 { + return nil + } + + parts := split[1:] + + for pi, part := range parts { + + // Have we reached the key part of the path? + // If yes, we're done. + if pi == len(parts)-1 { + return nil + } + + target, ok := doc.get(decodePatchKey(part), options) + + if target == nil || ok != nil { + + // If the current container is an array which has fewer elements than our target index, + // pad the current container with nulls. + if arrIndex, err = strconv.Atoi(part); err == nil { + pa, ok := doc.(*partialArray) + + if ok && arrIndex >= len(*pa)+1 { + // Pad the array with null values up to the required index. + for i := len(*pa); i <= arrIndex-1; i++ { + doc.add(strconv.Itoa(i), newLazyNode(newRawMessage(rawJSONNull)), options) + } + } + } + + // Check if the next part is a numeric index. + // If yes, then create an array, otherwise, create an object. + if arrIndex, err = strconv.Atoi(parts[pi+1]); err == nil { + if arrIndex < 0 { + + if !options.SupportNegativeIndices { + return errors.Wrapf(ErrInvalidIndex, "Unable to ensure path for invalid index: %d", arrIndex) + } + + if arrIndex < -1 { + return errors.Wrapf(ErrInvalidIndex, "Unable to ensure path for negative index other than -1: %d", arrIndex) + } + + arrIndex = 0 + } + + newNode := newLazyNode(newRawMessage(rawJSONArray)) + doc.add(part, newNode, options) + doc, _ = newNode.intoAry() + + // Pad the new array with null values up to the required index. + for i := 0; i < arrIndex; i++ { + doc.add(strconv.Itoa(i), newLazyNode(newRawMessage(rawJSONNull)), options) + } + } else { + newNode := newLazyNode(newRawMessage(rawJSONObject)) + + doc.add(part, newNode, options) + doc, _ = newNode.intoDoc() + } + } else { + if isArray(*target.raw) { + doc, err = target.intoAry() + + if err != nil { + return err + } + } else { + doc, err = target.intoDoc() + + if err != nil { + return err + } + } + } + } + + return nil +} + +func (p Patch) remove(doc *container, op Operation, options *ApplyOptions) error { path, err := op.Path() if err != nil { return errors.Wrapf(ErrMissing, "remove operation failed to decode path") } - con, key := findObject(doc, path) + con, key := findObject(doc, path, options) if con == nil { + if options.AllowMissingPathOnRemove { + return nil + } return errors.Wrapf(ErrMissing, "remove operation does not apply: doc is missing path: \"%s\"", path) } - err = con.remove(key) + err = con.remove(key, options) if err != nil { return errors.Wrapf(err, "error in remove for path: '%s'", path) } @@ -545,24 +839,24 @@ func (p Patch) remove(doc *container, op Operation) error { return nil } -func (p Patch) replace(doc *container, op Operation) error { +func (p Patch) replace(doc *container, op Operation, options *ApplyOptions) error { path, err := op.Path() if err != nil { return errors.Wrapf(err, "replace operation failed to decode path") } - con, key := findObject(doc, path) + con, key := findObject(doc, path, options) if con == nil { return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing path: %s", path) } - _, ok := con.get(key) + _, ok := con.get(key, options) if ok != nil { return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing key: %s", path) } - err = con.set(key, op.value()) + err = con.set(key, op.value(), options) if err != nil { return errors.Wrapf(err, "error in remove for path: '%s'", path) } @@ -570,24 +864,24 @@ func (p Patch) replace(doc *container, op Operation) error { return nil } -func (p Patch) move(doc *container, op Operation) error { +func (p Patch) move(doc *container, op Operation, options *ApplyOptions) error { from, err := op.From() if err != nil { return errors.Wrapf(err, "move operation failed to decode from") } - con, key := findObject(doc, from) + con, key := findObject(doc, from, options) if con == nil { return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing from path: %s", from) } - val, err := con.get(key) + val, err := con.get(key, options) if err != nil { return errors.Wrapf(err, "error in move for path: '%s'", key) } - err = con.remove(key) + err = con.remove(key, options) if err != nil { return errors.Wrapf(err, "error in move for path: '%s'", key) } @@ -597,13 +891,13 @@ func (p Patch) move(doc *container, op Operation) error { return errors.Wrapf(err, "move operation failed to decode path") } - con, key = findObject(doc, path) + con, key = findObject(doc, path, options) if con == nil { return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing destination path: %s", path) } - err = con.add(key, val) + err = con.add(key, val, options) if err != nil { return errors.Wrapf(err, "error in move for path: '%s'", path) } @@ -611,19 +905,19 @@ func (p Patch) move(doc *container, op Operation) error { return nil } -func (p Patch) test(doc *container, op Operation) error { +func (p Patch) test(doc *container, op Operation, options *ApplyOptions) error { path, err := op.Path() if err != nil { return errors.Wrapf(err, "test operation failed to decode path") } - con, key := findObject(doc, path) + con, key := findObject(doc, path, options) if con == nil { return errors.Wrapf(ErrMissing, "test operation does not apply: is missing path: %s", path) } - val, err := con.get(key) + val, err := con.get(key, options) if err != nil && errors.Cause(err) != ErrMissing { return errors.Wrapf(err, "error in test for path: '%s'", path) } @@ -644,19 +938,19 @@ func (p Patch) test(doc *container, op Operation) error { return errors.Wrapf(ErrTestFailed, "testing value %s failed", path) } -func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) error { +func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64, options *ApplyOptions) error { from, err := op.From() if err != nil { return errors.Wrapf(err, "copy operation failed to decode from") } - con, key := findObject(doc, from) + con, key := findObject(doc, from, options) if con == nil { return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing from path: %s", from) } - val, err := con.get(key) + val, err := con.get(key, options) if err != nil { return errors.Wrapf(err, "error in copy for from: '%s'", from) } @@ -666,7 +960,7 @@ func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) er return errors.Wrapf(ErrMissing, "copy operation failed to decode path") } - con, key = findObject(doc, path) + con, key = findObject(doc, path, options) if con == nil { return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing destination path: %s", path) @@ -678,11 +972,11 @@ func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) er } (*accumulatedCopySize) += int64(sz) - if AccumulatedCopySizeLimit > 0 && *accumulatedCopySize > AccumulatedCopySizeLimit { - return NewAccumulatedCopySizeError(AccumulatedCopySizeLimit, *accumulatedCopySize) + if options.AccumulatedCopySizeLimit > 0 && *accumulatedCopySize > options.AccumulatedCopySizeLimit { + return NewAccumulatedCopySizeError(options.AccumulatedCopySizeLimit, *accumulatedCopySize) } - err = con.add(key, valCopy) + err = con.add(key, valCopy, options) if err != nil { return errors.Wrapf(err, "error while adding value during copy") } @@ -692,13 +986,8 @@ func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) er // Equal indicates if 2 JSON documents have the same structural equality. func Equal(a, b []byte) bool { - ra := make(json.RawMessage, len(a)) - copy(ra, a) - la := newLazyNode(&ra) - - rb := make(json.RawMessage, len(b)) - copy(rb, b) - lb := newLazyNode(&rb) + la := newLazyNode(newRawMessage(a)) + lb := newLazyNode(newRawMessage(b)) return la.equal(lb) } @@ -719,12 +1008,24 @@ func DecodePatch(buf []byte) (Patch, error) { // Apply mutates a JSON document according to the patch, and returns the new // document. func (p Patch) Apply(doc []byte) ([]byte, error) { - return p.ApplyIndent(doc, "") + return p.ApplyWithOptions(doc, NewApplyOptions()) +} + +// ApplyWithOptions mutates a JSON document according to the patch and the passed in ApplyOptions. +// It returns the new document. +func (p Patch) ApplyWithOptions(doc []byte, options *ApplyOptions) ([]byte, error) { + return p.ApplyIndentWithOptions(doc, "", options) } // ApplyIndent mutates a JSON document according to the patch, and returns the new // document indented. func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) { + return p.ApplyIndentWithOptions(doc, indent, NewApplyOptions()) +} + +// ApplyIndentWithOptions mutates a JSON document according to the patch and the passed in ApplyOptions. +// It returns the new document indented. +func (p Patch) ApplyIndentWithOptions(doc []byte, indent string, options *ApplyOptions) ([]byte, error) { var pd container if doc[0] == '[' { pd = &partialArray{} @@ -745,17 +1046,17 @@ func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) { for _, op := range p { switch op.Kind() { case "add": - err = p.add(&pd, op) + err = p.add(&pd, op, options) case "remove": - err = p.remove(&pd, op) + err = p.remove(&pd, op, options) case "replace": - err = p.replace(&pd, op) + err = p.replace(&pd, op, options) case "move": - err = p.move(&pd, op) + err = p.move(&pd, op, options) case "test": - err = p.test(&pd, op) + err = p.test(&pd, op, options) case "copy": - err = p.copy(&pd, op, &accumulatedCopySize) + err = p.copy(&pd, op, &accumulatedCopySize, options) default: err = fmt.Errorf("Unexpected kind: %s", op.Kind()) } diff --git a/vendor/github.com/pelletier/go-toml/azure-pipelines.yml b/vendor/github.com/pelletier/go-toml/azure-pipelines.yml index 242b5b5403..ff5376b093 100644 --- a/vendor/github.com/pelletier/go-toml/azure-pipelines.yml +++ b/vendor/github.com/pelletier/go-toml/azure-pipelines.yml @@ -13,9 +13,9 @@ stages: vmImage: ubuntu-latest steps: - task: GoTool@0 - displayName: "Install Go 1.14" + displayName: "Install Go 1.15" inputs: - version: "1.14" + version: "1.15" - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/" - script: mkdir -p ${HOME}/go/src/github.com/pelletier/go-toml - script: cp -R . ${HOME}/go/src/github.com/pelletier/go-toml @@ -36,9 +36,9 @@ stages: vmImage: ubuntu-latest steps: - task: GoTool@0 - displayName: "Install Go 1.14" + displayName: "Install Go 1.15" inputs: - version: "1.14" + version: "1.15" - task: Go@0 displayName: "go fmt ./..." inputs: @@ -51,9 +51,9 @@ stages: vmImage: ubuntu-latest steps: - task: GoTool@0 - displayName: "Install Go 1.14" + displayName: "Install Go 1.15" inputs: - version: "1.14" + version: "1.15" - task: Go@0 displayName: "Generate coverage" inputs: @@ -71,9 +71,9 @@ stages: vmImage: ubuntu-latest steps: - task: GoTool@0 - displayName: "Install Go 1.14" + displayName: "Install Go 1.15" inputs: - version: "1.14" + version: "1.15" - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/" - task: Bash@3 inputs: @@ -86,9 +86,9 @@ stages: vmImage: ubuntu-latest steps: - task: GoTool@0 - displayName: "Install Go 1.14" + displayName: "Install Go 1.15" inputs: - version: "1.14" + version: "1.15" - script: echo "##vso[task.setvariable variable=PATH]${PATH}:/home/vsts/go/bin/" - script: mkdir -p ${HOME}/go/src/github.com/pelletier/go-toml - script: cp -R . ${HOME}/go/src/github.com/pelletier/go-toml @@ -102,6 +102,15 @@ stages: displayName: "unit tests" strategy: matrix: + linux 1.15: + goVersion: '1.15' + imageName: 'ubuntu-latest' + mac 1.15: + goVersion: '1.15' + imageName: 'macOS-latest' + windows 1.15: + goVersion: '1.15' + imageName: 'windows-latest' linux 1.14: goVersion: '1.14' imageName: 'ubuntu-latest' @@ -111,15 +120,6 @@ stages: windows 1.14: goVersion: '1.14' imageName: 'windows-latest' - linux 1.13: - goVersion: '1.13' - imageName: 'ubuntu-latest' - mac 1.13: - goVersion: '1.13' - imageName: 'macOS-latest' - windows 1.13: - goVersion: '1.13' - imageName: 'windows-latest' pool: vmImage: $(imageName) steps: @@ -155,7 +155,7 @@ stages: - task: GoTool@0 displayName: "Install Go" inputs: - version: 1.14 + version: 1.15 - task: Bash@3 inputs: targetType: inline diff --git a/vendor/github.com/pelletier/go-toml/benchmark.json b/vendor/github.com/pelletier/go-toml/benchmark.json deleted file mode 100644 index 86f99c6a87..0000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark.json +++ /dev/null @@ -1,164 +0,0 @@ -{ - "array": { - "key1": [ - 1, - 2, - 3 - ], - "key2": [ - "red", - "yellow", - "green" - ], - "key3": [ - [ - 1, - 2 - ], - [ - 3, - 4, - 5 - ] - ], - "key4": [ - [ - 1, - 2 - ], - [ - "a", - "b", - "c" - ] - ], - "key5": [ - 1, - 2, - 3 - ], - "key6": [ - 1, - 2 - ] - }, - "boolean": { - "False": false, - "True": true - }, - "datetime": { - "key1": "1979-05-27T07:32:00Z", - "key2": "1979-05-27T00:32:00-07:00", - "key3": "1979-05-27T00:32:00.999999-07:00" - }, - "float": { - "both": { - "key": 6.626e-34 - }, - "exponent": { - "key1": 5e+22, - "key2": 1000000, - "key3": -0.02 - }, - "fractional": { - "key1": 1, - "key2": 3.1415, - "key3": -0.01 - }, - "underscores": { - "key1": 9224617.445991227, - "key2": 1e+100 - } - }, - "fruit": [{ - "name": "apple", - "physical": { - "color": "red", - "shape": "round" - }, - "variety": [{ - "name": "red delicious" - }, - { - "name": "granny smith" - } - ] - }, - { - "name": "banana", - "variety": [{ - "name": "plantain" - }] - } - ], - "integer": { - "key1": 99, - "key2": 42, - "key3": 0, - "key4": -17, - "underscores": { - "key1": 1000, - "key2": 5349221, - "key3": 12345 - } - }, - "products": [{ - "name": "Hammer", - "sku": 738594937 - }, - {}, - { - "color": "gray", - "name": "Nail", - "sku": 284758393 - } - ], - "string": { - "basic": { - "basic": "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF." - }, - "literal": { - "multiline": { - "lines": "The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", - "regex2": "I [dw]on't need \\d{2} apples" - }, - "quoted": "Tom \"Dubs\" Preston-Werner", - "regex": "\u003c\\i\\c*\\s*\u003e", - "winpath": "C:\\Users\\nodejs\\templates", - "winpath2": "\\\\ServerX\\admin$\\system32\\" - }, - "multiline": { - "continued": { - "key1": "The quick brown fox jumps over the lazy dog.", - "key2": "The quick brown fox jumps over the lazy dog.", - "key3": "The quick brown fox jumps over the lazy dog." - }, - "key1": "One\nTwo", - "key2": "One\nTwo", - "key3": "One\nTwo" - } - }, - "table": { - "inline": { - "name": { - "first": "Tom", - "last": "Preston-Werner" - }, - "point": { - "x": 1, - "y": 2 - } - }, - "key": "value", - "subtable": { - "key": "another value" - } - }, - "x": { - "y": { - "z": { - "w": {} - } - } - } -} diff --git a/vendor/github.com/pelletier/go-toml/benchmark.sh b/vendor/github.com/pelletier/go-toml/benchmark.sh index 7914fff49c..a69d3040fa 100644 --- a/vendor/github.com/pelletier/go-toml/benchmark.sh +++ b/vendor/github.com/pelletier/go-toml/benchmark.sh @@ -20,11 +20,15 @@ git clone ${reference_git} ${ref_tempdir} >/dev/null 2>/dev/null pushd ${ref_tempdir} >/dev/null git checkout ${reference_ref} >/dev/null 2>/dev/null go test -bench=. -benchmem | tee ${ref_benchmark} +cd benchmark +go test -bench=. -benchmem | tee -a ${ref_benchmark} popd >/dev/null echo "" echo "=== local" go test -bench=. -benchmem | tee ${local_benchmark} +cd benchmark +go test -bench=. -benchmem | tee -a ${local_benchmark} echo "" echo "=== diff" diff --git a/vendor/github.com/pelletier/go-toml/benchmark.toml b/vendor/github.com/pelletier/go-toml/benchmark.toml deleted file mode 100644 index dfd77e0962..0000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark.toml +++ /dev/null @@ -1,244 +0,0 @@ -################################################################################ -## Comment - -# Speak your mind with the hash symbol. They go from the symbol to the end of -# the line. - - -################################################################################ -## Table - -# Tables (also known as hash tables or dictionaries) are collections of -# key/value pairs. They appear in square brackets on a line by themselves. - -[table] - -key = "value" # Yeah, you can do this. - -# Nested tables are denoted by table names with dots in them. Name your tables -# whatever crap you please, just don't use #, ., [ or ]. - -[table.subtable] - -key = "another value" - -# You don't need to specify all the super-tables if you don't want to. TOML -# knows how to do it for you. - -# [x] you -# [x.y] don't -# [x.y.z] need these -[x.y.z.w] # for this to work - - -################################################################################ -## Inline Table - -# Inline tables provide a more compact syntax for expressing tables. They are -# especially useful for grouped data that can otherwise quickly become verbose. -# Inline tables are enclosed in curly braces `{` and `}`. No newlines are -# allowed between the curly braces unless they are valid within a value. - -[table.inline] - -name = { first = "Tom", last = "Preston-Werner" } -point = { x = 1, y = 2 } - - -################################################################################ -## String - -# There are four ways to express strings: basic, multi-line basic, literal, and -# multi-line literal. All strings must contain only valid UTF-8 characters. - -[string.basic] - -basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF." - -[string.multiline] - -# The following strings are byte-for-byte equivalent: -key1 = "One\nTwo" -key2 = """One\nTwo""" -key3 = """ -One -Two""" - -[string.multiline.continued] - -# The following strings are byte-for-byte equivalent: -key1 = "The quick brown fox jumps over the lazy dog." - -key2 = """ -The quick brown \ - - - fox jumps over \ - the lazy dog.""" - -key3 = """\ - The quick brown \ - fox jumps over \ - the lazy dog.\ - """ - -[string.literal] - -# What you see is what you get. -winpath = 'C:\Users\nodejs\templates' -winpath2 = '\\ServerX\admin$\system32\' -quoted = 'Tom "Dubs" Preston-Werner' -regex = '<\i\c*\s*>' - - -[string.literal.multiline] - -regex2 = '''I [dw]on't need \d{2} apples''' -lines = ''' -The first newline is -trimmed in raw strings. - All other whitespace - is preserved. -''' - - -################################################################################ -## Integer - -# Integers are whole numbers. Positive numbers may be prefixed with a plus sign. -# Negative numbers are prefixed with a minus sign. - -[integer] - -key1 = +99 -key2 = 42 -key3 = 0 -key4 = -17 - -[integer.underscores] - -# For large numbers, you may use underscores to enhance readability. Each -# underscore must be surrounded by at least one digit. -key1 = 1_000 -key2 = 5_349_221 -key3 = 1_2_3_4_5 # valid but inadvisable - - -################################################################################ -## Float - -# A float consists of an integer part (which may be prefixed with a plus or -# minus sign) followed by a fractional part and/or an exponent part. - -[float.fractional] - -key1 = +1.0 -key2 = 3.1415 -key3 = -0.01 - -[float.exponent] - -key1 = 5e+22 -key2 = 1e6 -key3 = -2E-2 - -[float.both] - -key = 6.626e-34 - -[float.underscores] - -key1 = 9_224_617.445_991_228_313 -key2 = 1e1_00 - - -################################################################################ -## Boolean - -# Booleans are just the tokens you're used to. Always lowercase. - -[boolean] - -True = true -False = false - - -################################################################################ -## Datetime - -# Datetimes are RFC 3339 dates. - -[datetime] - -key1 = 1979-05-27T07:32:00Z -key2 = 1979-05-27T00:32:00-07:00 -key3 = 1979-05-27T00:32:00.999999-07:00 - - -################################################################################ -## Array - -# Arrays are square brackets with other primitives inside. Whitespace is -# ignored. Elements are separated by commas. Data types may not be mixed. - -[array] - -key1 = [ 1, 2, 3 ] -key2 = [ "red", "yellow", "green" ] -key3 = [ [ 1, 2 ], [3, 4, 5] ] -#key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok - -# Arrays can also be multiline. So in addition to ignoring whitespace, arrays -# also ignore newlines between the brackets. Terminating commas are ok before -# the closing bracket. - -key5 = [ - 1, 2, 3 -] -key6 = [ - 1, - 2, # this is ok -] - - -################################################################################ -## Array of Tables - -# These can be expressed by using a table name in double brackets. Each table -# with the same double bracketed name will be an element in the array. The -# tables are inserted in the order encountered. - -[[products]] - -name = "Hammer" -sku = 738594937 - -[[products]] - -[[products]] - -name = "Nail" -sku = 284758393 -color = "gray" - - -# You can create nested arrays of tables as well. - -[[fruit]] - name = "apple" - - [fruit.physical] - color = "red" - shape = "round" - - [[fruit.variety]] - name = "red delicious" - - [[fruit.variety]] - name = "granny smith" - -[[fruit]] - name = "banana" - - [[fruit.variety]] - name = "plantain" diff --git a/vendor/github.com/pelletier/go-toml/benchmark.yml b/vendor/github.com/pelletier/go-toml/benchmark.yml deleted file mode 100644 index 0bd19f08a6..0000000000 --- a/vendor/github.com/pelletier/go-toml/benchmark.yml +++ /dev/null @@ -1,121 +0,0 @@ ---- -array: - key1: - - 1 - - 2 - - 3 - key2: - - red - - yellow - - green - key3: - - - 1 - - 2 - - - 3 - - 4 - - 5 - key4: - - - 1 - - 2 - - - a - - b - - c - key5: - - 1 - - 2 - - 3 - key6: - - 1 - - 2 -boolean: - 'False': false - 'True': true -datetime: - key1: '1979-05-27T07:32:00Z' - key2: '1979-05-27T00:32:00-07:00' - key3: '1979-05-27T00:32:00.999999-07:00' -float: - both: - key: 6.626e-34 - exponent: - key1: 5.0e+22 - key2: 1000000 - key3: -0.02 - fractional: - key1: 1 - key2: 3.1415 - key3: -0.01 - underscores: - key1: 9224617.445991227 - key2: 1.0e+100 -fruit: -- name: apple - physical: - color: red - shape: round - variety: - - name: red delicious - - name: granny smith -- name: banana - variety: - - name: plantain -integer: - key1: 99 - key2: 42 - key3: 0 - key4: -17 - underscores: - key1: 1000 - key2: 5349221 - key3: 12345 -products: -- name: Hammer - sku: 738594937 -- {} -- color: gray - name: Nail - sku: 284758393 -string: - basic: - basic: "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF." - literal: - multiline: - lines: | - The first newline is - trimmed in raw strings. - All other whitespace - is preserved. - regex2: I [dw]on't need \d{2} apples - quoted: Tom "Dubs" Preston-Werner - regex: "<\\i\\c*\\s*>" - winpath: C:\Users\nodejs\templates - winpath2: "\\\\ServerX\\admin$\\system32\\" - multiline: - continued: - key1: The quick brown fox jumps over the lazy dog. - key2: The quick brown fox jumps over the lazy dog. - key3: The quick brown fox jumps over the lazy dog. - key1: |- - One - Two - key2: |- - One - Two - key3: |- - One - Two -table: - inline: - name: - first: Tom - last: Preston-Werner - point: - x: 1 - y: 2 - key: value - subtable: - key: another value -x: - y: - z: - w: {} diff --git a/vendor/github.com/pelletier/go-toml/go.mod b/vendor/github.com/pelletier/go-toml/go.mod index c7faa6b3e1..e924cb90c0 100644 --- a/vendor/github.com/pelletier/go-toml/go.mod +++ b/vendor/github.com/pelletier/go-toml/go.mod @@ -2,8 +2,4 @@ module github.com/pelletier/go-toml go 1.12 -require ( - github.com/BurntSushi/toml v0.3.1 - github.com/davecgh/go-spew v1.1.1 - gopkg.in/yaml.v2 v2.3.0 -) +require github.com/davecgh/go-spew v1.1.1 diff --git a/vendor/github.com/pelletier/go-toml/lexer.go b/vendor/github.com/pelletier/go-toml/lexer.go index 425e847a7a..b188619246 100644 --- a/vendor/github.com/pelletier/go-toml/lexer.go +++ b/vendor/github.com/pelletier/go-toml/lexer.go @@ -306,7 +306,7 @@ func (l *tomlLexer) lexComma() tomlLexStateFn { // Parse the key and emits its value without escape sequences. // bare keys, basic string keys and literal string keys are supported. func (l *tomlLexer) lexKey() tomlLexStateFn { - growingString := "" + var sb strings.Builder for r := l.peek(); isKeyChar(r) || r == '\n' || r == '\r'; r = l.peek() { if r == '"' { @@ -315,7 +315,9 @@ func (l *tomlLexer) lexKey() tomlLexStateFn { if err != nil { return l.errorf(err.Error()) } - growingString += "\"" + str + "\"" + sb.WriteString("\"") + sb.WriteString(str) + sb.WriteString("\"") l.next() continue } else if r == '\'' { @@ -324,41 +326,45 @@ func (l *tomlLexer) lexKey() tomlLexStateFn { if err != nil { return l.errorf(err.Error()) } - growingString += "'" + str + "'" + sb.WriteString("'") + sb.WriteString(str) + sb.WriteString("'") l.next() continue } else if r == '\n' { return l.errorf("keys cannot contain new lines") } else if isSpace(r) { - str := " " + var str strings.Builder + str.WriteString(" ") + // skip trailing whitespace l.next() for r = l.peek(); isSpace(r); r = l.peek() { - str += string(r) + str.WriteRune(r) l.next() } // break loop if not a dot if r != '.' { break } - str += "." + str.WriteString(".") // skip trailing whitespace after dot l.next() for r = l.peek(); isSpace(r); r = l.peek() { - str += string(r) + str.WriteRune(r) l.next() } - growingString += str + sb.WriteString(str.String()) continue } else if r == '.' { // skip } else if !isValidBareChar(r) { return l.errorf("keys cannot contain %c character", r) } - growingString += string(r) + sb.WriteRune(r) l.next() } - l.emitWithValue(tokenKey, growingString) + l.emitWithValue(tokenKey, sb.String()) return l.lexVoid } @@ -383,7 +389,7 @@ func (l *tomlLexer) lexLeftBracket() tomlLexStateFn { } func (l *tomlLexer) lexLiteralStringAsString(terminator string, discardLeadingNewLine bool) (string, error) { - growingString := "" + var sb strings.Builder if discardLeadingNewLine { if l.follow("\r\n") { @@ -397,14 +403,14 @@ func (l *tomlLexer) lexLiteralStringAsString(terminator string, discardLeadingNe // find end of string for { if l.follow(terminator) { - return growingString, nil + return sb.String(), nil } next := l.peek() if next == eof { break } - growingString += string(l.next()) + sb.WriteRune(l.next()) } return "", errors.New("unclosed string") @@ -438,7 +444,7 @@ func (l *tomlLexer) lexLiteralString() tomlLexStateFn { // Terminator is the substring indicating the end of the token. // The resulting string does not include the terminator. func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine, acceptNewLines bool) (string, error) { - growingString := "" + var sb strings.Builder if discardLeadingNewLine { if l.follow("\r\n") { @@ -451,7 +457,7 @@ func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine, for { if l.follow(terminator) { - return growingString, nil + return sb.String(), nil } if l.follow("\\") { @@ -469,61 +475,61 @@ func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine, l.next() } case '"': - growingString += "\"" + sb.WriteString("\"") l.next() case 'n': - growingString += "\n" + sb.WriteString("\n") l.next() case 'b': - growingString += "\b" + sb.WriteString("\b") l.next() case 'f': - growingString += "\f" + sb.WriteString("\f") l.next() case '/': - growingString += "/" + sb.WriteString("/") l.next() case 't': - growingString += "\t" + sb.WriteString("\t") l.next() case 'r': - growingString += "\r" + sb.WriteString("\r") l.next() case '\\': - growingString += "\\" + sb.WriteString("\\") l.next() case 'u': l.next() - code := "" + var code strings.Builder for i := 0; i < 4; i++ { c := l.peek() if !isHexDigit(c) { return "", errors.New("unfinished unicode escape") } l.next() - code = code + string(c) + code.WriteRune(c) } - intcode, err := strconv.ParseInt(code, 16, 32) + intcode, err := strconv.ParseInt(code.String(), 16, 32) if err != nil { - return "", errors.New("invalid unicode escape: \\u" + code) + return "", errors.New("invalid unicode escape: \\u" + code.String()) } - growingString += string(rune(intcode)) + sb.WriteRune(rune(intcode)) case 'U': l.next() - code := "" + var code strings.Builder for i := 0; i < 8; i++ { c := l.peek() if !isHexDigit(c) { return "", errors.New("unfinished unicode escape") } l.next() - code = code + string(c) + code.WriteRune(c) } - intcode, err := strconv.ParseInt(code, 16, 64) + intcode, err := strconv.ParseInt(code.String(), 16, 64) if err != nil { - return "", errors.New("invalid unicode escape: \\U" + code) + return "", errors.New("invalid unicode escape: \\U" + code.String()) } - growingString += string(rune(intcode)) + sb.WriteRune(rune(intcode)) default: return "", errors.New("invalid escape sequence: \\" + string(l.peek())) } @@ -534,7 +540,7 @@ func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine, return "", fmt.Errorf("unescaped control character %U", r) } l.next() - growingString += string(r) + sb.WriteRune(r) } if l.peek() == eof { @@ -769,19 +775,19 @@ func init() { // /!\ also matches the empty string // // Example matches: - //1979-05-27T07:32:00Z - //1979-05-27T00:32:00-07:00 - //1979-05-27T00:32:00.999999-07:00 - //1979-05-27 07:32:00Z - //1979-05-27 00:32:00-07:00 - //1979-05-27 00:32:00.999999-07:00 - //1979-05-27T07:32:00 - //1979-05-27T00:32:00.999999 - //1979-05-27 07:32:00 - //1979-05-27 00:32:00.999999 - //1979-05-27 - //07:32:00 - //00:32:00.999999 + // 1979-05-27T07:32:00Z + // 1979-05-27T00:32:00-07:00 + // 1979-05-27T00:32:00.999999-07:00 + // 1979-05-27 07:32:00Z + // 1979-05-27 00:32:00-07:00 + // 1979-05-27 00:32:00.999999-07:00 + // 1979-05-27T07:32:00 + // 1979-05-27T00:32:00.999999 + // 1979-05-27 07:32:00 + // 1979-05-27 00:32:00.999999 + // 1979-05-27 + // 07:32:00 + // 00:32:00.999999 dateRegexp = regexp.MustCompile(`^(?:\d{1,4}-\d{2}-\d{2})?(?:[T ]?\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})?)?`) } diff --git a/vendor/github.com/pelletier/go-toml/marshal.go b/vendor/github.com/pelletier/go-toml/marshal.go index db5a7b4f09..032e0ffc52 100644 --- a/vendor/github.com/pelletier/go-toml/marshal.go +++ b/vendor/github.com/pelletier/go-toml/marshal.go @@ -76,6 +76,7 @@ var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() var localDateType = reflect.TypeOf(LocalDate{}) var localTimeType = reflect.TypeOf(LocalTime{}) var localDateTimeType = reflect.TypeOf(LocalDateTime{}) +var mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{}) // Check if the given marshal type maps to a Tree primitive func isPrimitive(mtype reflect.Type) bool { @@ -436,6 +437,7 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er if tree, ok := val.(*Tree); ok && mtypef.Anonymous && !opts.nameFromTag && !e.promoteAnon { e.appendTree(tval, tree) } else { + val = e.wrapTomlValue(val, tval) tval.SetPathWithOptions([]string{opts.name}, SetOptions{ Comment: opts.comment, Commented: opts.commented, @@ -474,6 +476,7 @@ func (e *Encoder) valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, er if err != nil { return nil, err } + val = e.wrapTomlValue(val, tval) if e.quoteMapKeys { keyStr, err := tomlValueStringRepresentation(key.String(), "", "", e.order, e.arraysOneElementPerLine) if err != nil { @@ -516,13 +519,13 @@ func (e *Encoder) valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (int // Convert given marshal value to toml value func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) { - e.line++ if mtype.Kind() == reflect.Ptr { switch { case isCustomMarshaler(mtype): return callCustomMarshaler(mval) case isTextMarshaler(mtype): - return callTextMarshaler(mval) + b, err := callTextMarshaler(mval) + return string(b), err default: return e.valueToToml(mtype.Elem(), mval.Elem()) } @@ -534,7 +537,8 @@ func (e *Encoder) valueToToml(mtype reflect.Type, mval reflect.Value) (interface case isCustomMarshaler(mtype): return callCustomMarshaler(mval) case isTextMarshaler(mtype): - return callTextMarshaler(mval) + b, err := callTextMarshaler(mval) + return string(b), err case isTree(mtype): return e.valueToTree(mtype, mval) case isOtherSequence(mtype), isCustomMarshalerSequence(mtype), isTextMarshalerSequence(mtype): @@ -577,6 +581,25 @@ func (e *Encoder) appendTree(t, o *Tree) error { return nil } +// Create a toml value with the current line number as the position line +func (e *Encoder) wrapTomlValue(val interface{}, parent *Tree) interface{} { + _, isTree := val.(*Tree) + _, isTreeS := val.([]*Tree) + if isTree || isTreeS { + return val + } + + ret := &tomlValue{ + value: val, + position: Position{ + e.line, + parent.position.Col, + }, + } + e.line++ + return ret +} + // Unmarshal attempts to unmarshal the Tree into a Go struct pointed by v. // Neither Unmarshaler interfaces nor UnmarshalTOML functions are supported for // sub-structs, and only definite types can be unmarshaled. @@ -681,6 +704,8 @@ func (d *Decoder) unmarshal(v interface{}) error { switch elem.Kind() { case reflect.Struct, reflect.Map: + case reflect.Interface: + elem = mapStringInterfaceType default: return errors.New("only a pointer to struct or map can be unmarshaled from TOML") } @@ -717,6 +742,10 @@ func (d *Decoder) valueFromTree(mtype reflect.Type, tval *Tree, mval1 *reflect.V if mvalPtr := reflect.New(mtype); isCustomUnmarshaler(mvalPtr.Type()) { d.visitor.visitAll() + if tval == nil { + return mvalPtr.Elem(), nil + } + if err := callCustomUnmarshaler(mvalPtr, tval.ToMap()); err != nil { return reflect.ValueOf(nil), fmt.Errorf("unmarshal toml: %v", err) } diff --git a/vendor/github.com/pelletier/go-toml/toml.go b/vendor/github.com/pelletier/go-toml/toml.go index d323c39bce..cbb89a9af3 100644 --- a/vendor/github.com/pelletier/go-toml/toml.go +++ b/vendor/github.com/pelletier/go-toml/toml.go @@ -122,6 +122,89 @@ func (t *Tree) GetPath(keys []string) interface{} { } } +// GetArray returns the value at key in the Tree. +// It returns []string, []int64, etc type if key has homogeneous lists +// Key is a dot-separated path (e.g. a.b.c) without single/double quoted strings. +// Returns nil if the path does not exist in the tree. +// If keys is of length zero, the current tree is returned. +func (t *Tree) GetArray(key string) interface{} { + if key == "" { + return t + } + return t.GetArrayPath(strings.Split(key, ".")) +} + +// GetArrayPath returns the element in the tree indicated by 'keys'. +// If keys is of length zero, the current tree is returned. +func (t *Tree) GetArrayPath(keys []string) interface{} { + if len(keys) == 0 { + return t + } + subtree := t + for _, intermediateKey := range keys[:len(keys)-1] { + value, exists := subtree.values[intermediateKey] + if !exists { + return nil + } + switch node := value.(type) { + case *Tree: + subtree = node + case []*Tree: + // go to most recent element + if len(node) == 0 { + return nil + } + subtree = node[len(node)-1] + default: + return nil // cannot navigate through other node types + } + } + // branch based on final node type + switch node := subtree.values[keys[len(keys)-1]].(type) { + case *tomlValue: + switch n := node.value.(type) { + case []interface{}: + return getArray(n) + default: + return node.value + } + default: + return node + } +} + +// if homogeneous array, then return slice type object over []interface{} +func getArray(n []interface{}) interface{} { + var s []string + var i64 []int64 + var f64 []float64 + var bl []bool + for _, value := range n { + switch v := value.(type) { + case string: + s = append(s, v) + case int64: + i64 = append(i64, v) + case float64: + f64 = append(f64, v) + case bool: + bl = append(bl, v) + default: + return n + } + } + if len(s) == len(n) { + return s + } else if len(i64) == len(n) { + return i64 + } else if len(f64) == len(n) { + return f64 + } else if len(bl) == len(n) { + return bl + } + return n +} + // GetPosition returns the position of the given key. func (t *Tree) GetPosition(key string) Position { if key == "" { @@ -130,6 +213,50 @@ func (t *Tree) GetPosition(key string) Position { return t.GetPositionPath(strings.Split(key, ".")) } +// SetPositionPath sets the position of element in the tree indicated by 'keys'. +// If keys is of length zero, the current tree position is set. +func (t *Tree) SetPositionPath(keys []string, pos Position) { + if len(keys) == 0 { + t.position = pos + return + } + subtree := t + for _, intermediateKey := range keys[:len(keys)-1] { + value, exists := subtree.values[intermediateKey] + if !exists { + return + } + switch node := value.(type) { + case *Tree: + subtree = node + case []*Tree: + // go to most recent element + if len(node) == 0 { + return + } + subtree = node[len(node)-1] + default: + return + } + } + // branch based on final node type + switch node := subtree.values[keys[len(keys)-1]].(type) { + case *tomlValue: + node.position = pos + return + case *Tree: + node.position = pos + return + case []*Tree: + // go to most recent element + if len(node) == 0 { + return + } + node[len(node)-1].position = pos + return + } +} + // GetPositionPath returns the element in the tree indicated by 'keys'. // If keys is of length zero, the current tree is returned. func (t *Tree) GetPositionPath(keys []string) Position { @@ -212,7 +339,8 @@ func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interfac // go to most recent element if len(node) == 0 { // create element if it does not exist - subtree.values[intermediateKey] = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})) + node = append(node, newTreeWithPosition(Position{Line: t.position.Line + i, Col: t.position.Col})) + subtree.values[intermediateKey] = node } subtree = node[len(node)-1] } @@ -232,6 +360,8 @@ func (t *Tree) SetPathWithOptions(keys []string, opts SetOptions, value interfac toInsert = value case *tomlValue: v.comment = opts.Comment + v.commented = opts.Commented + v.multiline = opts.Multiline toInsert = v default: toInsert = &tomlValue{value: value, diff --git a/vendor/github.com/pelletier/go-toml/tomltree_create.go b/vendor/github.com/pelletier/go-toml/tomltree_create.go index 79610e9b34..80353500a0 100644 --- a/vendor/github.com/pelletier/go-toml/tomltree_create.go +++ b/vendor/github.com/pelletier/go-toml/tomltree_create.go @@ -57,6 +57,19 @@ func simpleValueCoercion(object interface{}) (interface{}, error) { return float64(original), nil case fmt.Stringer: return original.String(), nil + case []interface{}: + value := reflect.ValueOf(original) + length := value.Len() + arrayValue := reflect.MakeSlice(value.Type(), 0, length) + for i := 0; i < length; i++ { + val := value.Index(i).Interface() + simpleValue, err := simpleValueCoercion(val) + if err != nil { + return nil, err + } + arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue)) + } + return arrayValue.Interface(), nil default: return nil, fmt.Errorf("cannot convert type %T to Tree", object) } diff --git a/vendor/github.com/pelletier/go-toml/tomltree_write.go b/vendor/github.com/pelletier/go-toml/tomltree_write.go index 2d6487ede4..ae6dac49d1 100644 --- a/vendor/github.com/pelletier/go-toml/tomltree_write.go +++ b/vendor/github.com/pelletier/go-toml/tomltree_write.go @@ -163,7 +163,7 @@ func tomlValueStringRepresentation(v interface{}, commented string, indent strin return "\"" + encodeTomlString(value) + "\"", nil case []byte: b, _ := v.([]byte) - return tomlValueStringRepresentation(string(b), commented, indent, ord, arraysOneElementPerLine) + return string(b), nil case bool: if value { return "true", nil diff --git a/vendor/gopkg.in/yaml.v3/.travis.yml b/vendor/gopkg.in/yaml.v3/.travis.yml deleted file mode 100644 index a130fe883c..0000000000 --- a/vendor/gopkg.in/yaml.v3/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: go - -go: - - "1.4.x" - - "1.5.x" - - "1.6.x" - - "1.7.x" - - "1.8.x" - - "1.9.x" - - "1.10.x" - - "1.11.x" - - "1.12.x" - - "1.13.x" - - "1.14.x" - - "tip" - -go_import_path: gopkg.in/yaml.v3 diff --git a/vendor/gopkg.in/yaml.v3/decode.go b/vendor/gopkg.in/yaml.v3/decode.go index 21c0dacfdf..df36e3a30f 100644 --- a/vendor/gopkg.in/yaml.v3/decode.go +++ b/vendor/gopkg.in/yaml.v3/decode.go @@ -399,7 +399,7 @@ func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good // // If n holds a null value, prepare returns before doing anything. func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { - if n.ShortTag() == nullTag || n.Kind == 0 && n.IsZero() { + if n.ShortTag() == nullTag { return out, false, false } again := true @@ -808,8 +808,10 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } } + mapIsNew := false if out.IsNil() { out.Set(reflect.MakeMap(outt)) + mapIsNew = true } for i := 0; i < l; i += 2 { if isMerge(n.Content[i]) { @@ -826,7 +828,7 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { failf("invalid map key: %#v", k.Interface()) } e := reflect.New(et).Elem() - if d.unmarshal(n.Content[i+1], e) { + if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) { out.SetMapIndex(k, e) } } diff --git a/vendor/gopkg.in/yaml.v3/emitterc.go b/vendor/gopkg.in/yaml.v3/emitterc.go index c29217ef54..0f47c9ca8a 100644 --- a/vendor/gopkg.in/yaml.v3/emitterc.go +++ b/vendor/gopkg.in/yaml.v3/emitterc.go @@ -814,26 +814,24 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_ } } if len(emitter.key_line_comment) > 0 { - // [Go] A line comment was previously provided for the key. Handle it before - // the value so the inline comments are placed correctly. - if yaml_emitter_silent_nil_event(emitter, event) && len(emitter.line_comment) == 0 { - // Nothing other than the line comment will be written on the line. - emitter.line_comment = emitter.key_line_comment - emitter.key_line_comment = nil - } else { - // An actual value is coming, so emit the comment line. + // [Go] Line comments are generally associated with the value, but when there's + // no value on the same line as a mapping key they end up attached to the + // key itself. + if event.typ == yaml_SCALAR_EVENT { + if len(emitter.line_comment) == 0 { + // A scalar is coming and it has no line comments by itself yet, + // so just let it handle the line comment as usual. If it has a + // line comment, we can't have both so the one from the key is lost. + emitter.line_comment = emitter.key_line_comment + emitter.key_line_comment = nil + } + } else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) { + // An indented block follows, so write the comment right now. emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment if !yaml_emitter_process_line_comment(emitter) { return false } emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment - // Indent in unless it's a block that will reindent anyway. - if event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE || (event.typ != yaml_MAPPING_START_EVENT && event.typ != yaml_SEQUENCE_START_EVENT) { - emitter.indent = emitter.best_indent*((emitter.indent+emitter.best_indent)/emitter.best_indent) - if !yaml_emitter_write_indent(emitter) { - return false - } - } } } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) @@ -1896,7 +1894,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } - if !put_break(emitter) { + if !yaml_emitter_process_line_comment(emitter) { return false } //emitter.indention = true @@ -1933,10 +1931,10 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } - - if !put_break(emitter) { + if !yaml_emitter_process_line_comment(emitter) { return false } + //emitter.indention = true emitter.whitespace = true diff --git a/vendor/gopkg.in/yaml.v3/encode.go b/vendor/gopkg.in/yaml.v3/encode.go index 45e8d1e1b9..de9e72a3e6 100644 --- a/vendor/gopkg.in/yaml.v3/encode.go +++ b/vendor/gopkg.in/yaml.v3/encode.go @@ -120,6 +120,11 @@ func (e *encoder) marshal(tag string, in reflect.Value) { e.nodev(in) return case Node: + if !in.CanAddr() { + var n = reflect.New(in.Type()).Elem() + n.Set(in) + in = n + } e.nodev(in.Addr()) return case time.Time: diff --git a/vendor/gopkg.in/yaml.v3/scannerc.go b/vendor/gopkg.in/yaml.v3/scannerc.go index d9a539c39a..ca0070108f 100644 --- a/vendor/gopkg.in/yaml.v3/scannerc.go +++ b/vendor/gopkg.in/yaml.v3/scannerc.go @@ -2260,10 +2260,9 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l } } if parser.buffer[parser.buffer_pos] == '#' { - // TODO Test this and then re-enable it. - //if !yaml_parser_scan_line_comment(parser, start_mark) { - // return false - //} + if !yaml_parser_scan_line_comment(parser, start_mark) { + return false + } for !is_breakz(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { @@ -2892,6 +2891,10 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo var token_mark = token.start_mark var start_mark yaml_mark_t + var next_indent = parser.indent + if next_indent < 0 { + next_indent = 0 + } var recent_empty = false var first_empty = parser.newlines <= 1 @@ -2923,15 +2926,18 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo continue } c := parser.buffer[parser.buffer_pos+peek] - if is_breakz(parser.buffer, parser.buffer_pos+peek) || parser.flow_level > 0 && (c == ']' || c == '}') { + var close_flow = parser.flow_level > 0 && (c == ']' || c == '}') + if close_flow || is_breakz(parser.buffer, parser.buffer_pos+peek) { // Got line break or terminator. - if !recent_empty { - if first_empty && (start_mark.line == foot_line || start_mark.column-1 < parser.indent) { + if close_flow || !recent_empty { + if close_flow || first_empty && (start_mark.line == foot_line && token.typ != yaml_VALUE_TOKEN || start_mark.column-1 < next_indent) { // This is the first empty line and there were no empty lines before, // so this initial part of the comment is a foot of the prior token // instead of being a head for the following one. Split it up. + // Alternatively, this might also be the last comment inside a flow + // scope, so it must be a footer. if len(text) > 0 { - if start_mark.column-1 < parser.indent { + if start_mark.column-1 < next_indent { // If dedented it's unrelated to the prior token. token_mark = start_mark } @@ -2962,7 +2968,7 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo continue } - if len(text) > 0 && column < parser.indent+1 && column != start_mark.column { + if len(text) > 0 && (close_flow || column-1 < next_indent && column != start_mark.column) { // The comment at the different indentation is a foot of the // preceding data rather than a head of the upcoming one. parser.comments = append(parser.comments, yaml_comment_t{ @@ -3013,6 +3019,10 @@ func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) boo peek = 0 column = 0 line = parser.mark.line + next_indent = parser.indent + if next_indent < 0 { + next_indent = 0 + } } if len(text) > 0 { diff --git a/vendor/gopkg.in/yaml.v3/yaml.go b/vendor/gopkg.in/yaml.v3/yaml.go index 56e8a84903..8cec6da48d 100644 --- a/vendor/gopkg.in/yaml.v3/yaml.go +++ b/vendor/gopkg.in/yaml.v3/yaml.go @@ -449,6 +449,11 @@ func (n *Node) ShortTag() string { case ScalarNode: tag, _ := resolve("", n.Value) return tag + case 0: + // Special case to make the zero value convenient. + if n.IsZero() { + return nullTag + } } return "" } diff --git a/vendor/modules.txt b/vendor/modules.txt index c3637cfff2..9d272541ea 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -67,7 +67,7 @@ github.com/PuerkitoBio/purell github.com/PuerkitoBio/urlesc # github.com/adrg/xdg v0.3.1 github.com/adrg/xdg -# github.com/alessio/shellescape v1.2.2 +# github.com/alessio/shellescape v1.4.1 github.com/alessio/shellescape # github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 github.com/asaskevich/govalidator @@ -187,7 +187,7 @@ github.com/emicklei/go-restful github.com/emicklei/go-restful/log # github.com/evanphx/json-patch v4.9.0+incompatible github.com/evanphx/json-patch -# github.com/evanphx/json-patch/v5 v5.1.0 +# github.com/evanphx/json-patch/v5 v5.2.0 github.com/evanphx/json-patch/v5 # github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d github.com/exponent-io/jsonpath @@ -446,7 +446,7 @@ github.com/openshift/api/operator/v1 github.com/openshift/custom-resource-status/conditions/v1 # github.com/pborman/uuid v1.2.0 github.com/pborman/uuid -# github.com/pelletier/go-toml v1.8.0 +# github.com/pelletier/go-toml v1.8.1 github.com/pelletier/go-toml # github.com/peterbourgon/diskv v2.0.1+incompatible github.com/peterbourgon/diskv @@ -1020,7 +1020,7 @@ gopkg.in/tomb.v1 # gopkg.in/yaml.v2 v2.4.0 ## explicit gopkg.in/yaml.v2 -# gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 +# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b gopkg.in/yaml.v3 # helm.sh/helm/v3 v3.5.2 ## explicit @@ -1526,6 +1526,8 @@ k8s.io/utils/trace # kubevirt.io/client-go v0.40.0 => github.com/kubevirt/client-go v0.40.0 ## explicit kubevirt.io/client-go/api/v1 +kubevirt.io/client-go/apis/snapshot +kubevirt.io/client-go/apis/snapshot/v1alpha1 kubevirt.io/client-go/precond # kubevirt.io/containerized-data-importer v1.31.0 => github.com/rancher/kubevirt-containerized-data-importer v1.26.1-0.20210303063201-9e7a78643487 ## explicit @@ -1543,7 +1545,7 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client sigs.k8s.io/apiserver-network-proxy/konnectivity-client/proto/client # sigs.k8s.io/cli-utils v0.16.0 sigs.k8s.io/cli-utils/pkg/kstatus/status -# sigs.k8s.io/kind v0.9.0 +# sigs.k8s.io/kind v0.11.1 ## explicit sigs.k8s.io/kind/pkg/apis/config/defaults sigs.k8s.io/kind/pkg/apis/config/v1alpha4 @@ -1571,6 +1573,8 @@ sigs.k8s.io/kind/pkg/cluster/internal/providers/docker sigs.k8s.io/kind/pkg/cluster/internal/providers/podman sigs.k8s.io/kind/pkg/cluster/nodes sigs.k8s.io/kind/pkg/cluster/nodeutils +sigs.k8s.io/kind/pkg/cmd +sigs.k8s.io/kind/pkg/cmd/kind/version sigs.k8s.io/kind/pkg/errors sigs.k8s.io/kind/pkg/exec sigs.k8s.io/kind/pkg/fs diff --git a/vendor/sigs.k8s.io/kind/pkg/apis/config/defaults/image.go b/vendor/sigs.k8s.io/kind/pkg/apis/config/defaults/image.go index 4ca7cb26a1..96371c2c95 100644 --- a/vendor/sigs.k8s.io/kind/pkg/apis/config/defaults/image.go +++ b/vendor/sigs.k8s.io/kind/pkg/apis/config/defaults/image.go @@ -18,4 +18,4 @@ limitations under the License. package defaults // Image is the default for the Config.Image field, aka the default node image. -const Image = "kindest/node:v1.19.1@sha256:98cf5288864662e37115e362b23e4369c8c4a408f99cbc06e58ac30ddc721600" +const Image = "kindest/node:v1.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6" diff --git a/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/default.go b/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/default.go index 85cc167ea8..4626fdd41b 100644 --- a/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/default.go +++ b/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/default.go @@ -37,21 +37,26 @@ func SetDefaultsCluster(obj *Cluster) { SetDefaultsNode(a) } if obj.Networking.IPFamily == "" { - obj.Networking.IPFamily = "ipv4" + obj.Networking.IPFamily = IPv4Family } // default to listening on 127.0.0.1:randomPort on ipv4 // and [::1]:randomPort on ipv6 if obj.Networking.APIServerAddress == "" { obj.Networking.APIServerAddress = "127.0.0.1" - if obj.Networking.IPFamily == "ipv6" { + if obj.Networking.IPFamily == IPv6Family { obj.Networking.APIServerAddress = "::1" } } // default the pod CIDR if obj.Networking.PodSubnet == "" { obj.Networking.PodSubnet = "10.244.0.0/16" - if obj.Networking.IPFamily == "ipv6" { - obj.Networking.PodSubnet = "fd00:10:244::/64" + if obj.Networking.IPFamily == IPv6Family { + // node-mask cidr default is /64 so we need a larger subnet, we use /56 following best practices + // xref: https://www.ripe.net/publications/docs/ripe-690#4--size-of-end-user-prefix-assignment---48---56-or-something-else- + obj.Networking.PodSubnet = "fd00:10:244::/56" + } + if obj.Networking.IPFamily == DualStackFamily { + obj.Networking.PodSubnet = "10.244.0.0/16,fd00:10:244::/56" } } // default the service CIDR using a different subnet than kubeadm default @@ -60,13 +65,16 @@ func SetDefaultsCluster(obj *Cluster) { // we allocate a /16 subnet that allows 65535 services (current Kubernetes tested limit is O(10k) services) if obj.Networking.ServiceSubnet == "" { obj.Networking.ServiceSubnet = "10.96.0.0/16" - if obj.Networking.IPFamily == "ipv6" { + if obj.Networking.IPFamily == IPv6Family { obj.Networking.ServiceSubnet = "fd00:10:96::/112" } + if obj.Networking.IPFamily == DualStackFamily { + obj.Networking.ServiceSubnet = "10.96.0.0/16,fd00:10:96::/112" + } } // default the KubeProxyMode using iptables as it's already the default if obj.Networking.KubeProxyMode == "" { - obj.Networking.KubeProxyMode = IPTablesMode + obj.Networking.KubeProxyMode = IPTablesProxyMode } } diff --git a/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/types.go b/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/types.go index 9722c3d995..c6cfafbdea 100644 --- a/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/types.go +++ b/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/types.go @@ -54,7 +54,7 @@ type Cluster struct { // // https://tools.ietf.org/html/rfc7386 // - // The cluster-level patches are appied before the node-level patches. + // The cluster-level patches are applied before the node-level patches. KubeadmConfigPatches []string `yaml:"kubeadmConfigPatches,omitempty"` // KubeadmConfigPatchesJSON6902 are applied to the generated kubeadm config @@ -69,7 +69,7 @@ type Cluster struct { // // https://tools.ietf.org/html/rfc6902 // - // The cluster-level patches are appied before the node-level patches. + // The cluster-level patches are applied before the node-level patches. KubeadmConfigPatchesJSON6902 []PatchJSON6902 `yaml:"kubeadmConfigPatchesJSON6902,omitempty"` // ContainerdConfigPatches are applied to every node's containerd config @@ -104,6 +104,9 @@ type Node struct { // If unset a default image will be used, see defaults.Image Image string `yaml:"image,omitempty"` + // Labels are the labels with which the respective node will be labeled + Labels map[string]string `yaml:"labels,omitempty"` + /* Advanced fields */ // TODO: cri-like types should be inline instead @@ -196,16 +199,18 @@ const ( IPv4Family ClusterIPFamily = "ipv4" // IPv6Family sets ClusterIPFamily to ipv6 IPv6Family ClusterIPFamily = "ipv6" + // DualStackFamily sets ClusterIPFamily to dual + DualStackFamily ClusterIPFamily = "dual" ) // ProxyMode defines a proxy mode for kube-proxy type ProxyMode string const ( - // IPTablesMode sets ProxyMode to iptables - IPTablesMode ProxyMode = "iptables" - // IPVSMode sets ProxyMode to iptables - IPVSMode ProxyMode = "ipvs" + // IPTablesProxyMode sets ProxyMode to iptables + IPTablesProxyMode ProxyMode = "iptables" + // IPVSProxyMode sets ProxyMode to ipvs + IPVSProxyMode ProxyMode = "ipvs" ) // PatchJSON6902 represents an inline kustomize json 6902 patch @@ -228,7 +233,7 @@ https://github.com/kubernetes/kubernetes/blob/063e7ff358fdc8b0916e6f39beedc0d025 // This is a close copy of the upstream cri Mount type // see: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2 // It additionally serializes the "propagation" field with the string enum -// names on disk as opposed to the int32 values, and the serlialzed field names +// names on disk as opposed to the int32 values, and the serialized field names // have been made closer to core/v1 VolumeMount field names // In yaml this looks like: // containerPath: /foo diff --git a/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/zz_generated.deepcopy.go b/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/zz_generated.deepcopy.go index 6fa61d4e34..8aab0fa122 100644 --- a/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/zz_generated.deepcopy.go +++ b/vendor/sigs.k8s.io/kind/pkg/apis/config/v1alpha4/zz_generated.deepcopy.go @@ -114,6 +114,13 @@ func (in *Networking) DeepCopy() *Networking { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Node) DeepCopyInto(out *Node) { *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.ExtraMounts != nil { in, out := &in.ExtraMounts, &out.ExtraMounts *out = make([]Mount, len(*in)) diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/config/config.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/config/config.go index 69d2773181..b6f1e4dd17 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/config/config.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/config/config.go @@ -20,6 +20,7 @@ package config import ( "bytes" "fmt" + "net" "strings" "sigs.k8s.io/kind/pkg/cluster/constants" @@ -47,6 +48,11 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { ctx.Status.Start("Writing configuration 📜") defer ctx.Status.End(false) + providerInfo, err := ctx.Provider.Info() + if err != nil { + return err + } + allNodes, err := ctx.Nodes() if err != nil { return err @@ -60,8 +66,9 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { // create kubeadm init config fns := []func() error{} + provider := fmt.Sprintf("%s", ctx.Provider) configData := kubeadm.ConfigData{ - NodeProvider: fmt.Sprintf("%s", ctx.Provider), + NodeProvider: provider, ClusterName: ctx.Config.Name, ControlPlaneEndpoint: controlPlaneEndpoint, APIBindPort: common.APIServerInternalPort, @@ -71,15 +78,16 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { KubeProxyMode: string(ctx.Config.Networking.KubeProxyMode), ServiceSubnet: ctx.Config.Networking.ServiceSubnet, ControlPlane: true, - IPv6: ctx.Config.Networking.IPFamily == "ipv6", + IPFamily: ctx.Config.Networking.IPFamily, FeatureGates: ctx.Config.FeatureGates, RuntimeConfig: ctx.Config.RuntimeConfig, + RootlessProvider: providerInfo.Rootless, } kubeadmConfigPlusPatches := func(node nodes.Node, data kubeadm.ConfigData) func() error { return func() error { data.NodeName = node.String() - kubeadmConfig, err := getKubeadmConfig(ctx.Config, data, node) + kubeadmConfig, err := getKubeadmConfig(ctx.Config, data, node, provider) if err != nil { // TODO(bentheelder): logging here return errors.Wrap(err, "failed to generate kubeadm config content") @@ -90,15 +98,42 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { } } + // Populate the list of control-plane node labels and the list of worker node labels respectively. + // controlPlaneLabels is an array of maps (labels, read from config) associated with all the control-plane nodes. + // workerLabels is an array of maps (labels, read from config) associated with all the worker nodes. + controlPlaneLabels := []map[string]string{} + workerLabels := []map[string]string{} + for _, node := range ctx.Config.Nodes { + if node.Role == config.ControlPlaneRole { + controlPlaneLabels = append(controlPlaneLabels, node.Labels) + } else if node.Role == config.WorkerRole { + workerLabels = append(workerLabels, node.Labels) + } else { + continue + } + } + + // hashMapLabelsToCommaSeparatedLabels converts labels in hashmap form to labels in a comma-separated string form like "key1=value1,key2=value2" + hashMapLabelsToCommaSeparatedLabels := func(labels map[string]string) string { + output := "" + for key, value := range labels { + output += fmt.Sprintf("%s=%s,", key, value) + } + return strings.TrimSuffix(output, ",") // remove the last character (comma) in the output string + } + // create the kubeadm join configuration for control plane nodes controlPlanes, err := nodeutils.ControlPlaneNodes(allNodes) if err != nil { return err } - for _, node := range controlPlanes { + for i, node := range controlPlanes { node := node // capture loop variable configData := configData // copy config data + if len(controlPlaneLabels[i]) > 0 { + configData.NodeLabels = hashMapLabelsToCommaSeparatedLabels(controlPlaneLabels[i]) // updating the config with the respective labels to be written over the current control-plane node in consideration + } fns = append(fns, kubeadmConfigPlusPatches(node, configData)) } @@ -109,10 +144,13 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { } if len(workers) > 0 { // create the workers concurrently - for _, node := range workers { + for i, node := range workers { node := node // capture loop variable configData := configData // copy config data configData.ControlPlane = false + if len(workerLabels[i]) > 0 { + configData.NodeLabels = hashMapLabelsToCommaSeparatedLabels(workerLabels[i]) // updating the config with the respective labels to be written over the current worker node in consideration + } fns = append(fns, kubeadmConfigPlusPatches(node, configData)) } } @@ -141,7 +179,7 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { } patched, err := patch.TOML(buff.String(), ctx.Config.ContainerdConfigPatches, ctx.Config.ContainerdConfigPatchesJSON6902) if err != nil { - return errors.Wrap(err, "failed to patch contianerd config") + return errors.Wrap(err, "failed to patch containerd config") } if err := nodeutils.WriteFile(node, containerdConfigPath, patched); err != nil { return errors.Wrap(err, "failed to write patched containerd config") @@ -166,7 +204,7 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { // getKubeadmConfig generates the kubeadm config contents for the cluster // by running data through the template and applying patches as needed. -func getKubeadmConfig(cfg *config.Cluster, data kubeadm.ConfigData, node nodes.Node) (path string, err error) { +func getKubeadmConfig(cfg *config.Cluster, data kubeadm.ConfigData, node nodes.Node, provider string) (path string, err error) { kubeVersion, err := nodeutils.KubeVersion(node) if err != nil { // TODO(bentheelder): logging here @@ -199,11 +237,14 @@ func getKubeadmConfig(cfg *config.Cluster, data kubeadm.ConfigData, node nodes.N data.NodeAddress = nodeAddress // configure the right protocol addresses - if cfg.Networking.IPFamily == "ipv6" { - if nodeAddressIPv6 == "" { - return "", errors.Errorf("failed to get IPV6 address; is the docker daemon configured to use IPV6 correctly?") + if cfg.Networking.IPFamily == config.IPv6Family || cfg.Networking.IPFamily == config.DualStackFamily { + if ip := net.ParseIP(nodeAddressIPv6); ip.To16() == nil { + return "", errors.Errorf("failed to get IPv6 address for node %s; is %s configured to use IPv6 correctly?", node.String(), provider) } data.NodeAddress = nodeAddressIPv6 + if cfg.Networking.IPFamily == config.DualStackFamily { + data.NodeAddress = fmt.Sprintf("%s,%s", nodeAddress, nodeAddressIPv6) + } } // generate the config contents diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/installcni/cni.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/installcni/cni.go index 94f9ed41c6..f7f6da0fc2 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/installcni/cni.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/installcni/cni.go @@ -23,8 +23,10 @@ import ( "text/template" "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/internal/apis/config" "sigs.k8s.io/kind/pkg/cluster/internal/create/actions" + "sigs.k8s.io/kind/pkg/cluster/internal/patch" "sigs.k8s.io/kind/pkg/cluster/nodeutils" ) @@ -83,6 +85,40 @@ func (a *action) Execute(ctx *actions.ActionContext) error { manifest = out.String() } + // NOTE: this is intentionally undocumented, as an internal implementation + // detail. Going forward users should disable the default CNI and install + // their own, or use the default. The internal templating mechanism is + // not intended for external usage and is unstable. + if strings.Contains(manifest, "would you kindly patch this file") { + // Add the controlplane endpoint so kindnet doesn´t have to wait for kube-proxy + controlPlaneEndpoint, err := ctx.Provider.GetAPIServerInternalEndpoint(ctx.Config.Name) + if err != nil { + return err + } + + patchValue := ` +- op: add + path: /spec/template/spec/containers/0/env/- + value: + name: CONTROL_PLANE_ENDPOINT + value: ` + controlPlaneEndpoint + + controlPlanePatch6902 := config.PatchJSON6902{ + Group: "apps", + Version: "v1", + Kind: "DaemonSet", + Patch: patchValue, + } + + patchedConfig, err := patch.KubeYAML(manifest, nil, []config.PatchJSON6902{controlPlanePatch6902}) + if err != nil { + return err + } + manifest = patchedConfig + } + + ctx.Logger.V(5).Infof("Using the following Kindnetd config:\n%s", manifest) + // install the manifest if err := node.Command( "kubectl", "create", "--kubeconfig=/etc/kubernetes/admin.conf", diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/installstorage/storage.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/installstorage/storage.go index 69db58109b..fa9a095b09 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/installstorage/storage.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/installstorage/storage.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package installstorage implements the an action to isntall a default +// Package installstorage implements the an action to install a default // storageclass package installstorage diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/kubeadminit/init.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/kubeadminit/init.go index d82c2c20ad..9c596cb1f1 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/kubeadminit/init.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/kubeadminit/init.go @@ -26,16 +26,19 @@ import ( "sigs.k8s.io/kind/pkg/cluster/nodeutils" "sigs.k8s.io/kind/pkg/cluster/internal/create/actions" + "sigs.k8s.io/kind/pkg/internal/apis/config" ) -// kubeadmInitAction implements action for executing the kubadm init +// kubeadmInitAction implements action for executing the kubeadm init // and a set of default post init operations like e.g. install the // CNI network plugin. -type action struct{} +type action struct { + skipKubeProxy bool +} // NewAction returns a new action for kubeadm init -func NewAction() actions.Action { - return &action{} +func NewAction(cfg *config.Cluster) actions.Action { + return &action{skipKubeProxy: cfg.Networking.KubeProxyMode == config.NoneProxyMode} } // Execute runs the action @@ -56,13 +59,18 @@ func (a *action) Execute(ctx *actions.ActionContext) error { return err } + // skip preflight checks, as these have undesirable side effects + // and don't tell us much. requires kubeadm 1.13+ + skipPhases := "preflight" + if a.skipKubeProxy { + skipPhases += ",addon/kube-proxy" + } + // run kubeadm cmd := node.Command( // init because this is the control plane node "kubeadm", "init", - // skip preflight checks, as these have undesirable side effects - // and don't tell us much. requires kubeadm 1.13+ - "--skip-phases=preflight", + "--skip-phases="+skipPhases, // specify our generated config file "--config=/kind/kubeadm.conf", "--skip-token-print", diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/kubeadmjoin/join.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/kubeadmjoin/join.go index 3b53dfe398..fbd33555d3 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/kubeadmjoin/join.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/kubeadmjoin/join.go @@ -32,7 +32,7 @@ import ( ) // Action implements action for creating the kubeadm join -// and deployng it on the bootrap control-plane node. +// and deploying it on the bootstrap control-plane node. type Action struct{} // NewAction returns a new action for creating the kubeadm jion @@ -115,7 +115,7 @@ func joinWorkers( return nil } -// runKubeadmJoin executes kubadm join command +// runKubeadmJoin executes kubeadm join command func runKubeadmJoin(logger log.Logger, node nodes.Node) error { // run kubeadm join // TODO(bentheelder): this should be using the config file diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/waitforready/waitforready.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/waitforready/waitforready.go index a6bf7b9792..1dfbf6c29b 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/waitforready/waitforready.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/actions/waitforready/waitforready.go @@ -93,7 +93,7 @@ func waitForReady(node nodes.Node, until time.Time) bool { // to true. "-o=jsonpath='{.items..status.conditions[-1:].status}'", ) - lines, err := exec.CombinedOutputLines(cmd) + lines, err := exec.OutputLines(cmd) if err != nil { return false } @@ -104,7 +104,7 @@ func waitForReady(node nodes.Node, until time.Time) bool { status := strings.Fields(lines[0]) for _, s := range status { // Check node status. If node is ready then this will be 'True', - // 'False' or 'Unkown' otherwise. + // 'False' or 'Unknown' otherwise. if !strings.Contains(s, "True") { return false } diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/create.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/create.go index 9d96b7ebec..e89d789605 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/create.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/create/create.go @@ -19,7 +19,6 @@ package create import ( "fmt" "math/rand" - "regexp" "time" "github.com/alessio/shellescape" @@ -49,12 +48,6 @@ const ( clusterNameMax = 50 ) -// similar to valid docker container names, but since we will prefix -// and suffix this name, we can relax it a little -// see NewContext() for usage -// https://godoc.org/github.com/docker/docker/daemon/names#pkg-constants -var validNameRE = regexp.MustCompile(`^[a-z0-9_.-]+$`) - // ClusterOptions holds cluster creation options type ClusterOptions struct { Config *config.Cluster @@ -73,6 +66,11 @@ type ClusterOptions struct { // Cluster creates a cluster func Cluster(logger log.Logger, p providers.Provider, opts *ClusterOptions) error { + // validate provider first + if err := validateProvider(p); err != nil { + return err + } + // default / process options (namely config) if err := fixupOptions(opts); err != nil { return err @@ -83,14 +81,6 @@ func Cluster(logger log.Logger, p providers.Provider, opts *ClusterOptions) erro return err } - // TODO: move to config validation - // validate the name - if !validNameRE.MatchString(opts.Config.Name) { - return errors.Errorf( - "'%s' is not a valid cluster name, cluster names must match `%s`", - opts.Config.Name, validNameRE.String(), - ) - } // warn if cluster name might typically be too long if len(opts.Config.Name) > clusterNameMax { logger.Warnf("cluster name %q is probably too long, this might not work properly on some systems", opts.Config.Name) @@ -123,7 +113,7 @@ func Cluster(logger log.Logger, p providers.Provider, opts *ClusterOptions) erro } if !opts.StopBeforeSettingUpKubernetes { actionsToRun = append(actionsToRun, - kubeadminit.NewAction(), // run kubeadm init + kubeadminit.NewAction(opts.Config), // run kubeadm init ) // this step might be skipped, but is next after init if !opts.Config.Networking.DisableDefaultCNI { @@ -249,3 +239,19 @@ func fixupOptions(opts *ClusterOptions) error { return nil } + +func validateProvider(p providers.Provider) error { + info, err := p.Info() + if err != nil { + return err + } + if info.Rootless { + if !info.Cgroup2 { + return errors.New("running kind with rootless provider requires cgroup v2, see https://kind.sigs.k8s.io/docs/user/rootless/") + } + if !info.SupportsMemoryLimit || !info.SupportsPidsLimit || !info.SupportsCPUShares { + return errors.New("running kind with rootless provider requires setting systemd property \"Delegate=yes\", see https://kind.sigs.k8s.io/docs/user/rootless/") + } + } + return nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeadm/config.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeadm/config.go index 4eeb8a4bfd..c1241b3d92 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeadm/config.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeadm/config.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/util/version" "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/internal/apis/config" ) // ConfigData is supplied to the kubeadm config template, with values populated @@ -46,7 +47,7 @@ type ConfigData struct { // ControlPlane flag specifies the node belongs to the control plane ControlPlane bool - // The main IP address of the node + // The IP address or comma separated list IP addresses of of the node NodeAddress string // The name for the node (not the address) NodeName string @@ -67,18 +68,27 @@ type ConfigData struct { // Kubernetes API Server RuntimeConfig RuntimeConfig map[string]string - // IPv4 values take precedence over IPv6 by default, if true set IPv6 default values - IPv6 bool + // IPFamily of the cluster, it can be IPv4, IPv6 or DualStack + IPFamily config.ClusterIPFamily + + // Labels are the labels, in the format "key1=val1,key2=val2", with which the respective node will be labeled + NodeLabels string // DerivedConfigData is populated by Derive() // These auto-generated fields are available to Config templates, // but not meant to be set by hand DerivedConfigData + + // Provider is running with rootless mode, so kube-proxy needs to be configured + // not to fail on sysctl error. + RootlessProvider bool } // DerivedConfigData fields are automatically derived by // ConfigData.Derive if they are not specified / zero valued type DerivedConfigData struct { + // AdvertiseAddress is the first address in NodeAddress + AdvertiseAddress string // DockerStableTag is automatically derived from KubernetesVersion DockerStableTag string // SortedFeatureGateKeys allows us to iterate FeatureGates deterministically @@ -87,14 +97,24 @@ type DerivedConfigData struct { FeatureGatesString string // RuntimeConfigString is of the form `Foo=true,Baz=false` RuntimeConfigString string + // KubeadmFeatureGates contains Kubeadm only feature gates + KubeadmFeatureGates map[string]bool + // IPv4 values take precedence over IPv6 by default, if true set IPv6 default values + IPv6 bool } // Derive automatically derives DockerStableTag if not specified func (c *ConfigData) Derive() { + // get the first address to use it as the API advertised address + c.AdvertiseAddress = strings.Split(c.NodeAddress, ",")[0] + if c.DockerStableTag == "" { c.DockerStableTag = strings.Replace(c.KubernetesVersion, "+", "_", -1) } + // get the IP addresses family for defaulting components + c.IPv6 = c.IPFamily == config.IPv6Family + // get sorted list of FeatureGate keys featureGateKeys := make([]string, 0, len(c.FeatureGates)) for k := range c.FeatureGates { @@ -134,7 +154,7 @@ func (c *ConfigData) Derive() { // EG: // https://godoc.org/k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1 -// ConfigTemplateBetaV1 is the kubadm config template for API version v1beta1 +// ConfigTemplateBetaV1 is the kubeadm config template for API version v1beta1 const ConfigTemplateBetaV1 = `# config generated by kind apiVersion: kubeadm.k8s.io/v1beta1 kind: ClusterConfiguration @@ -187,7 +207,7 @@ bootstrapTokens: # we use a well know port for making the API server discoverable inside docker network. # from the host machine such port will be accessible via a random local port instead. localAPIEndpoint: - advertiseAddress: "{{ .NodeAddress }}" + advertiseAddress: "{{ .AdvertiseAddress }}" bindPort: {{.APIBindPort}} nodeRegistration: criSocket: "/run/containerd/containerd.sock" @@ -204,7 +224,7 @@ metadata: {{ if .ControlPlane -}} controlPlane: localAPIEndpoint: - advertiseAddress: "{{ .NodeAddress }}" + advertiseAddress: "{{ .AdvertiseAddress }}" bindPort: {{.APIBindPort}} {{- end }} nodeRegistration: @@ -223,6 +243,11 @@ apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration metadata: name: config +# explicitly set default cgroup driver +# unblocks https://github.com/kubernetes/kubernetes/pull/99471 +# TODO: consider switching to systemd instead +# tracked in: https://github.com/kubernetes-sigs/kind/issues/1726 +cgroupDriver: cgroupfs # configure ipv6 addresses in IPv6 mode {{ if .IPv6 -}} address: "::" @@ -240,6 +265,7 @@ evictionHard: {{ range $key := .SortedFeatureGateKeys }} "{{ $key }}": {{$.FeatureGates $key }} {{end}}{{end}} +{{if ne .KubeProxyMode "None"}} --- apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration @@ -252,9 +278,14 @@ mode: "{{ .KubeProxyMode }}" {{end}}{{end}} iptables: minSyncPeriod: 1s +conntrack: +# Skip setting sysctl value "net.netfilter.nf_conntrack_max" +# It is a global variable that affects other namespaces + maxPerCore: 0 +{{end}} ` -// ConfigTemplateBetaV2 is the kubadm config template for API version v1beta2 +// ConfigTemplateBetaV2 is the kubeadm config template for API version v1beta2 const ConfigTemplateBetaV2 = `# config generated by kind apiVersion: kubeadm.k8s.io/v1beta2 kind: ClusterConfiguration @@ -262,6 +293,10 @@ metadata: name: config kubernetesVersion: {{.KubernetesVersion}} clusterName: "{{.ClusterName}}" +{{ if .KubeadmFeatureGates}}featureGates: +{{ range $key, $value := .KubeadmFeatureGates }} + "{{ $key }}": {{ $value }} +{{end}}{{end}} controlPlaneEndpoint: "{{ .ControlPlaneEndpoint }}" # on docker for mac we have to expose the api server via port forward, # so we need to ensure the cert is valid for localhost so we can talk @@ -307,7 +342,7 @@ bootstrapTokens: # we use a well know port for making the API server discoverable inside docker network. # from the host machine such port will be accessible via a random local port instead. localAPIEndpoint: - advertiseAddress: "{{ .NodeAddress }}" + advertiseAddress: "{{ .AdvertiseAddress }}" bindPort: {{.APIBindPort}} nodeRegistration: criSocket: "unix:///run/containerd/containerd.sock" @@ -315,6 +350,7 @@ nodeRegistration: fail-swap-on: "false" node-ip: "{{ .NodeAddress }}" provider-id: "kind://{{.NodeProvider}}/{{.ClusterName}}/{{.NodeName}}" + node-labels: "{{ .NodeLabels }}" --- # no-op entry that exists solely so it can be patched apiVersion: kubeadm.k8s.io/v1beta2 @@ -324,7 +360,7 @@ metadata: {{ if .ControlPlane -}} controlPlane: localAPIEndpoint: - advertiseAddress: "{{ .NodeAddress }}" + advertiseAddress: "{{ .AdvertiseAddress }}" bindPort: {{.APIBindPort}} {{- end }} nodeRegistration: @@ -333,6 +369,7 @@ nodeRegistration: fail-swap-on: "false" node-ip: "{{ .NodeAddress }}" provider-id: "kind://{{.NodeProvider}}/{{.ClusterName}}/{{.NodeName}}" + node-labels: "{{ .NodeLabels }}" discovery: bootstrapToken: apiServerEndpoint: "{{ .ControlPlaneEndpoint }}" @@ -343,6 +380,11 @@ apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration metadata: name: config +# explicitly set default cgroup driver +# unblocks https://github.com/kubernetes/kubernetes/pull/99471 +# TODO: consider switching to systemd instead +# tracked in: https://github.com/kubernetes-sigs/kind/issues/1726 +cgroupDriver: cgroupfs # configure ipv6 addresses in IPv6 mode {{ if .IPv6 -}} address: "::" @@ -360,6 +402,7 @@ evictionHard: {{ range $key := .SortedFeatureGateKeys }} "{{ $key }}": {{ index $.FeatureGates $key }} {{end}}{{end}} +{{if ne .KubeProxyMode "None"}} --- apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration @@ -372,6 +415,16 @@ mode: "{{ .KubeProxyMode }}" {{end}}{{end}} iptables: minSyncPeriod: 1s +conntrack: +# Skip setting sysctl value "net.netfilter.nf_conntrack_max" +# It is a global variable that affects other namespaces + maxPerCore: 0 +{{if .RootlessProvider}} +# Skip setting "net.netfilter.nf_conntrack_tcp_timeout_established" + tcpEstablishedTimeout: 0s +# Skip setting "net.netfilter.nf_conntrack_tcp_timeout_close" + tcpCloseWaitTimeout: 0s +{{end}}{{end}} ` // Config returns a kubeadm config generated from config data, in particular @@ -390,6 +443,9 @@ func Config(data ConfigData) (config string, err error) { // assume the latest API version, then fallback if the k8s version is too low templateSource := ConfigTemplateBetaV2 if ver.LessThan(version.MustParseSemantic("v1.15.0")) { + if data.RootlessProvider { + return "", errors.Errorf("version %q is not compatible with rootless provider", ver) + } templateSource = ConfigTemplateBetaV1 } @@ -401,6 +457,17 @@ func Config(data ConfigData) (config string, err error) { // derive any automatic fields if not supplied data.Derive() + // Kubeadm has its own feature-gate for dual stack + // we need to enable it for Kubernetes version 1.20 only + // dual-stack is only supported in 1.20+ + // TODO: remove this when 1.20 is EOL or we no longer support + // dual-stack for 1.20 in KIND + if ver.LessThan(version.MustParseSemantic("v1.21.0")) && + ver.AtLeast(version.MustParseSemantic("v1.20.0")) { + data.KubeadmFeatureGates = make(map[string]bool) + data.KubeadmFeatureGates["IPv6DualStack"] = true + } + // execute the template var buff bytes.Buffer err = t.Execute(&buff, data) diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/encode.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/encode.go index eae81165d2..336212eff8 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/encode.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/encode.go @@ -46,7 +46,7 @@ func Encode(cfg *Config) ([]byte, error) { } // normYaml round trips yaml bytes through sigs.k8s.io/yaml to normalize them -// versus other kuberernetes ecosystem yaml output +// versus other kubernetes ecosystem yaml output func normYaml(y []byte) ([]byte, error) { var unstructured interface{} if err := kubeyaml.Unmarshal(y, &unstructured); err != nil { diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/types.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/types.go index a2e0f55cb0..1da5df5451 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/types.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/types.go @@ -30,11 +30,11 @@ We've forked them to: // Other fields are handled as unstructured data purely read for writing back // to disk via the OtherFields field type Config struct { - // Clusters is a map of referencable names to cluster configs + // Clusters is a map of referenceable names to cluster configs Clusters []NamedCluster `yaml:"clusters,omitempty"` - // Users is a map of referencable names to user configs + // Users is a map of referenceable names to user configs Users []NamedUser `yaml:"users,omitempty"` - // Contexts is a map of referencable names to context configs + // Contexts is a map of referenceable names to context configs Contexts []NamedContext `yaml:"contexts,omitempty"` // CurrentContext is the name of the context that you would like to use by default CurrentContext string `yaml:"current-context,omitempty"` diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/kubeconfig.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/kubeconfig.go index 63d46c6595..3763980f7b 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/kubeconfig.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/kubeconfig.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package kubeconfig provides utilites kind uses internally to manage +// Package kubeconfig provides utilities kind uses internally to manage // kind cluster kubeconfigs package kubeconfig diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/patch/mergepatch.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/patch/mergepatch.go index 7b8679f73c..c52b9f55e8 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/patch/mergepatch.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/patch/mergepatch.go @@ -30,7 +30,16 @@ type mergePatch struct { func parseMergePatches(rawPatches []string) ([]mergePatch, error) { patches := []mergePatch{} + // split document streams before trying to parse them + splitRawPatches := make([]string, 0, len(rawPatches)) for _, raw := range rawPatches { + splitRaw, err := splitYAMLDocuments(raw) + if err != nil { + return nil, err + } + splitRawPatches = append(splitRawPatches, splitRaw...) + } + for _, raw := range splitRawPatches { matchInfo, err := parseYAMLMatchInfo(raw) if err != nil { return nil, errors.WithStack(err) diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/logs.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/logs.go index 4ff618ed32..275de5d888 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/logs.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/logs.go @@ -22,6 +22,7 @@ func CollectLogs(n nodes.Node, dir string) error { return cmd.SetStdout(f).SetStderr(f).Run() } } + return errors.AggregateConcurrent([]func() error{ // record info about the node container execToPathFn( diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/network.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/network.go index d73347294c..b34041e6bd 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/network.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/network.go @@ -21,10 +21,12 @@ import ( "crypto/sha1" "encoding/binary" "encoding/json" + "fmt" "io" "net" "regexp" "sort" + "strconv" "strings" "sigs.k8s.io/kind/pkg/errors" @@ -37,7 +39,7 @@ import ( // By default currently picking a single network is equivalent to the previous // behavior *except* that we moved from the default bridge to a user defined // network because the default bridge is actually special versus any other -// docker network and lacks the emebdded DNS +// docker network and lacks the embedded DNS // // For now this also makes it easier for apps to join the same network, and // leaves users with complex networking desires to create and manage their own @@ -61,9 +63,11 @@ func ensureNetwork(name string) error { // Generate unique subnet per network based on the name // obtained from the ULA fc00::/8 range + // Use the MTU configured for the docker default network // Make N attempts with "probing" in case we happen to collide subnet := generateULASubnetFromName(name, 0) - err = createNetworkNoDuplicates(name, subnet) + mtu := getDefaultNetworkMTU() + err = createNetworkNoDuplicates(name, subnet, mtu) if err == nil { // Success! return nil @@ -75,7 +79,7 @@ func ensureNetwork(name string) error { // If it is, make more attempts below if isIPv6UnavailableError(err) { // only one attempt, IPAM is automatic in ipv4 only - return createNetworkNoDuplicates(name, "") + return createNetworkNoDuplicates(name, "", mtu) } if isPoolOverlapError(err) { // pool overlap suggests perhaps another process created the network @@ -97,7 +101,7 @@ func ensureNetwork(name string) error { const maxAttempts = 5 for attempt := int32(1); attempt < maxAttempts; attempt++ { subnet := generateULASubnetFromName(name, attempt) - err = createNetworkNoDuplicates(name, subnet) + err = createNetworkNoDuplicates(name, subnet, mtu) if err == nil { // success! return nil @@ -121,8 +125,8 @@ func ensureNetwork(name string) error { return errors.New("exhausted attempts trying to find a non-overlapping subnet") } -func createNetworkNoDuplicates(name, ipv6Subnet string) error { - if err := createNetwork(name, ipv6Subnet); err != nil && !isNetworkAlreadyExistsError(err) { +func createNetworkNoDuplicates(name, ipv6Subnet string, mtu int) error { + if err := createNetwork(name, ipv6Subnet, mtu); err != nil && !isNetworkAlreadyExistsError(err) { return err } _, err := removeDuplicateNetworks(name) @@ -142,15 +146,33 @@ func removeDuplicateNetworks(name string) (bool, error) { return len(networks) > 0, nil } -func createNetwork(name, ipv6Subnet string) error { - if ipv6Subnet == "" { - return exec.Command("docker", "network", "create", "-d=bridge", - "-o", "com.docker.network.bridge.enable_ip_masquerade=true", - name).Run() - } - return exec.Command("docker", "network", "create", "-d=bridge", +func createNetwork(name, ipv6Subnet string, mtu int) error { + args := []string{"network", "create", "-d=bridge", "-o", "com.docker.network.bridge.enable_ip_masquerade=true", - "--ipv6", "--subnet", ipv6Subnet, name).Run() + } + if mtu > 0 { + args = append(args, "-o", fmt.Sprintf("com.docker.network.driver.mtu=%d", mtu)) + } + if ipv6Subnet != "" { + args = append(args, "--ipv6", "--subnet", ipv6Subnet) + } + args = append(args, name) + return exec.Command("docker", args...).Run() +} + +// getDefaultNetworkMTU obtains the MTU from the docker default network +func getDefaultNetworkMTU() int { + cmd := exec.Command("docker", "network", "inspect", "bridge", + "-f", `{{ index .Options "com.docker.network.driver.mtu" }}`) + lines, err := exec.OutputLines(cmd) + if err != nil || len(lines) != 1 { + return 0 + } + mtu, err := strconv.Atoi(lines[0]) + if err != nil { + return 0 + } + return mtu } func sortedNetworksWithName(name string) ([]string, error) { diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provider.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provider.go index 4e0a9e3e1e..35d5344da5 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provider.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provider.go @@ -17,6 +17,8 @@ limitations under the License. package docker import ( + "encoding/csv" + "encoding/json" "fmt" "net" "os" @@ -49,6 +51,7 @@ func NewProvider(logger log.Logger) providers.Provider { // see NewProvider type provider struct { logger log.Logger + info *providers.ProviderInfo } // String implements fmt.Stringer @@ -66,7 +69,7 @@ func (p *provider) Provision(status *cli.Status, cfg *config.Cluster) (err error return err } - // ensure the pre-requesite network exists + // ensure the pre-requisite network exists networkName := fixedNetworkName if n := os.Getenv("KIND_EXPERIMENTAL_DOCKER_NETWORK"); n != "" { p.logger.Warn("WARNING: Overriding docker network due to KIND_EXPERIMENTAL_DOCKER_NETWORK") @@ -246,7 +249,6 @@ func (p *provider) CollectLogs(dir string, nodes []nodes.Node) error { } // construct a slice of methods to collect logs fns := []func() error{ - // TODO(bentheelder): record the kind version here as well // record info about the host docker execToPathFn( exec.Command("docker", "info"), @@ -282,3 +284,62 @@ func (p *provider) CollectLogs(dir string, nodes []nodes.Node) error { errs = append(errs, errors.AggregateConcurrent(fns)) return errors.NewAggregate(errs) } + +// Info returns the provider info. +// The info is cached on the first time of the execution. +func (p *provider) Info() (*providers.ProviderInfo, error) { + var err error + if p.info == nil { + p.info, err = info() + } + return p.info, err +} + +// dockerInfo corresponds to `docker info --format '{{json .}}'` +type dockerInfo struct { + CgroupDriver string `json:"CgroupDriver"` // "systemd", "cgroupfs", "none" + CgroupVersion string `json:"CgroupVersion"` // e.g. "2" + MemoryLimit bool `json:"MemoryLimit"` + PidsLimit bool `json:"PidsLimit"` + CPUShares bool `json:"CPUShares"` + SecurityOptions []string `json:"SecurityOptions"` +} + +func info() (*providers.ProviderInfo, error) { + cmd := exec.Command("docker", "info", "--format", "{{json .}}") + out, err := exec.Output(cmd) + if err != nil { + return nil, errors.Wrap(err, "failed to get docker info") + } + var dInfo dockerInfo + if err := json.Unmarshal(out, &dInfo); err != nil { + return nil, err + } + info := providers.ProviderInfo{ + Cgroup2: dInfo.CgroupVersion == "2", + } + // When CgroupDriver == "none", the MemoryLimit/PidsLimit/CPUShares + // values are meaningless and need to be considered false. + // https://github.com/moby/moby/issues/42151 + if dInfo.CgroupDriver != "none" { + info.SupportsMemoryLimit = dInfo.MemoryLimit + info.SupportsPidsLimit = dInfo.PidsLimit + info.SupportsCPUShares = dInfo.CPUShares + } + for _, o := range dInfo.SecurityOptions { + // o is like "name=seccomp,profile=default", or "name=rootless", + csvReader := csv.NewReader(strings.NewReader(o)) + sliceSlice, err := csvReader.ReadAll() + if err != nil { + return nil, err + } + for _, f := range sliceSlice { + for _, ff := range f { + if ff == "name=rootless" { + info.Rootless = true + } + } + } + } + return &info, nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provision.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provision.go index 7c3cf204e2..501618615e 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provision.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/provision.go @@ -63,7 +63,8 @@ func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs // For now remote docker + multi control plane is not supported apiServerPort = 0 // replaced with random ports apiServerAddress = "127.0.0.1" // only the LB needs to be non-local - if clusterIsIPv6(cfg) { + // only for IPv6 only clusters + if cfg.Networking.IPFamily == config.IPv6Family { apiServerAddress = "::1" // only the LB needs to be non-local } // plan loadbalancer node @@ -134,7 +135,7 @@ func createContainer(args []string) error { } func clusterIsIPv6(cfg *config.Cluster) bool { - return cfg.Networking.IPFamily == "ipv6" + return cfg.Networking.IPFamily == config.IPv6Family || cfg.Networking.IPFamily == config.DualStackFamily } func clusterHasImplicitLoadBalancer(cfg *config.Cluster) bool { @@ -183,6 +184,9 @@ func commonArgs(cluster string, cfg *config.Cluster, networkName string, nodeNam // however this _actually_ means the same thing as always // so the closest thing is on-failure:1, which will retry *once* "--restart=on-failure:1", + // this can be enabled by default in docker daemon.json, so we explicitly + // disable it, we want our entrypoint to be PID1, not docker-init / tini + "--init=false", } // enable IPv6 if necessary @@ -251,6 +255,11 @@ func runArgsForNode(node *config.Node, clusterIPFamily config.ClusterIPFamily, n } args = append(args, mappingArgs...) + switch node.Role { + case config.ControlPlaneRole: + args = append(args, "-e", "KUBECONFIG=/etc/kubernetes/admin.conf") + } + // finally, specify the image to run return append(args, node.Image), nil } @@ -310,7 +319,7 @@ func getProxyEnv(cfg *config.Cluster, networkName string, nodeNames []string) (m func getSubnets(networkName string) ([]string, error) { format := `{{range (index (index . "IPAM") "Config")}}{{index . "Subnet"}} {{end}}` cmd := exec.Command("docker", "network", "inspect", "-f", format, networkName) - lines, err := exec.CombinedOutputLines(cmd) + lines, err := exec.OutputLines(cmd) if err != nil { return nil, errors.Wrap(err, "failed to get subnets") } diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/util.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/util.go index fd8d4faeba..82c1d34a93 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/util.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/docker/util.go @@ -17,6 +17,7 @@ limitations under the License. package docker import ( + "encoding/json" "strings" "sigs.k8s.io/kind/pkg/exec" @@ -35,7 +36,7 @@ func IsAvailable() bool { // usernsRemap checks if userns-remap is enabled in dockerd func usernsRemap() bool { cmd := exec.Command("docker", "info", "--format", "'{{json .SecurityOptions}}'") - lines, err := exec.CombinedOutputLines(cmd) + lines, err := exec.OutputLines(cmd) if err != nil { return false } @@ -48,15 +49,39 @@ func usernsRemap() bool { } // mountDevMapper checks if the Docker storage driver is Btrfs or ZFS +// or if the backing filesystem is Btrfs func mountDevMapper() bool { storage := "" + // check the docker storage driver cmd := exec.Command("docker", "info", "-f", "{{.Driver}}") - lines, err := exec.CombinedOutputLines(cmd) - if err != nil { + lines, err := exec.OutputLines(cmd) + if err != nil || len(lines) != 1 { return false } - if len(lines) > 0 { - storage = strings.ToLower(strings.TrimSpace(lines[0])) + + storage = strings.ToLower(strings.TrimSpace(lines[0])) + if storage == "btrfs" || storage == "zfs" || storage == "devicemapper" { + return true } - return storage == "btrfs" || storage == "zfs" + + // check the backing file system + // docker info -f '{{json .DriverStatus }}' + // [["Backing Filesystem","extfs"],["Supports d_type","true"],["Native Overlay Diff","true"]] + cmd = exec.Command("docker", "info", "-f", "{{json .DriverStatus }}") + lines, err = exec.OutputLines(cmd) + if err != nil || len(lines) != 1 { + return false + } + var dat [][]string + if err := json.Unmarshal([]byte(lines[0]), &dat); err != nil { + return false + } + for _, item := range dat { + if item[0] == "Backing Filesystem" { + storage = strings.ToLower(item[1]) + break + } + } + + return storage == "btrfs" || storage == "zfs" || storage == "xfs" } diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/network.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/network.go new file mode 100644 index 0000000000..87d7e6679e --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/network.go @@ -0,0 +1,135 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package podman + +import ( + "crypto/sha1" + "encoding/binary" + "net" + "regexp" + "strings" + + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/exec" +) + +// This may be overridden by KIND_EXPERIMENTAL_PODMAN_NETWORK env, +// experimentally... +// +// By default currently picking a single network is equivalent to the previous +// behavior *except* that we moved from the default bridge to a user defined +// network because the default bridge is actually special versus any other +// docker network and lacks the embedded DNS +// +// For now this also makes it easier for apps to join the same network, and +// leaves users with complex networking desires to create and manage their own +// networks. +const fixedNetworkName = "kind" + +// ensureNetwork creates a new network +// podman only creates IPv6 networks for versions >= 2.2.0 +func ensureNetwork(name string) error { + // network already exists + if checkIfNetworkExists(name) { + return nil + } + + // generate unique subnet per network based on the name + // obtained from the ULA fc00::/8 range + // Make N attempts with "probing" in case we happen to collide + subnet := generateULASubnetFromName(name, 0) + err := createNetwork(name, subnet) + if err == nil { + // Success! + return nil + } + + if isUnknownIPv6FlagError(err) { + return createNetwork(name, "") + } + + // Only continue if the error is because of the subnet range + // is already allocated + if !isPoolOverlapError(err) { + return err + } + + // keep trying for ipv6 subnets + const maxAttempts = 5 + for attempt := int32(1); attempt < maxAttempts; attempt++ { + subnet := generateULASubnetFromName(name, attempt) + err = createNetwork(name, subnet) + if err == nil { + // success! + return nil + } else if !isPoolOverlapError(err) { + // unknown error ... + return err + } + } + return errors.New("exhausted attempts trying to find a non-overlapping subnet") + +} + +func createNetwork(name, ipv6Subnet string) error { + if ipv6Subnet == "" { + return exec.Command("podman", "network", "create", "-d=bridge", name).Run() + } + return exec.Command("podman", "network", "create", "-d=bridge", + "--ipv6", "--subnet", ipv6Subnet, name).Run() +} + +func checkIfNetworkExists(name string) bool { + _, err := exec.Output(exec.Command( + "podman", "network", "inspect", + regexp.QuoteMeta(name), + )) + return err == nil +} + +func isUnknownIPv6FlagError(err error) bool { + rerr := exec.RunErrorForError(err) + return rerr != nil && + strings.Contains(string(rerr.Output), "unknown flag: --ipv6") +} + +func isPoolOverlapError(err error) bool { + rerr := exec.RunErrorForError(err) + return rerr != nil && + (strings.Contains(string(rerr.Output), "is being used by a network interface") || + strings.Contains(string(rerr.Output), "is already being used by a cni configuration")) +} + +// generateULASubnetFromName generate an IPv6 subnet based on the +// name and Nth probing attempt +func generateULASubnetFromName(name string, attempt int32) string { + ip := make([]byte, 16) + ip[0] = 0xfc + ip[1] = 0x00 + h := sha1.New() + _, _ = h.Write([]byte(name)) + _ = binary.Write(h, binary.LittleEndian, attempt) + bs := h.Sum(nil) + for i := 2; i < 8; i++ { + ip[i] = bs[i] + } + subnet := &net.IPNet{ + IP: net.IP(ip), + Mask: net.CIDRMask(64, 128), + } + return subnet.String() +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/node.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/node.go index 4a530f08b2..5285dd2244 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/node.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/node.go @@ -53,7 +53,7 @@ func (n *node) Role() (string, error) { func (n *node) IP() (ipv4 string, ipv6 string, err error) { // retrieve the IP address of the node using podman inspect cmd := exec.Command("podman", "inspect", - "-f", "{{.NetworkSettings.IPAddress}},{{.NetworkSettings.GlobalIPv6Address}}", + "-f", "{{range .NetworkSettings.Networks}}{{.IPAddress}},{{.GlobalIPv6Address}}{{end}}", n.name, // ... against the "node" container ) lines, err := exec.OutputLines(cmd) diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provider.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provider.go index b338491b94..53cc1c5a8b 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provider.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provider.go @@ -26,6 +26,7 @@ import ( "strings" "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/version" "sigs.k8s.io/kind/pkg/cluster/nodes" "sigs.k8s.io/kind/pkg/cluster/nodeutils" @@ -52,6 +53,7 @@ func NewProvider(logger log.Logger) providers.Provider { // see NewProvider type provider struct { logger log.Logger + info *providers.ProviderInfo } // String implements fmt.Stringer @@ -67,25 +69,30 @@ func (p *provider) Provision(status *cli.Status, cfg *config.Cluster) (err error return err } - // kind doesn't work with podman rootless, surface an error - if os.Geteuid() != 0 { - p.logger.Errorf("podman provider does not work properly in rootless mode") - os.Exit(1) - } - // TODO: validate cfg // ensure node images are pulled before actually provisioning if err := ensureNodeImages(p.logger, status, cfg); err != nil { return err } + // ensure the pre-requisite network exists + networkName := fixedNetworkName + if n := os.Getenv("KIND_EXPERIMENTAL_PODMAN_NETWORK"); n != "" { + p.logger.Warn("WARNING: Overriding podman network due to KIND_EXPERIMENTAL_PODMAN_NETWORK") + p.logger.Warn("WARNING: Here be dragons! This is not supported currently.") + networkName = n + } + if err := ensureNetwork(networkName); err != nil { + return errors.Wrap(err, "failed to ensure podman network") + } + // actually provision the cluster icons := strings.Repeat("📦 ", len(cfg.Nodes)) status.Start(fmt.Sprintf("Preparing nodes %s", icons)) defer func() { status.End(err == nil) }() // plan creating the containers - createContainerFuncs, err := planCreation(cfg) + createContainerFuncs, err := planCreation(cfg, networkName) if err != nil { return err } @@ -174,7 +181,46 @@ func (p *provider) GetAPIServerEndpoint(cluster string) (string, error) { return "", errors.Wrap(err, "failed to get api server endpoint") } - // retrieve the specific port mapping using podman inspect + // TODO: get rid of this once podman settles on how to get the port mapping using podman inspect + // This is only used to get the Kubeconfig server field + v, err := getPodmanVersion() + if err != nil { + return "", errors.Wrap(err, "failed to check podman version") + } + // podman inspect was broken between 2.2.0 and 3.0.0 + // https://github.com/containers/podman/issues/8444 + if v.AtLeast(version.MustParseSemantic("2.2.0")) && + v.LessThan(version.MustParseSemantic("3.0.0")) { + p.logger.Warnf("WARNING: podman version %s not fully supported, please use versions 3.0.0+") + + cmd := exec.Command( + "podman", "inspect", + "--format", + "{{range .NetworkSettings.Ports }}{{range .}}{{.HostIP}}/{{.HostPort}}{{end}}{{end}}", + n.String(), + ) + + lines, err := exec.OutputLines(cmd) + if err != nil { + return "", errors.Wrap(err, "failed to get api server port") + } + if len(lines) != 1 { + return "", errors.Errorf("network details should only be one line, got %d lines", len(lines)) + } + // output is in the format IP/Port + parts := strings.Split(strings.TrimSpace(lines[0]), "/") + if len(parts) != 2 { + return "", errors.Errorf("network details should be in the format IP/Port, received: %s", parts) + } + host := parts[0] + port, err := strconv.Atoi(parts[1]) + if err != nil { + return "", errors.Errorf("network port not an integer: %v", err) + } + + return net.JoinHostPort(host, strconv.Itoa(port)), nil + } + cmd := exec.Command( "podman", "inspect", "--format", @@ -223,6 +269,7 @@ func (p *provider) GetAPIServerEndpoint(cluster string) (string, error) { } } } + var portMappings19 []portMapping19 if err := json.Unmarshal([]byte(lines[0]), &portMappings19); err != nil { return "", errors.Errorf("invalid network details: %v", err) @@ -233,7 +280,7 @@ func (p *provider) GetAPIServerEndpoint(cluster string) (string, error) { } } - return "", errors.Errorf("unable to find apiserver endpoint information") + return "", errors.Errorf("failed to get api server port") } // GetAPIServerInternalEndpoint is part of the providers.Provider interface @@ -247,14 +294,8 @@ func (p *provider) GetAPIServerInternalEndpoint(cluster string) (string, error) if err != nil { return "", errors.Wrap(err, "failed to get apiserver endpoint") } - // TODO: check cluster IP family and return the correct IP - // This means IPv6 singlestack is broken on podman - ipv4, _, err := n.IP() - if err != nil { - return "", errors.Wrap(err, "failed to get apiserver IP") - } - return net.JoinHostPort(ipv4, fmt.Sprintf("%d", common.APIServerInternalPort)), nil - + // NOTE: we're using the nodes's hostnames which are their names + return net.JoinHostPort(n.String(), fmt.Sprintf("%d", common.APIServerInternalPort)), nil } // node returns a new node handle for this provider @@ -278,7 +319,6 @@ func (p *provider) CollectLogs(dir string, nodes []nodes.Node) error { } // construct a slice of methods to collect logs fns := []func() error{ - // TODO(bentheelder): record the kind version here as well // record info about the host podman execToPathFn( exec.Command("podman", "info"), @@ -313,3 +353,63 @@ func (p *provider) CollectLogs(dir string, nodes []nodes.Node) error { errs = append(errs, errors.AggregateConcurrent(fns)) return errors.NewAggregate(errs) } + +// Info returns the provider info. +// The info is cached on the first time of the execution. +func (p *provider) Info() (*providers.ProviderInfo, error) { + if p.info == nil { + var err error + p.info, err = info(p.logger) + if err != nil { + return p.info, err + } + } + return p.info, nil +} + +// podmanInfo corresponds to `podman info --format 'json`. +// The structure is different from `docker info --format '{{json .}}'`, +// and lacks information about the availability of the cgroup controllers. +type podmanInfo struct { + Host struct { + CgroupVersion string `json:"cgroupVersion,omitempty"` // "v2" + Security struct { + Rootless bool `json:"rootless,omitempty"` + } `json:"security"` + } `json:"host"` +} + +// info detects ProviderInfo by executing `podman info --format json`. +func info(logger log.Logger) (*providers.ProviderInfo, error) { + const podman = "podman" + args := []string{"info", "--format", "json"} + cmd := exec.Command(podman, args...) + out, err := exec.Output(cmd) + if err != nil { + return nil, errors.Wrapf(err, "failed to get podman info (%s %s): %q", + podman, strings.Join(args, " "), string(out)) + } + var pInfo podmanInfo + if err := json.Unmarshal(out, &pInfo); err != nil { + return nil, err + } + info := &providers.ProviderInfo{ + Rootless: pInfo.Host.Security.Rootless, + Cgroup2: pInfo.Host.CgroupVersion == "v2", + // We assume all the cgroup controllers to be available. + // + // For rootless, this assumption is not always correct, + // so we print the warning below. + // + // TODO: We wiil be able to implement proper cgroup controller detection + // after the GA of Podman 3.2.x: https://github.com/containers/podman/pull/10387 + SupportsMemoryLimit: true, // not guaranteed to be correct + SupportsPidsLimit: true, // not guaranteed to be correct + SupportsCPUShares: true, // not guaranteed to be correct + } + if info.Rootless { + logger.Warn("Cgroup controller detection is not implemented for Podman. " + + "If you see cgroup-related errors, you might need to set systemd property \"Delegate=yes\", see https://kind.sigs.k8s.io/docs/user/rootless/") + } + return info, nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provision.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provision.go index a6f766c1f7..51dce4860b 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provision.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/provision.go @@ -32,10 +32,10 @@ import ( ) // planCreation creates a slice of funcs that will create the containers -func planCreation(cfg *config.Cluster) (createContainerFuncs []func() error, err error) { +func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs []func() error, err error) { // these apply to all container creation nodeNamer := common.MakeNodeNamer(cfg.Name) - genericArgs, err := commonArgs(cfg) + genericArgs, err := commonArgs(cfg, networkName) if err != nil { return nil, err } @@ -51,7 +51,8 @@ func planCreation(cfg *config.Cluster) (createContainerFuncs []func() error, err // For now remote podman + multi control plane is not supported apiServerPort = 0 // replaced with random ports apiServerAddress = "127.0.0.1" // only the LB needs to be non-local - if clusterIsIPv6(cfg) { + // only for IPv6 only clusters + if cfg.Networking.IPFamily == config.IPv6Family { apiServerAddress = "::1" // only the LB needs to be non-local } // plan loadbalancer node @@ -120,7 +121,7 @@ func createContainer(args []string) error { } func clusterIsIPv6(cfg *config.Cluster) bool { - return cfg.Networking.IPFamily == "ipv6" + return cfg.Networking.IPFamily == config.IPv6Family || cfg.Networking.IPFamily == config.DualStackFamily } func clusterHasImplicitLoadBalancer(cfg *config.Cluster) bool { @@ -135,13 +136,16 @@ func clusterHasImplicitLoadBalancer(cfg *config.Cluster) bool { } // commonArgs computes static arguments that apply to all containers -func commonArgs(cfg *config.Cluster) ([]string, error) { +func commonArgs(cfg *config.Cluster, networkName string) ([]string, error) { // standard arguments all nodes containers need, computed once args := []string{ - "--detach", // run the container detached - "--tty", // allocate a tty for entrypoint logs + "--detach", // run the container detached + "--tty", // allocate a tty for entrypoint logs + "--net", networkName, // attach to its own network // label the node with the cluster ID "--label", fmt.Sprintf("%s=%s", clusterLabelKey, cfg.Name), + // specify container implementation to systemd + "-e", "container=podman", } // enable IPv6 if necessary @@ -150,7 +154,7 @@ func commonArgs(cfg *config.Cluster) ([]string, error) { } // pass proxy environment variables - proxyEnv, err := getProxyEnv(cfg) + proxyEnv, err := getProxyEnv(cfg, networkName) if err != nil { return nil, errors.Wrap(err, "proxy setup error") } @@ -158,6 +162,12 @@ func commonArgs(cfg *config.Cluster) ([]string, error) { args = append(args, "-e", fmt.Sprintf("%s=%s", key, val)) } + // handle Podman on Btrfs or ZFS same as we do with Docker + // https://github.com/kubernetes-sigs/kind/issues/1416#issuecomment-606514724 + if mountDevMapper() { + args = append(args, "--volume", "/dev/mapper:/dev/mapper") + } + return args, nil } @@ -208,6 +218,11 @@ func runArgsForNode(node *config.Node, clusterIPFamily config.ClusterIPFamily, n } args = append(args, mappingArgs...) + switch node.Role { + case config.ControlPlaneRole: + args = append(args, "-e", "KUBECONFIG=/etc/kubernetes/admin.conf") + } + // finally, specify the image to run _, image := sanitizeImage(node.Image) return append(args, image), nil @@ -242,12 +257,12 @@ func runArgsForLoadBalancer(cfg *config.Cluster, name string, args []string) ([] return append(args, image), nil } -func getProxyEnv(cfg *config.Cluster) (map[string]string, error) { +func getProxyEnv(cfg *config.Cluster, networkName string) (map[string]string, error) { envs := common.GetProxyEnvs(cfg) // Specifically add the podman network subnets to NO_PROXY if we are using a proxy if len(envs) > 0 { - // podman default bridge network is named "bridge" (https://docs.podman.com/network/bridge/#use-the-default-bridge-network) - subnets, err := getSubnets("bridge") + // kind default bridge is "kind" + subnets, err := getSubnets(networkName) if err != nil { return nil, err } @@ -266,9 +281,10 @@ func getProxyEnv(cfg *config.Cluster) (map[string]string, error) { } func getSubnets(networkName string) ([]string, error) { - format := `{{range (index (index . "IPAM") "Config")}}{{index . "Subnet"}} {{end}}` + // TODO: unmarshall json and get rid of this complex query + format := `{{ range (index (index (index (index . "plugins") 0 ) "ipam" ) "ranges")}}{{ index ( index . 0 ) "subnet" }} {{end}}` cmd := exec.Command("podman", "network", "inspect", "-f", format, networkName) - lines, err := exec.CombinedOutputLines(cmd) + lines, err := exec.OutputLines(cmd) if err != nil { return nil, errors.Wrap(err, "failed to get subnets") } diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/util.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/util.go index f3d19d0f21..eb274ab247 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/util.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/util.go @@ -38,7 +38,7 @@ func IsAvailable() bool { func getPodmanVersion() (*version.Version, error) { cmd := exec.Command("podman", "--version") - lines, err := exec.CombinedOutputLines(cmd) + lines, err := exec.OutputLines(cmd) if err != nil { return nil, err } @@ -115,3 +115,19 @@ func deleteVolumes(names []string) error { cmd := exec.Command("podman", args...) return cmd.Run() } + +// mountDevMapper checks if the podman storage driver is Btrfs or ZFS +func mountDevMapper() bool { + storage := "" + cmd := exec.Command("podman", "info", "-f", + `{{ index .Store.GraphStatus "Backing Filesystem"}}`) + lines, err := exec.OutputLines(cmd) + if err != nil { + return false + } + + if len(lines) > 0 { + storage = strings.ToLower(strings.TrimSpace(lines[0])) + } + return storage == "btrfs" || storage == "zfs" +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/provider.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/provider.go index 6e28c4dc79..d2f8e36287 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/provider.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/provider.go @@ -45,4 +45,15 @@ type Provider interface { GetAPIServerInternalEndpoint(cluster string) (string, error) // CollectLogs will populate dir with cluster logs and other debug files CollectLogs(dir string, nodes []nodes.Node) error + // Info returns the provider info + Info() (*ProviderInfo, error) +} + +// ProviderInfo is the info of the provider +type ProviderInfo struct { + Rootless bool + Cgroup2 bool + SupportsMemoryLimit bool + SupportsPidsLimit bool + SupportsCPUShares bool } diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/nodeutils/util.go b/vendor/sigs.k8s.io/kind/pkg/cluster/nodeutils/util.go index f44d3166e3..df0af0c924 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/nodeutils/util.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/nodeutils/util.go @@ -32,7 +32,7 @@ import ( func KubeVersion(n nodes.Node) (version string, err error) { // grab kubernetes version from the node image cmd := n.Command("cat", "/kind/version") - lines, err := exec.CombinedOutputLines(cmd) + lines, err := exec.OutputLines(cmd) if err != nil { return "", errors.Wrap(err, "failed to get file") } diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go b/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go index 26d308ce4c..7a6ae1e6b9 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go @@ -17,11 +17,17 @@ limitations under the License. package cluster import ( + "io/ioutil" + "os" + "path/filepath" "sort" + "sigs.k8s.io/kind/pkg/cmd/kind/version" + "sigs.k8s.io/kind/pkg/cluster/constants" "sigs.k8s.io/kind/pkg/cluster/nodes" "sigs.k8s.io/kind/pkg/cluster/nodeutils" + "sigs.k8s.io/kind/pkg/errors" "sigs.k8s.io/kind/pkg/log" internalcreate "sigs.k8s.io/kind/pkg/cluster/internal/create" @@ -68,20 +74,54 @@ func NewProvider(options ...ProviderOption) *Provider { } } + // ensure a provider if none was set + // NOTE: depends on logger being set (see sorting above) if p.provider == nil { - // auto-detect based on each package IsAvailable() function - // default to docker for backwards compatibility - if docker.IsAvailable() { - p.provider = docker.NewProvider(p.logger) - } else if podman.IsAvailable() { - p.provider = podman.NewProvider(p.logger) - } else { - p.provider = docker.NewProvider(p.logger) + // DetectNodeProvider does not fallback to allow callers to determine + // this behavior + // However for compatibility if the caller of NewProvider supplied no + // option and we autodetect internally, we default to the docker provider + // for fallback, to avoid a breaking change for now. + // This may change in the future. + // TODO: consider breaking this API for earlier errors. + providerOpt, _ := DetectNodeProvider() + if providerOpt == nil { + providerOpt = ProviderWithDocker() } + providerOpt.apply(p) } return p } +// NoNodeProviderDetectedError indicates that we could not autolocate an available +// NodeProvider backend on the host +var NoNodeProviderDetectedError = errors.NewWithoutStack("failed to detect any supported node provider") + +// DetectNodeProvider allows callers to autodetect the node provider +// *without* fallback to the default. +// +// Pass the returned ProviderOption to NewProvider to pass the auto-detect Docker +// or Podman option explicitly (in the future there will be more options) +// +// NOTE: The kind *cli* also checks `KIND_EXPERIMENTAL_PROVIDER` for "podman" or +// "docker" currently and does not auto-detect / respects this if set. +// +// This will be replaced with some other mechanism in the future (likely when +// podman support is GA), in the meantime though your tool may wish to match this. +// +// In the future when this is not considered experimental, +// that logic will be in a public API as well. +func DetectNodeProvider() (ProviderOption, error) { + // auto-detect based on each node provider's IsAvailable() function + if docker.IsAvailable() { + return ProviderWithDocker(), nil + } + if podman.IsAvailable() { + return ProviderWithPodman(), nil + } + return nil, errors.WithStack(NoNodeProviderDetectedError) +} + // ProviderOption is an option for configuring a provider type ProviderOption interface { apply(p *Provider) @@ -191,5 +231,18 @@ func (p *Provider) CollectLogs(name, dir string) error { if err != nil { return err } + // ensure directory + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + return errors.Wrap(err, "failed to create logs directory") + } + // write kind version + if err := ioutil.WriteFile( + filepath.Join(dir, "kind-version.txt"), + []byte(version.DisplayVersion()), + 0666, // match os.Create + ); err != nil { + return errors.Wrap(err, "failed to write kind-version.txt") + } + // collect and write cluster logs return p.provider.CollectLogs(dir, n) } diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/doc.go b/vendor/sigs.k8s.io/kind/pkg/cmd/doc.go new file mode 100644 index 0000000000..ae20f68a75 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/doc.go @@ -0,0 +1,15 @@ +/* +Copyright 2019 The Kubernetes Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package cmd provides helpers used by kind's commands / cli +package cmd diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/iostreams.go b/vendor/sigs.k8s.io/kind/pkg/cmd/iostreams.go new file mode 100644 index 0000000000..2903235dec --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/iostreams.go @@ -0,0 +1,41 @@ +/* +Copyright 2019 The Kubernetes Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "io" + "os" +) + +// IOStreams provides the standard names for iostreams. +// This is useful for embedding and for unit testing. +// Inconsistent and different names make it hard to read and review code +// This is based on cli-runtime, but just the nice type without the dependency +type IOStreams struct { + // In think, os.Stdin + In io.Reader + // Out think, os.Stdout + Out io.Writer + // ErrOut think, os.Stderr + ErrOut io.Writer +} + +// StandardIOStreams returns an IOStreams from os.Stdin, os.Stdout +func StandardIOStreams() IOStreams { + return IOStreams{ + In: os.Stdin, + Out: os.Stdout, + ErrOut: os.Stderr, + } +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/version/version.go b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/version/version.go new file mode 100644 index 0000000000..48b783e365 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/version/version.go @@ -0,0 +1,89 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package version implements the `version` command +package version + +import ( + "fmt" + "runtime" + + "github.com/spf13/cobra" + + "sigs.k8s.io/kind/pkg/cmd" + "sigs.k8s.io/kind/pkg/log" +) + +// Version returns the kind CLI Semantic Version +func Version() string { + v := VersionCore + // add pre-release version info if we have it + if VersionPreRelease != "" { + v += "-" + VersionPreRelease + // if commit was set, add the + + // we only do this for pre-release versions + if GitCommit != "" { + // NOTE: use 14 character short hash, like Kubernetes + v += "+" + truncate(GitCommit, 14) + } + } + return v +} + +// DisplayVersion is Version() display formatted, this is what the version +// subcommand prints +func DisplayVersion() string { + return "kind v" + Version() + " " + runtime.Version() + " " + runtime.GOOS + "/" + runtime.GOARCH +} + +// VersionCore is the core portion of the kind CLI version per Semantic Versioning 2.0.0 +const VersionCore = "0.11.1" + +// VersionPreRelease is the pre-release portion of the kind CLI version per +// Semantic Versioning 2.0.0 +const VersionPreRelease = "" + +// GitCommit is the commit used to build the kind binary, if available. +// It is injected at build time. +var GitCommit = "" + +// NewCommand returns a new cobra.Command for version +func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Args: cobra.NoArgs, + Use: "version", + Short: "Prints the kind CLI version", + Long: "Prints the kind CLI version", + RunE: func(cmd *cobra.Command, args []string) error { + if logger.V(0).Enabled() { + // if not -q / --quiet, show lots of info + fmt.Fprintln(streams.Out, DisplayVersion()) + } else { + // otherwise only show semver + fmt.Fprintln(streams.Out, Version()) + } + return nil + }, + } + return cmd +} + +func truncate(s string, maxLen int) string { + if len(s) < maxLen { + return s + } + return s[:maxLen] +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cmd/logger.go b/vendor/sigs.k8s.io/kind/pkg/cmd/logger.go new file mode 100644 index 0000000000..eaf0f944ca --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/logger.go @@ -0,0 +1,47 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "io" + "os" + + "sigs.k8s.io/kind/pkg/log" + + "sigs.k8s.io/kind/pkg/internal/cli" + "sigs.k8s.io/kind/pkg/internal/env" +) + +// NewLogger returns the standard logger used by the kind CLI +// This logger writes to os.Stderr +func NewLogger() log.Logger { + var writer io.Writer = os.Stderr + if env.IsSmartTerminal(writer) { + writer = cli.NewSpinner(writer) + } + return cli.NewLogger(writer, 0) +} + +// ColorEnabled returns true if color is enabled for the logger +// this should be used to control output +func ColorEnabled(logger log.Logger) bool { + type maybeColorer interface { + ColorEnabled() bool + } + v, ok := logger.(maybeColorer) + return ok && v.ColorEnabled() +} diff --git a/vendor/sigs.k8s.io/kind/pkg/errors/errors.go b/vendor/sigs.k8s.io/kind/pkg/errors/errors.go index 3e2d1d7140..98bc47bf2b 100644 --- a/vendor/sigs.k8s.io/kind/pkg/errors/errors.go +++ b/vendor/sigs.k8s.io/kind/pkg/errors/errors.go @@ -17,6 +17,8 @@ limitations under the License. package errors import ( + stderrors "errors" + pkgerrors "github.com/pkg/errors" ) @@ -26,6 +28,12 @@ func New(message string) error { return pkgerrors.New(message) } +// NewWithoutStack is like new but does NOT wrap with a stack +// This is useful for exported errors +func NewWithoutStack(message string) error { + return stderrors.New(message) +} + // Errorf formats according to a format specifier and returns the string as a // value that satisfies error. Errorf also records the stack trace at the // point it was called. diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/convert_v1alpha4.go b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/convert_v1alpha4.go index 96e7aa8d4c..f37fe6c16e 100644 --- a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/convert_v1alpha4.go +++ b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/convert_v1alpha4.go @@ -51,6 +51,7 @@ func convertv1alpha4Node(in *v1alpha4.Node, out *Node) { out.Role = NodeRole(in.Role) out.Image = in.Image + out.Labels = in.Labels out.KubeadmConfigPatches = in.KubeadmConfigPatches out.ExtraMounts = make([]Mount, len(in.ExtraMounts)) out.ExtraPortMappings = make([]PortMapping, len(in.ExtraPortMappings)) diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/default.go b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/default.go index fc2f288a85..7a93df8022 100644 --- a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/default.go +++ b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/default.go @@ -48,14 +48,14 @@ func SetDefaultsCluster(obj *Cluster) { SetDefaultsNode(a) } if obj.Networking.IPFamily == "" { - obj.Networking.IPFamily = "ipv4" + obj.Networking.IPFamily = IPv4Family } // default to listening on 127.0.0.1:randomPort on ipv4 // and [::1]:randomPort on ipv6 if obj.Networking.APIServerAddress == "" { obj.Networking.APIServerAddress = "127.0.0.1" - if obj.Networking.IPFamily == "ipv6" { + if obj.Networking.IPFamily == IPv6Family { obj.Networking.APIServerAddress = "::1" } } @@ -63,23 +63,32 @@ func SetDefaultsCluster(obj *Cluster) { // default the pod CIDR if obj.Networking.PodSubnet == "" { obj.Networking.PodSubnet = "10.244.0.0/16" - if obj.Networking.IPFamily == "ipv6" { - obj.Networking.PodSubnet = "fd00:10:244::/64" + if obj.Networking.IPFamily == IPv6Family { + // node-mask cidr default is /64 so we need a larger subnet, we use /56 following best practices + // xref: https://www.ripe.net/publications/docs/ripe-690#4--size-of-end-user-prefix-assignment---48---56-or-something-else- + obj.Networking.PodSubnet = "fd00:10:244::/56" + } + if obj.Networking.IPFamily == DualStackFamily { + obj.Networking.PodSubnet = "10.244.0.0/16,fd00:10:244::/56" } } // default the service CIDR using the kubeadm default // https://github.com/kubernetes/kubernetes/blob/746404f82a28e55e0b76ffa7e40306fb88eb3317/cmd/kubeadm/app/apis/kubeadm/v1beta2/defaults.go#L32 - // Note: kubeadm is doing it already but this simplifies kind's logic + // Note: kubeadm is using a /12 subnet, that may allocate a 2^20 bitmap in etcd + // we allocate a /16 subnet that allows 65535 services (current Kubernetes tested limit is O(10k) services) if obj.Networking.ServiceSubnet == "" { - obj.Networking.ServiceSubnet = "10.96.0.0/12" - if obj.Networking.IPFamily == "ipv6" { + obj.Networking.ServiceSubnet = "10.96.0.0/16" + if obj.Networking.IPFamily == IPv6Family { obj.Networking.ServiceSubnet = "fd00:10:96::/112" } + if obj.Networking.IPFamily == DualStackFamily { + obj.Networking.ServiceSubnet = "10.96.0.0/16,fd00:10:96::/112" + } } // default the KubeProxyMode using iptables as it's already the default if obj.Networking.KubeProxyMode == "" { - obj.Networking.KubeProxyMode = IPTablesMode + obj.Networking.KubeProxyMode = IPTablesProxyMode } } diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/types.go b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/types.go index 25b364044b..33bbd721d9 100644 --- a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/types.go +++ b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/types.go @@ -85,6 +85,9 @@ type Node struct { // If unset a default image will be used, see defaults.Image Image string + // Labels are the labels with which the respective node will be labeled + Labels map[string]string + /* Advanced fields */ // ExtraMounts describes additional mount points for the node container @@ -157,16 +160,20 @@ const ( IPv4Family ClusterIPFamily = "ipv4" // IPv6Family sets ClusterIPFamily to ipv6 IPv6Family ClusterIPFamily = "ipv6" + // DualStackFamily sets ClusterIPFamily to dual + DualStackFamily ClusterIPFamily = "dual" ) // ProxyMode defines a proxy mode for kube-proxy type ProxyMode string const ( - // IPTablesMode sets ProxyMode to iptables - IPTablesMode ProxyMode = "iptables" - // IPVSMode sets ProxyMode to iptables - IPVSMode ProxyMode = "ipvs" + // IPTablesProxyMode sets ProxyMode to iptables + IPTablesProxyMode ProxyMode = "iptables" + // IPVSProxyMode sets ProxyMode to ipvs + IPVSProxyMode ProxyMode = "ipvs" + // NoneProxyMode disables kube-proxy + NoneProxyMode ProxyMode = "none" ) // PatchJSON6902 represents an inline kustomize json 6902 patch @@ -184,7 +191,7 @@ type PatchJSON6902 struct { // This is a close copy of the upstream cri Mount type // see: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2 // It additionally serializes the "propagation" field with the string enum -// names on disk as opposed to the int32 values, and the serlialzed field names +// names on disk as opposed to the int32 values, and the serialized field names // have been made closer to core/v1 VolumeMount field names // In yaml this looks like: // containerPath: /foo diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/validate.go b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/validate.go index 62671621bc..5152322a94 100644 --- a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/validate.go +++ b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/validate.go @@ -17,16 +17,31 @@ limitations under the License. package config import ( + "fmt" "net" + "regexp" + "strings" "sigs.k8s.io/kind/pkg/errors" ) +// similar to valid docker container names, but since we will prefix +// and suffix this name, we can relax it a little +// see NewContext() for usage +// https://godoc.org/github.com/docker/docker/daemon/names#pkg-constants +var validNameRE = regexp.MustCompile(`^[a-z0-9.-]+$`) + // Validate returns a ConfigErrors with an entry for each problem // with the config, or nil if there are none func (c *Cluster) Validate() error { errs := []error{} + // validate the name + if !validNameRE.MatchString(c.Name) { + errs = append(errs, errors.Errorf("'%s' is not a valid cluster name, cluster names must match `%s`", + c.Name, validNameRE.String())) + } + // the api server port only needs checking if we aren't picking a random one // at runtime if c.Networking.APIServerPort != 0 { @@ -36,17 +51,20 @@ func (c *Cluster) Validate() error { } } + isDualStack := c.Networking.IPFamily == DualStackFamily // podSubnet should be a valid CIDR - if _, _, err := net.ParseCIDR(c.Networking.PodSubnet); err != nil { - errs = append(errs, errors.Wrapf(err, "invalid podSubnet")) + if err := validateSubnets(c.Networking.PodSubnet, isDualStack); err != nil { + errs = append(errs, errors.Errorf("invalid pod subnet %v", err)) } + // serviceSubnet should be a valid CIDR - if _, _, err := net.ParseCIDR(c.Networking.ServiceSubnet); err != nil { - errs = append(errs, errors.Wrapf(err, "invalid serviceSubnet")) + if err := validateSubnets(c.Networking.ServiceSubnet, isDualStack); err != nil { + errs = append(errs, errors.Errorf("invalid service subnet %v", err)) } // KubeProxyMode should be iptables or ipvs - if c.Networking.KubeProxyMode != IPTablesMode && c.Networking.KubeProxyMode != IPVSMode { + if c.Networking.KubeProxyMode != IPTablesProxyMode && c.Networking.KubeProxyMode != IPVSProxyMode && + c.Networking.KubeProxyMode != NoneProxyMode { errs = append(errs, errors.Errorf("invalid kubeProxyMode: %s", c.Networking.KubeProxyMode)) } @@ -121,3 +139,64 @@ func validatePort(port int32) error { } return nil } + +func validateSubnets(subnetStr string, dualstack bool) error { + allErrs := []error{} + + cidrsString := strings.Split(subnetStr, ",") + subnets := make([]*net.IPNet, 0, len(cidrsString)) + for _, cidrString := range cidrsString { + _, cidr, err := net.ParseCIDR(cidrString) + if err != nil { + return fmt.Errorf("failed to parse cidr value:%q with error: %v", cidrString, err) + } + subnets = append(subnets, cidr) + } + + switch { + // if DualStack only 2 CIDRs allowed + case dualstack && len(subnets) > 2: + allErrs = append(allErrs, errors.New("expected one (IPv4 or IPv6) CIDR or two CIDRs from each family for dual-stack networking")) + // if DualStack and there are 2 CIDRs validate if there is at least one of each IP family + case dualstack && len(subnets) == 2: + areDualStackCIDRs, err := isDualStackCIDRs(subnets) + if err != nil { + allErrs = append(allErrs, err) + } else if !areDualStackCIDRs { + allErrs = append(allErrs, errors.New("expected one (IPv4 or IPv6) CIDR or two CIDRs from each family for dual-stack networking")) + } + // if not DualStack only one CIDR allowed + case !dualstack && len(subnets) > 1: + allErrs = append(allErrs, errors.New("only one CIDR allowed for single-stack networking")) + } + + if len(allErrs) > 0 { + return errors.NewAggregate(allErrs) + } + return nil +} + +// isDualStackCIDRs returns if +// - all are valid cidrs +// - at least one cidr from each family (v4 or v6) +func isDualStackCIDRs(cidrs []*net.IPNet) (bool, error) { + v4Found := false + v6Found := false + for _, cidr := range cidrs { + if cidr == nil { + return false, fmt.Errorf("cidr %v is invalid", cidr) + } + + if v4Found && v6Found { + continue + } + + if cidr.IP != nil && cidr.IP.To4() == nil { + v6Found = true + continue + } + v4Found = true + } + + return v4Found && v6Found, nil +} diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/zz_generated.deepcopy.go b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/zz_generated.deepcopy.go index 74e0c9d5f5..3595f0ae36 100644 --- a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/zz_generated.deepcopy.go +++ b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/zz_generated.deepcopy.go @@ -113,6 +113,13 @@ func (in *Networking) DeepCopy() *Networking { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Node) DeepCopyInto(out *Node) { *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.ExtraMounts != nil { in, out := &in.ExtraMounts, &out.ExtraMounts *out = make([]Mount, len(*in)) diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/env/term.go b/vendor/sigs.k8s.io/kind/pkg/internal/env/term.go index a0be1e6eb7..5f809dd9f0 100644 --- a/vendor/sigs.k8s.io/kind/pkg/internal/env/term.go +++ b/vendor/sigs.k8s.io/kind/pkg/internal/env/term.go @@ -68,7 +68,13 @@ func isSmartTerminal(w io.Writer, GOOS string, lookupEnv func(string) (string, b // Explicitly dumb terminals are not smart // https://en.wikipedia.org/wiki/Computer_terminal#Dumb_terminals - if getenv("TERM") == "dumb" { + term := getenv("TERM") + if term == "dumb" { + return false + } + // st has some bug 🤷‍♂️ + // https://github.com/kubernetes-sigs/kind/issues/1892 + if term == "st-256color" { return false }