From d5233abb9467e51b086479564c5f5aa810f3b51d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Mar 2022 23:20:01 -0400 Subject: [PATCH] Bump sigs.k8s.io/kind from 0.11.1 to 0.12.0 (#645) Bumps [sigs.k8s.io/kind](https://github.com/kubernetes-sigs/kind) from 0.11.1 to 0.12.0. - [Release notes](https://github.com/kubernetes-sigs/kind/releases) - [Commits](https://github.com/kubernetes-sigs/kind/compare/v0.11.1...v0.12.0) --- updated-dependencies: - dependency-name: sigs.k8s.io/kind dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 9 +- .../github.com/evanphx/json-patch/v5/patch.go | 46 +++- vendor/modules.txt | 7 +- .../kind/pkg/apis/config/defaults/image.go | 2 +- .../kind/pkg/apis/config/v1alpha4/types.go | 2 +- .../config/v1alpha4/zz_generated.deepcopy.go | 1 + .../internal/create/actions/config/config.go | 90 +++----- .../create/actions/kubeadminit/init.go | 26 ++- .../actions/waitforready/waitforready.go | 24 +- .../pkg/cluster/internal/create/create.go | 2 +- .../pkg/cluster/internal/kubeadm/config.go | 165 +++++++++++++- .../kubeconfig/internal/kubeconfig/paths.go | 2 +- .../cluster/internal/kubeconfig/kubeconfig.go | 9 +- .../cluster/internal/loadbalancer/const.go | 2 +- .../internal/providers/common/cgroups.go | 85 ++++++++ .../internal/providers/common/images.go | 3 +- .../internal/providers/docker/network.go | 5 +- .../internal/providers/docker/provider.go | 5 +- .../internal/providers/docker/provision.go | 65 +++--- .../cluster/internal/providers/docker/util.go | 13 ++ .../internal/providers/podman/images.go | 29 ++- .../internal/providers/podman/provider.go | 71 ++++-- .../internal/providers/podman/provision.go | 66 +++--- .../cluster/internal/providers/podman/util.go | 54 ++++- .../kind/pkg/cluster/nodeutils/util.go | 28 ++- .../sigs.k8s.io/kind/pkg/cluster/provider.go | 4 +- .../kind/pkg/cmd/kind/version/version.go | 2 +- .../sigs.k8s.io/kind/pkg/errors/aggregate.go | 16 +- .../kind/pkg/errors/aggregate_forked.go | 167 ++++++++++++++ .../pkg/internal/apis/config/cluster_util.go | 34 +++ .../kind/pkg/internal/apis/config/types.go | 2 +- .../kind/pkg/internal/apis/config/validate.go | 15 +- .../apis/config/zz_generated.deepcopy.go | 1 + .../sigs.k8s.io/kind/pkg/internal/sets/doc.go | 25 +++ .../kind/pkg/internal/sets/empty.go | 23 ++ .../kind/pkg/internal/sets/string.go | 205 ++++++++++++++++++ .../kind/pkg/internal}/version/doc.go | 8 +- .../kind/pkg/internal}/version/version.go | 0 39 files changed, 1099 insertions(+), 216 deletions(-) create mode 100644 vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/cgroups.go create mode 100644 vendor/sigs.k8s.io/kind/pkg/errors/aggregate_forked.go create mode 100644 vendor/sigs.k8s.io/kind/pkg/internal/apis/config/cluster_util.go create mode 100644 vendor/sigs.k8s.io/kind/pkg/internal/sets/doc.go create mode 100644 vendor/sigs.k8s.io/kind/pkg/internal/sets/empty.go create mode 100644 vendor/sigs.k8s.io/kind/pkg/internal/sets/string.go rename vendor/{k8s.io/apimachinery/pkg/util => sigs.k8s.io/kind/pkg/internal}/version/doc.go (68%) rename vendor/{k8s.io/apimachinery/pkg/util => sigs.k8s.io/kind/pkg/internal}/version/version.go (100%) diff --git a/go.mod b/go.mod index 0898b4ba02..0507e85947 100644 --- a/go.mod +++ b/go.mod @@ -25,5 +25,5 @@ require ( golang.org/x/tools v0.1.9 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b k8s.io/apimachinery v0.23.4 - sigs.k8s.io/kind v0.11.1 + sigs.k8s.io/kind v0.12.0 ) diff --git a/go.sum b/go.sum index 7f58302650..9d9d8c8fd1 100644 --- a/go.sum +++ b/go.sum @@ -635,9 +635,9 @@ github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMi github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.2.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= -github.com/evanphx/json-patch/v5 v5.5.0 h1:bAmFiUJ+o0o2B4OiTFeE3MqCOtyo+jjPP9iZ0VRxYUc= github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -2776,7 +2776,6 @@ k8s.io/api v0.21.4/go.mod h1:fTVGP+M4D8+00FN2cMnJqk/eb/GH53bvmNs2SVTmpFk= k8s.io/api v0.21.6/go.mod h1:prsGo/DxHNAsmgzl60woqeI7zxN9X/BqqQHXLlobxWk= k8s.io/apiextensions-apiserver v0.21.4/go.mod h1:OoC8LhI9LnV+wKjZkXIBbLUwtnOGJiTRE33qctH5CIk= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= @@ -2855,8 +2854,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyz sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= -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/kind v0.12.0 h1:LFynXwQkH1MrWI8pM1FQty0oUwEKjU5EkMaVZaPld8E= +sigs.k8s.io/kind v0.12.0/go.mod h1:EcgDSBVxz8Bvm19fx8xkioFrf9dC30fMJdOTXBSGNoM= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/vendor/github.com/evanphx/json-patch/v5/patch.go b/vendor/github.com/evanphx/json-patch/v5/patch.go index f24e9d59c7..117f2c00df 100644 --- a/vendor/github.com/evanphx/json-patch/v5/patch.go +++ b/vendor/github.com/evanphx/json-patch/v5/patch.go @@ -766,9 +766,9 @@ func ensurePathExists(pd *container, path string, options *ApplyOptions) error { } } - // Check if the next part is a numeric index. + // Check if the next part is a numeric index or "-". // If yes, then create an array, otherwise, create an object. - if arrIndex, err = strconv.Atoi(parts[pi+1]); err == nil { + if arrIndex, err = strconv.Atoi(parts[pi+1]); err == nil || parts[pi+1] == "-" { if arrIndex < 0 { if !options.SupportNegativeIndices { @@ -845,6 +845,29 @@ func (p Patch) replace(doc *container, op Operation, options *ApplyOptions) erro return errors.Wrapf(err, "replace operation failed to decode path") } + if path == "" { + val := op.value() + + if val.which == eRaw { + if !val.tryDoc() { + if !val.tryAry() { + return errors.Wrapf(err, "replace operation value must be object or array") + } + } + } + + switch val.which { + case eAry: + *doc = &val.ary + case eDoc: + *doc = val.doc + case eRaw: + return errors.Wrapf(err, "replace operation hit impossible case") + } + + return nil + } + con, key := findObject(doc, path, options) if con == nil { @@ -911,6 +934,25 @@ func (p Patch) test(doc *container, op Operation, options *ApplyOptions) error { return errors.Wrapf(err, "test operation failed to decode path") } + if path == "" { + var self lazyNode + + switch sv := (*doc).(type) { + case *partialDoc: + self.doc = sv + self.which = eDoc + case *partialArray: + self.ary = *sv + self.which = eAry + } + + if self.equal(op.value()) { + return nil + } + + return errors.Wrapf(ErrTestFailed, "testing value %s failed", path) + } + con, key := findObject(doc, path, options) if con == nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 27b0ed8307..78f5ba1c71 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -170,7 +170,7 @@ github.com/docker/go-units # github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960 ## explicit github.com/dprotaso/go-yit -# github.com/evanphx/json-patch/v5 v5.5.0 +# github.com/evanphx/json-patch/v5 v5.6.0 github.com/evanphx/json-patch/v5 # github.com/form3tech-oss/jwt-go v3.2.5+incompatible github.com/form3tech-oss/jwt-go @@ -441,13 +441,12 @@ k8s.io/apimachinery/pkg/util/errors k8s.io/apimachinery/pkg/util/sets k8s.io/apimachinery/pkg/util/validation k8s.io/apimachinery/pkg/util/validation/field -k8s.io/apimachinery/pkg/util/version # k8s.io/klog/v2 v2.30.0 k8s.io/klog/v2 # k8s.io/utils v0.0.0-20211116205334-6203023598ed k8s.io/utils/internal/third_party/forked/golang/net k8s.io/utils/net -# sigs.k8s.io/kind v0.11.1 +# sigs.k8s.io/kind v0.12.0 ## explicit sigs.k8s.io/kind/pkg/apis/config/defaults sigs.k8s.io/kind/pkg/apis/config/v1alpha4 @@ -484,6 +483,8 @@ sigs.k8s.io/kind/pkg/internal/apis/config sigs.k8s.io/kind/pkg/internal/apis/config/encoding sigs.k8s.io/kind/pkg/internal/cli sigs.k8s.io/kind/pkg/internal/env +sigs.k8s.io/kind/pkg/internal/sets +sigs.k8s.io/kind/pkg/internal/version sigs.k8s.io/kind/pkg/log # sigs.k8s.io/yaml v1.3.0 sigs.k8s.io/yaml 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 96371c2c95..b1c48dd656 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.21.1@sha256:69860bda5563ac81e3c0057d654b5253219618a22ec3a346306239bba8cfa1a6" +const Image = "kindest/node:v1.23.4@sha256:0e34f0d0fd448aa2f2819cfd74e99fe5793a6e4938b328f657c8e3f81ee0dfb9" 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 c6cfafbdea..8a59ce123c 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 @@ -277,7 +277,7 @@ type PortMapping struct { HostPort int32 `yaml:"hostPort,omitempty"` // TODO: add protocol (tcp/udp) and port-ranges ListenAddress string `yaml:"listenAddress,omitempty"` - // Protocol (TCP/UDP) + // Protocol (TCP/UDP/SCTP) Protocol PortMappingProtocol `yaml:"protocol,omitempty"` } 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 8aab0fa122..c86626514f 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 @@ -1,3 +1,4 @@ +//go:build !ignore_autogenerated // +build !ignore_autogenerated /* 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 b6f1e4dd17..d4c22ee1a2 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 @@ -98,63 +98,18 @@ 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) + // create the kubeadm join configuration for the kubernetes cluster nodes only + kubeNodes, err := nodeutils.InternalNodes(allNodes) if err != nil { return err } - for i, node := range controlPlanes { + for _, node := range kubeNodes { 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)) } - // then create the kubeadm join config for the worker nodes if any - workers, err := nodeutils.SelectNodesByRole(allNodes, constants.WorkerNodeRoleValue) - if err != nil { - return err - } - if len(workers) > 0 { - // create the workers concurrently - 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)) - } - } - // Create the kubeadm config in all nodes concurrently if err := errors.UntilErrorConcurrent(fns); err != nil { return err @@ -162,11 +117,6 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { // if we have containerd config, patch all the nodes concurrently if len(ctx.Config.ContainerdConfigPatches) > 0 || len(ctx.Config.ContainerdConfigPatchesJSON6902) > 0 { - // we only want to patch kubernetes nodes - // this is a cheap workaround to re-use the already listed - // workers + control planes - kubeNodes := append([]nodes.Node{}, controlPlanes...) - kubeNodes = append(kubeNodes, workers...) fns := make([]func() error, len(kubeNodes)) for i, node := range kubeNodes { node := node // capture loop variable @@ -185,8 +135,8 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { return errors.Wrap(err, "failed to write patched containerd config") } // restart containerd now that we've re-configured it - // skip if the systemd (also the containerd) is not running - if err := node.Command("bash", "-c", `! systemctl is-system-running || systemctl restart containerd`).Run(); err != nil { + // skip if containerd is not running + if err := node.Command("bash", "-c", `! pgrep --exact containerd || systemctl restart containerd`).Run(); err != nil { return errors.Wrap(err, "failed to restart containerd after patching config") } return nil @@ -243,10 +193,29 @@ func getKubeadmConfig(cfg *config.Cluster, data kubeadm.ConfigData, node nodes.N } data.NodeAddress = nodeAddressIPv6 if cfg.Networking.IPFamily == config.DualStackFamily { - data.NodeAddress = fmt.Sprintf("%s,%s", nodeAddress, nodeAddressIPv6) + // order matters since the nodeAddress will be used later to configure the apiserver advertise address + // Ref: #2484 + primaryServiceSubnet := strings.Split(cfg.Networking.ServiceSubnet, ",")[0] + ip, _, err := net.ParseCIDR(primaryServiceSubnet) + if err != nil { + return "", fmt.Errorf("failed to parse primary Service Subnet %s (%s): %w", primaryServiceSubnet, cfg.Networking.ServiceSubnet, err) + } + if ip.To4() != nil { + data.NodeAddress = fmt.Sprintf("%s,%s", nodeAddress, nodeAddressIPv6) + } else { + data.NodeAddress = fmt.Sprintf("%s,%s", nodeAddressIPv6, nodeAddress) + } } } + // configure the node labels + if len(configNode.Labels) > 0 { + data.NodeLabels = hashMapLabelsToCommaSeparatedLabels(configNode.Labels) + } + + // set the node role + data.ControlPlane = string(configNode.Role) == constants.ControlPlaneNodeRoleValue + // generate the config contents cf, err := kubeadm.Config(data) if err != nil { @@ -299,3 +268,12 @@ func writeKubeadmConfig(kubeadmConfig string, node nodes.Node) error { return nil } + +// hashMapLabelsToCommaSeparatedLabels converts labels in hashmap form to labels in a comma-separated string form like "key1=value1,key2=value2" +func hashMapLabelsToCommaSeparatedLabels(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 +} 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 9c596cb1f1..42eabeef82 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 @@ -27,6 +27,7 @@ import ( "sigs.k8s.io/kind/pkg/cluster/internal/create/actions" "sigs.k8s.io/kind/pkg/internal/apis/config" + "sigs.k8s.io/kind/pkg/internal/version" ) // kubeadmInitAction implements action for executing the kubeadm init @@ -106,14 +107,31 @@ func (a *action) Execute(ctx *actions.ActionContext) error { } } - // if we are only provisioning one node, remove the master taint + // if we are only provisioning one node, remove the control plane taint // https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#master-isolation if len(allNodes) == 1 { + // TODO: Once kubeadm 1.23 is no longer supported remove the <1.24 handling. + // TODO: Remove only the "control-plane" taint for kubeadm >= 1.25. + // https://github.com/kubernetes-sigs/kind/issues/1699 + rawVersion, err := nodeutils.KubeVersion(node) + if err != nil { + return errors.Wrap(err, "failed to get Kubernetes version from node") + } + kubeVersion, err := version.ParseSemantic(rawVersion) + if err != nil { + return errors.Wrap(err, "could not parse Kubernetes version") + } + taints := []string{"node-role.kubernetes.io/control-plane-", "node-role.kubernetes.io/master-"} + if kubeVersion.LessThan(version.MustParseSemantic("v1.24.0")) { + taints = []string{"node-role.kubernetes.io/master-"} + } + taintArgs := []string{"--kubeconfig=/etc/kubernetes/admin.conf", "taint", "nodes", "--all"} + taintArgs = append(taintArgs, taints...) + if err := node.Command( - "kubectl", "--kubeconfig=/etc/kubernetes/admin.conf", - "taint", "nodes", "--all", "node-role.kubernetes.io/master-", + "kubectl", taintArgs..., ).Run(); err != nil { - return errors.Wrap(err, "failed to remove master taint") + return errors.Wrap(err, "failed to remove control plane taint") } } 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 1dfbf6c29b..59348a9059 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 @@ -25,7 +25,9 @@ import ( "sigs.k8s.io/kind/pkg/cluster/internal/create/actions" "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/exec" + "sigs.k8s.io/kind/pkg/internal/version" ) // Action implements an action for waiting for the cluster to be ready @@ -66,7 +68,23 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { // Wait for the nodes to reach Ready status. startTime := time.Now() - isReady := waitForReady(node, startTime.Add(a.waitTime)) + + // TODO: Remove the below handling once kubeadm 1.23 is no longer supported. + // https://github.com/kubernetes-sigs/kind/issues/1699 + rawVersion, err := nodeutils.KubeVersion(node) + if err != nil { + return errors.Wrap(err, "failed to get Kubernetes version from node") + } + kubeVersion, err := version.ParseSemantic(rawVersion) + if err != nil { + return errors.Wrap(err, "could not parse Kubernetes version") + } + selectorLabel := "node-role.kubernetes.io/control-plane" + if kubeVersion.LessThan(version.MustParseSemantic("v1.24.0")) { + selectorLabel = "node-role.kubernetes.io/master" + } + + isReady := waitForReady(node, startTime.Add(a.waitTime), selectorLabel) if !isReady { ctx.Status.End(false) ctx.Logger.V(0).Info(" • WARNING: Timed out waiting for Ready ⚠️") @@ -81,14 +99,14 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { // WaitForReady uses kubectl inside the "node" container to check if the // control plane nodes are "Ready". -func waitForReady(node nodes.Node, until time.Time) bool { +func waitForReady(node nodes.Node, until time.Time, selectorLabel string) bool { return tryUntil(until, func() bool { cmd := node.Command( "kubectl", "--kubeconfig=/etc/kubernetes/admin.conf", "get", "nodes", - "--selector=node-role.kubernetes.io/master", + "--selector="+selectorLabel, // When the node reaches status ready, the status field will be set // to true. "-o=jsonpath='{.items..status.conditions[-1:].status}'", 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 e89d789605..351ba6c754 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 @@ -151,7 +151,7 @@ func Cluster(logger log.Logger, p providers.Provider, opts *ClusterOptions) erro var err error for _, b := range []time.Duration{0, time.Millisecond, time.Millisecond * 50, time.Millisecond * 100} { time.Sleep(b) - if err = kubeconfig.Export(p, opts.Config.Name, opts.KubeconfigPath); err == nil { + if err = kubeconfig.Export(p, opts.Config.Name, opts.KubeconfigPath, true); err == nil { break } } 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 c1241b3d92..d66a2a92aa 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 @@ -23,9 +23,10 @@ import ( "strings" "text/template" - "k8s.io/apimachinery/pkg/util/version" "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/internal/apis/config" + "sigs.k8s.io/kind/pkg/internal/version" ) // ConfigData is supplied to the kubeadm config template, with values populated @@ -325,7 +326,6 @@ scheduler: {{ end }} # configure ipv6 default addresses for IPv6 clusters {{ if .IPv6 -}} - address: "::" bind-address: "::1" {{- end }} networking: @@ -427,6 +427,147 @@ conntrack: {{end}}{{end}} ` +// ConfigTemplateBetaV3 is the kubeadm config template for API version v1beta3 +const ConfigTemplateBetaV3 = `# config generated by kind +apiVersion: kubeadm.k8s.io/v1beta3 +kind: ClusterConfiguration +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 +# to the cluster after rewriting the kubeconfig to point to localhost +apiServer: + certSANs: [localhost, "{{.APIServerAddress}}"] + extraArgs: + "runtime-config": "{{ .RuntimeConfigString }}" +{{ if .FeatureGates }} + "feature-gates": "{{ .FeatureGatesString }}" +{{ end}} +controllerManager: + extraArgs: +{{ if .FeatureGates }} + "feature-gates": "{{ .FeatureGatesString }}" +{{ end }} + enable-hostpath-provisioner: "true" + # configure ipv6 default addresses for IPv6 clusters + {{ if .IPv6 -}} + bind-address: "::" + {{- end }} +scheduler: + extraArgs: +{{ if .FeatureGates }} + "feature-gates": "{{ .FeatureGatesString }}" +{{ end }} + # configure ipv6 default addresses for IPv6 clusters + {{ if .IPv6 -}} + bind-address: "::1" + {{- end }} +networking: + podSubnet: "{{ .PodSubnet }}" + serviceSubnet: "{{ .ServiceSubnet }}" +--- +apiVersion: kubeadm.k8s.io/v1beta3 +kind: InitConfiguration +metadata: + name: config +# we use a well know token for TLS bootstrap +bootstrapTokens: +- token: "{{ .Token }}" +# 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: "{{ .AdvertiseAddress }}" + bindPort: {{.APIBindPort}} +nodeRegistration: + criSocket: "unix:///run/containerd/containerd.sock" + kubeletExtraArgs: + 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/v1beta3 +kind: JoinConfiguration +metadata: + name: config +{{ if .ControlPlane -}} +controlPlane: + localAPIEndpoint: + advertiseAddress: "{{ .AdvertiseAddress }}" + bindPort: {{.APIBindPort}} +{{- end }} +nodeRegistration: + criSocket: "unix:///run/containerd/containerd.sock" + kubeletExtraArgs: + fail-swap-on: "false" + node-ip: "{{ .NodeAddress }}" + provider-id: "kind://{{.NodeProvider}}/{{.ClusterName}}/{{.NodeName}}" + node-labels: "{{ .NodeLabels }}" +discovery: + bootstrapToken: + apiServerEndpoint: "{{ .ControlPlaneEndpoint }}" + token: "{{ .Token }}" + unsafeSkipCAVerification: true +--- +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: "::" +healthzBindAddress: "::" +{{- end }} +# disable disk resource management by default +# kubelet will see the host disk that the inner container runtime +# is ultimately backed by and attempt to recover disk space. we don't want that. +imageGCHighThresholdPercent: 100 +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%" +{{if .FeatureGates}}featureGates: +{{ range $key := .SortedFeatureGateKeys }} + "{{ $key }}": {{ index $.FeatureGates $key }} +{{end}}{{end}} +{{if ne .KubeProxyMode "None"}} +--- +apiVersion: kubeproxy.config.k8s.io/v1alpha1 +kind: KubeProxyConfiguration +metadata: + name: config +mode: "{{ .KubeProxyMode }}" +{{if .FeatureGates}}featureGates: +{{ range $key := .SortedFeatureGateKeys }} + "{{ $key }}": {{ index $.FeatureGates $key }} +{{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 // the kubernetes version func Config(data ConfigData) (config string, err error) { @@ -440,13 +581,25 @@ func Config(data ConfigData) (config string, err error) { data.FeatureGates = make(map[string]bool) } + if data.RootlessProvider { + if ver.LessThan(version.MustParseSemantic("v1.22.0")) { + // rootless kind v0.12.x supports Kubernetes v1.22 with KubeletInUserNamespace gate. + // rootless kind v0.11.x supports older Kubernetes with fake procfs. + return "", errors.Errorf("version %q is not compatible with rootless provider (hint: kind v0.11.x may work with this version)", ver) + } + data.FeatureGates["KubeletInUserNamespace"] = true + + // For avoiding err="failed to get rootfs info: failed to get device for dir \"/var/lib/kubelet\": could not find device with major: 0, minor: 41 in cached partitions map" + // https://github.com/kubernetes-sigs/kind/issues/2524 + data.FeatureGates["LocalStorageCapacityIsolation"] = false + } + // assume the latest API version, then fallback if the k8s version is too low - templateSource := ConfigTemplateBetaV2 + templateSource := ConfigTemplateBetaV3 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 + } else if ver.LessThan(version.MustParseSemantic("v1.23.0")) { + templateSource = ConfigTemplateBetaV2 } t, err := template.New("kubeadm-config").Parse(templateSource) diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/paths.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/paths.go index 23514ed809..da374f7786 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/paths.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/kubeconfig/internal/kubeconfig/paths.go @@ -22,7 +22,7 @@ import ( "path/filepath" "runtime" - "k8s.io/apimachinery/pkg/util/sets" + "sigs.k8s.io/kind/pkg/internal/sets" ) const kubeconfigEnv = "KUBECONFIG" 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 3763980f7b..c5e4c2acea 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 @@ -32,8 +32,8 @@ import ( // Export exports the kubeconfig given the cluster context and a path to write it to // This will always be an external kubeconfig -func Export(p providers.Provider, name, explicitPath string) error { - cfg, err := get(p, name, true) +func Export(p providers.Provider, name, explicitPath string, external bool) error { + cfg, err := get(p, name, external) if err != nil { return err } @@ -63,7 +63,7 @@ func Get(p providers.Provider, name string, external bool) (string, error) { } // ContextForCluster returns the context name for a kind cluster based on -// it's name. This key is used for all list entries of kind clusters +// its name. This key is used for all list entries of kind clusters func ContextForCluster(kindClusterName string) string { return kubeconfig.KINDClusterKey(kindClusterName) } @@ -80,7 +80,8 @@ func get(p providers.Provider, name string, external bool) (*kubeconfig.Config, return nil, err } if len(nodes) < 1 { - return nil, errors.New("could not locate any control plane nodes") + return nil, errors.Errorf("could not locate any control plane nodes for cluster named '%s'. "+ + "Use the --name option to select a different cluster", name) } node := nodes[0] diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/loadbalancer/const.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/loadbalancer/const.go index 14df1565ce..fdee3efcbe 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/loadbalancer/const.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/loadbalancer/const.go @@ -17,7 +17,7 @@ limitations under the License. package loadbalancer // Image defines the loadbalancer image:tag -const Image = "kindest/haproxy:v20200708-548e36db" +const Image = "kindest/haproxy:v20220207-ca68f7d4" // ConfigPath defines the path to the config file in the image const ConfigPath = "/usr/local/etc/haproxy/haproxy.cfg" diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/cgroups.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/cgroups.go new file mode 100644 index 0000000000..9ef3abd043 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/cgroups.go @@ -0,0 +1,85 @@ +/* +Copyright 2021 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 common + +import ( + "bufio" + "context" + "os" + "regexp" + "sync" + + "sigs.k8s.io/kind/pkg/errors" + "sigs.k8s.io/kind/pkg/exec" +) + +var nodeReachedCgroupsReadyRegexp *regexp.Regexp +var nodeReachedCgroupsReadyRegexpCompileOnce sync.Once + +// NodeReachedCgroupsReadyRegexp returns a regexp for use with WaitUntilLogRegexpMatches +// +// This is used to avoid "ERROR: this script needs /sys/fs/cgroup/cgroup.procs to be empty (for writing the top-level cgroup.subtree_control)" +// See https://github.com/kubernetes-sigs/kind/issues/2409 +// +// This pattern matches either "detected cgroupv1" from the kind node image's entrypoint logs +// or "Multi-User System" target if is using cgroups v2, +// so that `docker exec` can be executed safely without breaking cgroup v2 hierarchy. +func NodeReachedCgroupsReadyRegexp() *regexp.Regexp { + nodeReachedCgroupsReadyRegexpCompileOnce.Do(func() { + // This is an approximation, see: https://github.com/kubernetes-sigs/kind/pull/2421 + nodeReachedCgroupsReadyRegexp = regexp.MustCompile("Reached target .*Multi-User System.*|detected cgroup v1") + }) + return nodeReachedCgroupsReadyRegexp +} + +// WaitUntilLogRegexpMatches waits until logCmd output produces a line matching re. +// It will use logCtx to determine if the logCmd deadline was exceeded for producing +// the most useful error message in failure cases, logCtx should be the context +// supplied to create logCmd with CommandContext +func WaitUntilLogRegexpMatches(logCtx context.Context, logCmd exec.Cmd, re *regexp.Regexp) error { + pr, pw, err := os.Pipe() + if err != nil { + return err + } + logCmd.SetStdout(pw) + logCmd.SetStderr(pw) + + defer pr.Close() + cmdErrC := make(chan error, 1) + go func() { + defer pw.Close() + cmdErrC <- logCmd.Run() + }() + + sc := bufio.NewScanner(pr) + for sc.Scan() { + line := sc.Text() + if re.MatchString(line) { + return nil + } + } + + // when we timeout the process will have been killed due to the timeout, which is not interesting + // in other cases if the command errored this may be a useful error + if ctxErr := logCtx.Err(); ctxErr != context.DeadlineExceeded { + if cmdErr := <-cmdErrC; cmdErr != nil { + return errors.Wrap(cmdErr, "failed to read logs") + } + } + // otherwise generic error + return errors.Errorf("could not find a log line that matches %q", re.String()) +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/images.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/images.go index f66155b38b..080a337f58 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/images.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/common/images.go @@ -17,9 +17,8 @@ limitations under the License. package common import ( - "k8s.io/apimachinery/pkg/util/sets" - "sigs.k8s.io/kind/pkg/internal/apis/config" + "sigs.k8s.io/kind/pkg/internal/sets" ) // RequiredNodeImages returns the set of _node_ images specified by the config 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 b34041e6bd..f432846108 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 @@ -266,7 +266,7 @@ func isIPv6UnavailableError(err error) bool { func isPoolOverlapError(err error) bool { rerr := exec.RunErrorForError(err) - return rerr != nil && strings.HasPrefix(string(rerr.Output), "Error response from daemon: Pool overlaps with other one on this address space") + return rerr != nil && strings.HasPrefix(string(rerr.Output), "Error response from daemon: Pool overlaps with other one on this address space") || strings.Contains(string(rerr.Output), "networks have overlapping") } func isNetworkAlreadyExistsError(err error) bool { @@ -275,7 +275,6 @@ func isNetworkAlreadyExistsError(err error) bool { } // returns true if: -// - err is nil // - err only contains no such network errors func isOnlyErrorNoSuchNetwork(err error) bool { rerr := exec.RunErrorForError(err) @@ -291,7 +290,7 @@ func isOnlyErrorNoSuchNetwork(err error) bool { } else if err != nil { return false } - // if the line begins with Eror: No such network: it's fine + // if the line begins with Error: No such network: it's fine s := string(l) if strings.HasPrefix(s, "Error: No such network:") { continue 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 35d5344da5..b1786fbc97 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 @@ -25,8 +25,6 @@ import ( "path/filepath" "strings" - "k8s.io/apimachinery/pkg/util/sets" - "sigs.k8s.io/kind/pkg/cluster/nodes" "sigs.k8s.io/kind/pkg/errors" "sigs.k8s.io/kind/pkg/exec" @@ -38,6 +36,7 @@ import ( "sigs.k8s.io/kind/pkg/cluster/nodeutils" "sigs.k8s.io/kind/pkg/internal/apis/config" "sigs.k8s.io/kind/pkg/internal/cli" + "sigs.k8s.io/kind/pkg/internal/sets" ) // NewProvider returns a new provider based on executing `docker ...` @@ -124,7 +123,7 @@ func (p *provider) ListNodes(cluster string) ([]nodes.Node, error) { ) lines, err := exec.OutputLines(cmd) if err != nil { - return nil, errors.Wrap(err, "failed to list clusters") + return nil, errors.Wrap(err, "failed to list nodes") } // convert names to node handles ret := make([]nodes.Node, 0, len(lines)) 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 501618615e..97b05594b0 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 @@ -17,10 +17,12 @@ limitations under the License. package docker import ( + "context" "fmt" "net" "path/filepath" "strings" + "time" "sigs.k8s.io/kind/pkg/cluster/constants" "sigs.k8s.io/kind/pkg/errors" @@ -42,7 +44,7 @@ func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs name := nodeNamer(string(node.Role)) // name the node names[i] = name } - haveLoadbalancer := clusterHasImplicitLoadBalancer(cfg) + haveLoadbalancer := config.ClusterHasImplicitLoadBalancer(cfg) if haveLoadbalancer { names = append(names, nodeNamer(constants.ExternalLoadBalancerNodeRoleValue)) } @@ -74,7 +76,7 @@ func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs if err != nil { return err } - return createContainer(args) + return createContainer(name, args) }) } @@ -110,7 +112,7 @@ func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs if err != nil { return err } - return createContainer(args) + return createContainerWithWaitUntilSystemdReachesMultiUserSystem(name, args) }) case config.WorkerRole: createContainerFuncs = append(createContainerFuncs, func() error { @@ -118,7 +120,7 @@ func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs if err != nil { return err } - return createContainer(args) + return createContainerWithWaitUntilSystemdReachesMultiUserSystem(name, args) }) default: return nil, errors.Errorf("unknown node role: %q", node.Role) @@ -127,28 +129,6 @@ func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs return createContainerFuncs, nil } -func createContainer(args []string) error { - if err := exec.Command("docker", args...).Run(); err != nil { - return errors.Wrap(err, "docker run error") - } - return nil -} - -func clusterIsIPv6(cfg *config.Cluster) bool { - return cfg.Networking.IPFamily == config.IPv6Family || cfg.Networking.IPFamily == config.DualStackFamily -} - -func clusterHasImplicitLoadBalancer(cfg *config.Cluster) bool { - controlPlanes := 0 - for _, configNode := range cfg.Nodes { - role := string(configNode.Role) - if role == constants.ControlPlaneNodeRoleValue { - controlPlanes++ - } - } - return controlPlanes > 1 -} - // commonArgs computes static arguments that apply to all containers func commonArgs(cluster string, cfg *config.Cluster, networkName string, nodeNames []string) ([]string, error) { // standard arguments all nodes containers need, computed once @@ -190,7 +170,7 @@ func commonArgs(cluster string, cfg *config.Cluster, networkName string, nodeNam } // enable IPv6 if necessary - if clusterIsIPv6(cfg) { + if config.ClusterHasIPv6(cfg) { args = append(args, "--sysctl=net.ipv6.conf.all.disable_ipv6=0", "--sysctl=net.ipv6.conf.all.forwarding=1") } @@ -214,14 +194,17 @@ func commonArgs(cluster string, cfg *config.Cluster, networkName string, nodeNam args = append(args, "--volume", "/dev/mapper:/dev/mapper") } + // enable /dev/fuse explicitly for fuse-overlayfs + // (Rootless Docker does not automatically mount /dev/fuse with --privileged) + if mountFuse() { + args = append(args, "--device", "/dev/fuse") + } return args, nil } func runArgsForNode(node *config.Node, clusterIPFamily config.ClusterIPFamily, name string, args []string) ([]string, error) { args = append([]string{ - "run", "--hostname", name, // make hostname match container name - "--name", name, // ... and set the container name // label the node with the role ID "--label", fmt.Sprintf("%s=%s", nodeRoleLabelKey, node.Role), // running containers in a container requires privileged @@ -243,6 +226,8 @@ func runArgsForNode(node *config.Node, clusterIPFamily config.ClusterIPFamily, n "--volume", "/var", // some k8s things want to read /lib/modules "--volume", "/lib/modules:/lib/modules:ro", + // propagate KIND_EXPERIMENTAL_CONTAINERD_SNAPSHOTTER to the entrypoint script + "-e", "KIND_EXPERIMENTAL_CONTAINERD_SNAPSHOTTER", }, args..., ) @@ -266,9 +251,7 @@ func runArgsForNode(node *config.Node, clusterIPFamily config.ClusterIPFamily, n func runArgsForLoadBalancer(cfg *config.Cluster, name string, args []string) ([]string, error) { args = append([]string{ - "run", "--hostname", name, // make hostname match container name - "--name", name, // ... and set the container name // label the node with the role ID "--label", fmt.Sprintf("%s=%s", nodeRoleLabelKey, constants.ExternalLoadBalancerNodeRoleValue), }, @@ -371,7 +354,7 @@ func generatePortMappings(clusterIPFamily config.ClusterIPFamily, portMappings . // in a future API revision we will handle this at the API level and remove this if pm.ListenAddress == "" { switch clusterIPFamily { - case config.IPv4Family: + case config.IPv4Family, config.DualStackFamily: pm.ListenAddress = "0.0.0.0" // this is the docker default anyhow case config.IPv6Family: pm.ListenAddress = "::" @@ -405,3 +388,21 @@ func generatePortMappings(clusterIPFamily config.ClusterIPFamily, portMappings . } return args, nil } + +func createContainer(name string, args []string) error { + if err := exec.Command("docker", append([]string{"run", "--name", name}, args...)...).Run(); err != nil { + return err + } + return nil +} + +func createContainerWithWaitUntilSystemdReachesMultiUserSystem(name string, args []string) error { + if err := exec.Command("docker", append([]string{"run", "--name", name}, args...)...).Run(); err != nil { + return err + } + + logCtx, logCancel := context.WithTimeout(context.Background(), 30*time.Second) + logCmd := exec.CommandContext(logCtx, "docker", "logs", "-f", name) + defer logCancel() + return common.WaitUntilLogRegexpMatches(logCtx, logCmd, common.NodeReachedCgroupsReadyRegexp()) +} 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 82c1d34a93..2ec86d73fc 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 @@ -85,3 +85,16 @@ func mountDevMapper() bool { return storage == "btrfs" || storage == "zfs" || storage == "xfs" } + +// rootless: use fuse-overlayfs by default +// https://github.com/kubernetes-sigs/kind/issues/2275 +func mountFuse() bool { + i, err := info() + if err != nil { + return false + } + if i != nil && i.Rootless { + return true + } + return false +} diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/images.go b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/images.go index cf6f7c0f60..c44fead507 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/images.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/internal/providers/podman/images.go @@ -83,10 +83,33 @@ func pull(logger log.Logger, image string, retries int) error { // sanitizeImage is a helper to return human readable image name and // the podman pullable image name from the provided image -func sanitizeImage(image string) (string, string) { +func sanitizeImage(image string) (friendlyImageName, pullImageName string) { + const ( + defaultDomain = "docker.io/" + officialRepoName = "library" + ) + + var remainder string + if strings.Contains(image, "@sha256:") { splits := strings.Split(image, "@sha256:") - return splits[0], strings.Split(splits[0], ":")[0] + "@sha256:" + splits[1] + friendlyImageName = splits[0] + remainder = strings.Split(splits[0], ":")[0] + "@sha256:" + splits[1] + } else { + friendlyImageName = image + remainder = image + } + + if !strings.ContainsRune(remainder, '/') { + remainder = officialRepoName + "/" + remainder } - return image, image + + i := strings.IndexRune(friendlyImageName, '/') + if i == -1 || (!strings.ContainsAny(friendlyImageName[:i], ".:") && friendlyImageName[:i] != "localhost") { + pullImageName = defaultDomain + remainder + } else { + pullImageName = remainder + } + + return } 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 53cc1c5a8b..856b07b046 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 @@ -25,9 +25,6 @@ import ( "strconv" "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" "sigs.k8s.io/kind/pkg/errors" @@ -39,6 +36,8 @@ import ( "sigs.k8s.io/kind/pkg/cluster/internal/providers/common" "sigs.k8s.io/kind/pkg/internal/apis/config" "sigs.k8s.io/kind/pkg/internal/cli" + "sigs.k8s.io/kind/pkg/internal/sets" + "sigs.k8s.io/kind/pkg/internal/version" ) // NewProvider returns a new provider based on executing `podman ...` @@ -130,7 +129,7 @@ func (p *provider) ListNodes(cluster string) ([]nodes.Node, error) { ) lines, err := exec.OutputLines(cmd) if err != nil { - return nil, errors.Wrap(err, "failed to list clusters") + return nil, errors.Wrap(err, "failed to list nodes") } // convert names to node handles ret := make([]nodes.Node, 0, len(lines)) @@ -166,6 +165,9 @@ func (p *provider) DeleteNodes(n []nodes.Node) error { } nodeVolumes = append(nodeVolumes, volumes...) } + if len(nodeVolumes) == 0 { + return nil + } return deleteVolumes(nodeVolumes) } @@ -372,8 +374,9 @@ func (p *provider) Info() (*providers.ProviderInfo, error) { // and lacks information about the availability of the cgroup controllers. type podmanInfo struct { Host struct { - CgroupVersion string `json:"cgroupVersion,omitempty"` // "v2" - Security struct { + CgroupVersion string `json:"cgroupVersion,omitempty"` // "v2" + CgroupControllers []string `json:"cgroupControllers,omitempty"` + Security struct { Rootless bool `json:"rootless,omitempty"` } `json:"security"` } `json:"host"` @@ -393,23 +396,47 @@ func info(logger log.Logger) (*providers.ProviderInfo, error) { if err := json.Unmarshal(out, &pInfo); err != nil { return nil, err } + stringSliceContains := func(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + return false + } + + // Since Podman version before v4.0.0 does not gives controller info. + // We assume all the cgroup controllers to be available. + // For rootless, this assumption is not always correct, + // so we print the warning below. + cgroupSupportsMemoryLimit := true + cgroupSupportsPidsLimit := true + cgroupSupportsCPUShares := true + + v, err := getPodmanVersion() + if err != nil { + return nil, errors.Wrap(err, "failed to check podman version") + } + // Info for controllers must be available after v4.0.0 + // via https://github.com/containers/podman/pull/10387 + if v.AtLeast(version.MustParseSemantic("4.0.0")) { + cgroupSupportsMemoryLimit = stringSliceContains(pInfo.Host.CgroupControllers, "memory") + cgroupSupportsPidsLimit = stringSliceContains(pInfo.Host.CgroupControllers, "pids") + cgroupSupportsCPUShares = stringSliceContains(pInfo.Host.CgroupControllers, "cpu") + } + 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/") + Rootless: pInfo.Host.Security.Rootless, + Cgroup2: pInfo.Host.CgroupVersion == "v2", + SupportsMemoryLimit: cgroupSupportsMemoryLimit, + SupportsPidsLimit: cgroupSupportsPidsLimit, + SupportsCPUShares: cgroupSupportsCPUShares, + } + if info.Rootless && !v.AtLeast(version.MustParseSemantic("4.0.0")) { + if logger != nil { + 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 51dce4860b..50aa7018ea 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 @@ -17,10 +17,12 @@ limitations under the License. package podman import ( + "context" "fmt" "net" "path/filepath" "strings" + "time" "sigs.k8s.io/kind/pkg/cluster/constants" "sigs.k8s.io/kind/pkg/errors" @@ -43,7 +45,7 @@ func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs // only the external LB should reflect the port if we have multiple control planes apiServerPort := cfg.Networking.APIServerPort apiServerAddress := cfg.Networking.APIServerAddress - if clusterHasImplicitLoadBalancer(cfg) { + if config.ClusterHasImplicitLoadBalancer(cfg) { // TODO: picking ports locally is less than ideal with a remote runtime // (does podman have this?) // but this is supposed to be an implementation detail and NOT picking @@ -62,7 +64,7 @@ func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs if err != nil { return err } - return createContainer(args) + return createContainer(name, args) }) } @@ -96,7 +98,7 @@ func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs if err != nil { return err } - return createContainer(args) + return createContainerWithWaitUntilSystemdReachesMultiUserSystem(name, args) }) case config.WorkerRole: createContainerFuncs = append(createContainerFuncs, func() error { @@ -104,7 +106,7 @@ func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs if err != nil { return err } - return createContainer(args) + return createContainerWithWaitUntilSystemdReachesMultiUserSystem(name, args) }) default: return nil, errors.Errorf("unknown node role: %q", node.Role) @@ -113,28 +115,6 @@ func planCreation(cfg *config.Cluster, networkName string) (createContainerFuncs return createContainerFuncs, nil } -func createContainer(args []string) error { - if err := exec.Command("podman", args...).Run(); err != nil { - return errors.Wrap(err, "podman run error") - } - return nil -} - -func clusterIsIPv6(cfg *config.Cluster) bool { - return cfg.Networking.IPFamily == config.IPv6Family || cfg.Networking.IPFamily == config.DualStackFamily -} - -func clusterHasImplicitLoadBalancer(cfg *config.Cluster) bool { - controlPlanes := 0 - for _, configNode := range cfg.Nodes { - role := string(configNode.Role) - if role == constants.ControlPlaneNodeRoleValue { - controlPlanes++ - } - } - return controlPlanes > 1 -} - // commonArgs computes static arguments that apply to all containers func commonArgs(cfg *config.Cluster, networkName string) ([]string, error) { // standard arguments all nodes containers need, computed once @@ -149,7 +129,7 @@ func commonArgs(cfg *config.Cluster, networkName string) ([]string, error) { } // enable IPv6 if necessary - if clusterIsIPv6(cfg) { + if config.ClusterHasIPv6(cfg) { args = append(args, "--sysctl=net.ipv6.conf.all.disable_ipv6=0", "--sysctl=net.ipv6.conf.all.forwarding=1") } @@ -168,6 +148,12 @@ func commonArgs(cfg *config.Cluster, networkName string) ([]string, error) { args = append(args, "--volume", "/dev/mapper:/dev/mapper") } + // rootless: use fuse-overlayfs by default + // https://github.com/kubernetes-sigs/kind/issues/2275 + if mountFuse() { + args = append(args, "--device", "/dev/fuse") + } + return args, nil } @@ -180,9 +166,7 @@ func runArgsForNode(node *config.Node, clusterIPFamily config.ClusterIPFamily, n } args = append([]string{ - "run", "--hostname", name, // make hostname match container name - "--name", name, // ... and set the container name // label the node with the role ID "--label", fmt.Sprintf("%s=%s", nodeRoleLabelKey, node.Role), // running containers in a container requires privileged @@ -206,6 +190,8 @@ func runArgsForNode(node *config.Node, clusterIPFamily config.ClusterIPFamily, n "--volume", fmt.Sprintf("%s:/var:suid,exec,dev", varVolume), // some k8s things want to read /lib/modules "--volume", "/lib/modules:/lib/modules:ro", + // propagate KIND_EXPERIMENTAL_CONTAINERD_SNAPSHOTTER to the entrypoint script + "-e", "KIND_EXPERIMENTAL_CONTAINERD_SNAPSHOTTER", }, args..., ) @@ -230,9 +216,7 @@ func runArgsForNode(node *config.Node, clusterIPFamily config.ClusterIPFamily, n func runArgsForLoadBalancer(cfg *config.Cluster, name string, args []string) ([]string, error) { args = append([]string{ - "run", "--hostname", name, // make hostname match container name - "--name", name, // ... and set the container name // label the node with the role ID "--label", fmt.Sprintf("%s=%s", nodeRoleLabelKey, constants.ExternalLoadBalancerNodeRoleValue), }, @@ -336,7 +320,7 @@ func generatePortMappings(clusterIPFamily config.ClusterIPFamily, portMappings . // in a future API revision we will handle this at the API level and remove this if pm.ListenAddress == "" { switch clusterIPFamily { - case config.IPv4Family: + case config.IPv4Family, config.DualStackFamily: pm.ListenAddress = "0.0.0.0" case config.IPv6Family: pm.ListenAddress = "::" @@ -375,3 +359,21 @@ func generatePortMappings(clusterIPFamily config.ClusterIPFamily, portMappings . } return args, nil } + +func createContainer(name string, args []string) error { + if err := exec.Command("podman", append([]string{"run", "--name", name}, args...)...).Run(); err != nil { + return err + } + return nil +} + +func createContainerWithWaitUntilSystemdReachesMultiUserSystem(name string, args []string) error { + if err := exec.Command("podman", append([]string{"run", "--name", name}, args...)...).Run(); err != nil { + return err + } + + logCtx, logCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer logCancel() + logCmd := exec.CommandContext(logCtx, "podman", "logs", "-f", name) + return common.WaitUntilLogRegexpMatches(logCtx, logCmd, common.NodeReachedCgroupsReadyRegexp()) +} 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 eb274ab247..969f09ccbd 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 @@ -17,13 +17,14 @@ limitations under the License. package podman import ( + "encoding/json" "fmt" "strings" - "k8s.io/apimachinery/pkg/util/version" - "sigs.k8s.io/kind/pkg/errors" "sigs.k8s.io/kind/pkg/exec" + + "sigs.k8s.io/kind/pkg/internal/version" ) // IsAvailable checks if podman is available in the system @@ -99,6 +100,10 @@ func getVolumes(label string) ([]string, error) { if err != nil { return nil, err } + if string(output) == "" { + // no volumes + return nil, nil + } // Trim away the last `\n`. trimmedOutput := strings.TrimSuffix(string(output), "\n") // Get names of all volumes by splitting via `\n`. @@ -118,16 +123,47 @@ func deleteVolumes(names []string) error { // 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) + cmd := exec.Command("podman", "info", "--format", "json") + out, err := exec.Output(cmd) if err != nil { return false } - if len(lines) > 0 { - storage = strings.ToLower(strings.TrimSpace(lines[0])) + var pInfo podmanStorageInfo + if err := json.Unmarshal(out, &pInfo); err != nil { + return false + } + + // match docker logic pkg/cluster/internal/providers/docker/util.go + if pInfo.Store.GraphDriverName == "btrfs" || + pInfo.Store.GraphDriverName == "zfs" || + pInfo.Store.GraphDriverName == "devicemapper" || + pInfo.Store.GraphStatus.BackingFilesystem == "btrfs" || + pInfo.Store.GraphStatus.BackingFilesystem == "xfs" || + pInfo.Store.GraphStatus.BackingFilesystem == "zfs" { + return true + } + return false +} + +type podmanStorageInfo struct { + Store struct { + GraphDriverName string `json:"graphDriverName,omitempty"` + GraphStatus struct { + BackingFilesystem string `json:"Backing Filesystem,omitempty"` // "v2" + } `json:"graphStatus"` + } `json:"store"` +} + +// rootless: use fuse-overlayfs by default +// https://github.com/kubernetes-sigs/kind/issues/2275 +func mountFuse() bool { + i, err := info(nil) + if err != nil { + return false + } + if i != nil && i.Rootless { + return true } - return storage == "btrfs" || storage == "zfs" + return false } 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 df0af0c924..59c5211aaa 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/nodeutils/util.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/nodeutils/util.go @@ -23,6 +23,8 @@ import ( "path" "strings" + "github.com/pelletier/go-toml" + "sigs.k8s.io/kind/pkg/cluster/nodes" "sigs.k8s.io/kind/pkg/errors" "sigs.k8s.io/kind/pkg/exec" @@ -76,13 +78,37 @@ func CopyNodeToNode(a, b nodes.Node, file string) error { // LoadImageArchive loads image onto the node, where image is a Reader over an image archive func LoadImageArchive(n nodes.Node, image io.Reader) error { - cmd := n.Command("ctr", "--namespace=k8s.io", "images", "import", "-").SetStdin(image) + snapshotter, err := getSnapshotter(n) + if err != nil { + return err + } + cmd := n.Command("ctr", "--namespace=k8s.io", "images", "import", "--snapshotter", snapshotter, "-").SetStdin(image) if err := cmd.Run(); err != nil { return errors.Wrap(err, "failed to load image") } return nil } +func getSnapshotter(n nodes.Node) (string, error) { + out, err := exec.Output(n.Command("containerd", "config", "dump")) + if err != nil { + return "", errors.Wrap(err, "failed to detect containerd snapshotter") + } + return parseSnapshotter(string(out)) +} + +func parseSnapshotter(config string) (string, error) { + parsed, err := toml.Load(config) + if err != nil { + return "", errors.Wrap(err, "failed to detect containerd snapshotter") + } + snapshotter, ok := parsed.GetPath([]string{"plugins", "io.containerd.grpc.v1.cri", "containerd", "snapshotter"}).(string) + if !ok { + return "", errors.New("failed to detect containerd snapshotter") + } + return snapshotter, nil +} + // ImageID returns ID of image on the node with the given image name if present func ImageID(n nodes.Node, image string) (string, error) { var out bytes.Buffer diff --git a/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go b/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go index 7a6ae1e6b9..efd2a3ac44 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go +++ b/vendor/sigs.k8s.io/kind/pkg/cluster/provider.go @@ -204,8 +204,8 @@ func (p *Provider) KubeConfig(name string, internal bool) (string, error) { // it into the selected file, following the rules from // https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#config // where explicitPath is the --kubeconfig value. -func (p *Provider) ExportKubeConfig(name string, explicitPath string) error { - return kubeconfig.Export(p.provider, defaultName(name), explicitPath) +func (p *Provider) ExportKubeConfig(name string, explicitPath string, internal bool) error { + return kubeconfig.Export(p.provider, defaultName(name), explicitPath, !internal) } // ListNodes returns the list of container IDs for the "nodes" in the cluster 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 index 48b783e365..82290e5c05 100644 --- a/vendor/sigs.k8s.io/kind/pkg/cmd/kind/version/version.go +++ b/vendor/sigs.k8s.io/kind/pkg/cmd/kind/version/version.go @@ -50,7 +50,7 @@ func DisplayVersion() string { } // VersionCore is the core portion of the kind CLI version per Semantic Versioning 2.0.0 -const VersionCore = "0.11.1" +const VersionCore = "0.12.0" // VersionPreRelease is the pre-release portion of the kind CLI version per // Semantic Versioning 2.0.0 diff --git a/vendor/sigs.k8s.io/kind/pkg/errors/aggregate.go b/vendor/sigs.k8s.io/kind/pkg/errors/aggregate.go index d8ed79f077..5258cd2e17 100644 --- a/vendor/sigs.k8s.io/kind/pkg/errors/aggregate.go +++ b/vendor/sigs.k8s.io/kind/pkg/errors/aggregate.go @@ -16,18 +16,14 @@ limitations under the License. package errors -import ( - k8serrors "k8s.io/apimachinery/pkg/util/errors" -) - -// NewAggregate is a k8s.io/apimachinery/pkg/util/errors.NewAggregate wrapper +// NewAggregate is a k8s.io/apimachinery/pkg/util/errors.NewAggregate compatible wrapper // note that while it returns a StackTrace wrapped Aggregate // That has been Flattened and Reduced func NewAggregate(errlist []error) error { return WithStack( - k8serrors.Reduce( - k8serrors.Flatten( - k8serrors.NewAggregate(errlist), + reduce( + flatten( + newAggregate(errlist), ), ), ) @@ -35,9 +31,9 @@ func NewAggregate(errlist []error) error { // Errors returns the deepest Aggregate in a Cause chain func Errors(err error) []error { - var errors k8serrors.Aggregate + var errors Aggregate for { - if v, ok := err.(k8serrors.Aggregate); ok { + if v, ok := err.(Aggregate); ok { errors = v } if causerErr, ok := err.(Causer); ok { diff --git a/vendor/sigs.k8s.io/kind/pkg/errors/aggregate_forked.go b/vendor/sigs.k8s.io/kind/pkg/errors/aggregate_forked.go new file mode 100644 index 0000000000..3e9ec30b4d --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/errors/aggregate_forked.go @@ -0,0 +1,167 @@ +/* +Copyright 2021 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 errors + +import ( + "errors" + + "sigs.k8s.io/kind/pkg/internal/sets" +) + +/* + The contents of this file are lightly forked from k8s.io/apimachinery/pkg/util/errors + Forking makes kind easier to import, and this code is stable. + + Currently the only source changes are renaming some methods so as to not + export them. +*/ + +// Aggregate represents an object that contains multiple errors, but does not +// necessarily have singular semantic meaning. +// The aggregate can be used with `errors.Is()` to check for the occurrence of +// a specific error type. +// Errors.As() is not supported, because the caller presumably cares about a +// specific error of potentially multiple that match the given type. +// +// NOTE: this type is originally from k8s.io/apimachinery/pkg/util/errors.Aggregate +// Since it is an interface, you can use the implementing types interchangeably +type Aggregate interface { + error + Errors() []error + Is(error) bool +} + +func newAggregate(errlist []error) Aggregate { + if len(errlist) == 0 { + return nil + } + // In case of input error list contains nil + var errs []error + for _, e := range errlist { + if e != nil { + errs = append(errs, e) + } + } + if len(errs) == 0 { + return nil + } + return aggregate(errs) +} + +// flatten takes an Aggregate, which may hold other Aggregates in arbitrary +// nesting, and flattens them all into a single Aggregate, recursively. +func flatten(agg Aggregate) Aggregate { + result := []error{} + if agg == nil { + return nil + } + for _, err := range agg.Errors() { + if a, ok := err.(Aggregate); ok { + r := flatten(a) + if r != nil { + result = append(result, r.Errors()...) + } + } else { + if err != nil { + result = append(result, err) + } + } + } + return newAggregate(result) +} + +// reduce will return err or, if err is an Aggregate and only has one item, +// the first item in the aggregate. +func reduce(err error) error { + if agg, ok := err.(Aggregate); ok && err != nil { + switch len(agg.Errors()) { + case 1: + return agg.Errors()[0] + case 0: + return nil + } + } + return err +} + +// This helper implements the error and Errors interfaces. Keeping it private +// prevents people from making an aggregate of 0 errors, which is not +// an error, but does satisfy the error interface. +type aggregate []error + +// Error is part of the error interface. +func (agg aggregate) Error() string { + if len(agg) == 0 { + // This should never happen, really. + return "" + } + if len(agg) == 1 { + return agg[0].Error() + } + seenerrs := sets.NewString() + result := "" + agg.visit(func(err error) bool { + msg := err.Error() + if seenerrs.Has(msg) { + return false + } + seenerrs.Insert(msg) + if len(seenerrs) > 1 { + result += ", " + } + result += msg + return false + }) + if len(seenerrs) == 1 { + return result + } + return "[" + result + "]" +} + +func (agg aggregate) Is(target error) bool { + return agg.visit(func(err error) bool { + return errors.Is(err, target) + }) +} + +func (agg aggregate) visit(f func(err error) bool) bool { + for _, err := range agg { + switch err := err.(type) { + case aggregate: + if match := err.visit(f); match { + return match + } + case Aggregate: + for _, nestedErr := range err.Errors() { + if match := f(nestedErr); match { + return match + } + } + default: + if match := f(err); match { + return match + } + } + } + + return false +} + +// Errors is part of the Aggregate interface. +func (agg aggregate) Errors() []error { + return []error(agg) +} diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/cluster_util.go b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/cluster_util.go new file mode 100644 index 0000000000..79d3387fa7 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/internal/apis/config/cluster_util.go @@ -0,0 +1,34 @@ +/* +Copyright 2021 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 config + +// ClusterHasIPv6 returns true if the cluster should have IPv6 enabled due to either +// being IPv6 cluster family or Dual Stack +func ClusterHasIPv6(c *Cluster) bool { + return c.Networking.IPFamily == IPv6Family || c.Networking.IPFamily == DualStackFamily +} + +// ClusterHasImplicitLoadBalancer returns true if this cluster has an implicit api-server LoadBalancer +func ClusterHasImplicitLoadBalancer(c *Cluster) bool { + controlPlanes := 0 + for _, node := range c.Nodes { + if node.Role == ControlPlaneRole { + controlPlanes++ + } + } + return controlPlanes > 1 +} 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 33bbd721d9..dc5011ee0e 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 @@ -235,7 +235,7 @@ type PortMapping struct { HostPort int32 // TODO: add protocol (tcp/udp) and port-ranges ListenAddress string - // Protocol (TCP/UDP) + // Protocol (TCP/UDP/SCTP) Protocol PortMappingProtocol } 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 5152322a94..c05ff8b5da 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 @@ -51,14 +51,13 @@ func (c *Cluster) Validate() error { } } - isDualStack := c.Networking.IPFamily == DualStackFamily // podSubnet should be a valid CIDR - if err := validateSubnets(c.Networking.PodSubnet, isDualStack); err != nil { + if err := validateSubnets(c.Networking.PodSubnet, c.Networking.IPFamily); err != nil { errs = append(errs, errors.Errorf("invalid pod subnet %v", err)) } // serviceSubnet should be a valid CIDR - if err := validateSubnets(c.Networking.ServiceSubnet, isDualStack); err != nil { + if err := validateSubnets(c.Networking.ServiceSubnet, c.Networking.IPFamily); err != nil { errs = append(errs, errors.Errorf("invalid service subnet %v", err)) } @@ -140,7 +139,7 @@ func validatePort(port int32) error { return nil } -func validateSubnets(subnetStr string, dualstack bool) error { +func validateSubnets(subnetStr string, ipFamily ClusterIPFamily) error { allErrs := []error{} cidrsString := strings.Split(subnetStr, ",") @@ -153,7 +152,11 @@ func validateSubnets(subnetStr string, dualstack bool) error { subnets = append(subnets, cidr) } + dualstack := ipFamily == DualStackFamily switch { + // if no subnets are defined + case len(subnets) == 0: + allErrs = append(allErrs, errors.New("no subnets defined")) // 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")) @@ -168,6 +171,10 @@ func validateSubnets(subnetStr string, dualstack bool) error { // 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")) + case ipFamily == IPv4Family && subnets[0].IP.To4() == nil: + allErrs = append(allErrs, errors.New("expected IPv4 CIDR for IPv4 family")) + case ipFamily == IPv6Family && subnets[0].IP.To4() != nil: + allErrs = append(allErrs, errors.New("expected IPv6 CIDR for IPv6 family")) } if len(allErrs) > 0 { 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 3595f0ae36..14ecf0909c 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 @@ -1,3 +1,4 @@ +//go:build !ignore_autogenerated // +build !ignore_autogenerated /* diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/sets/doc.go b/vendor/sigs.k8s.io/kind/pkg/internal/sets/doc.go new file mode 100644 index 0000000000..70f70a926f --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/internal/sets/doc.go @@ -0,0 +1,25 @@ +/* +Copyright 2021 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 sets implements set types. +// +// This is forked from k8s.io/apimachinery/pkg/util/sets (under the same project +// and license), because k8s.io/apimachinery is a relatively heavy dependency +// and we only need some trivial utilities. Avoiding importing k8s.io/apimachinery +// makes kind easier to embed in other projects for testing etc. +// +// The set implementation is relatively small and very stable. +package sets diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/sets/empty.go b/vendor/sigs.k8s.io/kind/pkg/internal/sets/empty.go new file mode 100644 index 0000000000..e11e622c5b --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/internal/sets/empty.go @@ -0,0 +1,23 @@ +/* +Copyright 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. +*/ + +// Code generated by set-gen. DO NOT EDIT. + +package sets + +// Empty is public since it is used by some internal API objects for conversions between external +// string arrays and internal sets, and conversion logic requires public types today. +type Empty struct{} diff --git a/vendor/sigs.k8s.io/kind/pkg/internal/sets/string.go b/vendor/sigs.k8s.io/kind/pkg/internal/sets/string.go new file mode 100644 index 0000000000..e6f37db887 --- /dev/null +++ b/vendor/sigs.k8s.io/kind/pkg/internal/sets/string.go @@ -0,0 +1,205 @@ +/* +Copyright 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. +*/ + +// Code generated by set-gen. DO NOT EDIT. + +package sets + +import ( + "reflect" + "sort" +) + +// sets.String is a set of strings, implemented via map[string]struct{} for minimal memory consumption. +type String map[string]Empty + +// NewString creates a String from a list of values. +func NewString(items ...string) String { + ss := String{} + ss.Insert(items...) + return ss +} + +// StringKeySet creates a String from a keys of a map[string](? extends interface{}). +// If the value passed in is not actually a map, this will panic. +func StringKeySet(theMap interface{}) String { + v := reflect.ValueOf(theMap) + ret := String{} + + for _, keyValue := range v.MapKeys() { + ret.Insert(keyValue.Interface().(string)) + } + return ret +} + +// Insert adds items to the set. +func (s String) Insert(items ...string) String { + for _, item := range items { + s[item] = Empty{} + } + return s +} + +// Delete removes all items from the set. +func (s String) Delete(items ...string) String { + for _, item := range items { + delete(s, item) + } + return s +} + +// Has returns true if and only if item is contained in the set. +func (s String) Has(item string) bool { + _, contained := s[item] + return contained +} + +// HasAll returns true if and only if all items are contained in the set. +func (s String) HasAll(items ...string) bool { + for _, item := range items { + if !s.Has(item) { + return false + } + } + return true +} + +// HasAny returns true if any items are contained in the set. +func (s String) HasAny(items ...string) bool { + for _, item := range items { + if s.Has(item) { + return true + } + } + return false +} + +// Difference returns a set of objects that are not in s2 +// For example: +// s1 = {a1, a2, a3} +// s2 = {a1, a2, a4, a5} +// s1.Difference(s2) = {a3} +// s2.Difference(s1) = {a4, a5} +func (s String) Difference(s2 String) String { + result := NewString() + for key := range s { + if !s2.Has(key) { + result.Insert(key) + } + } + return result +} + +// Union returns a new set which includes items in either s1 or s2. +// For example: +// s1 = {a1, a2} +// s2 = {a3, a4} +// s1.Union(s2) = {a1, a2, a3, a4} +// s2.Union(s1) = {a1, a2, a3, a4} +func (s1 String) Union(s2 String) String { + result := NewString() + for key := range s1 { + result.Insert(key) + } + for key := range s2 { + result.Insert(key) + } + return result +} + +// Intersection returns a new set which includes the item in BOTH s1 and s2 +// For example: +// s1 = {a1, a2} +// s2 = {a2, a3} +// s1.Intersection(s2) = {a2} +func (s1 String) Intersection(s2 String) String { + var walk, other String + result := NewString() + if s1.Len() < s2.Len() { + walk = s1 + other = s2 + } else { + walk = s2 + other = s1 + } + for key := range walk { + if other.Has(key) { + result.Insert(key) + } + } + return result +} + +// IsSuperset returns true if and only if s1 is a superset of s2. +func (s1 String) IsSuperset(s2 String) bool { + for item := range s2 { + if !s1.Has(item) { + return false + } + } + return true +} + +// Equal returns true if and only if s1 is equal (as a set) to s2. +// Two sets are equal if their membership is identical. +// (In practice, this means same elements, order doesn't matter) +func (s1 String) Equal(s2 String) bool { + return len(s1) == len(s2) && s1.IsSuperset(s2) +} + +type sortableSliceOfString []string + +func (s sortableSliceOfString) Len() int { return len(s) } +func (s sortableSliceOfString) Less(i, j int) bool { return lessString(s[i], s[j]) } +func (s sortableSliceOfString) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// List returns the contents as a sorted string slice. +func (s String) List() []string { + res := make(sortableSliceOfString, 0, len(s)) + for key := range s { + res = append(res, key) + } + sort.Sort(res) + return []string(res) +} + +// UnsortedList returns the slice with contents in random order. +func (s String) UnsortedList() []string { + res := make([]string, 0, len(s)) + for key := range s { + res = append(res, key) + } + return res +} + +// Returns a single element from the set. +func (s String) PopAny() (string, bool) { + for key := range s { + s.Delete(key) + return key, true + } + var zeroValue string + return zeroValue, false +} + +// Len returns the size of the set. +func (s String) Len() int { + return len(s) +} + +func lessString(lhs, rhs string) bool { + return lhs < rhs +} diff --git a/vendor/k8s.io/apimachinery/pkg/util/version/doc.go b/vendor/sigs.k8s.io/kind/pkg/internal/version/doc.go similarity index 68% rename from vendor/k8s.io/apimachinery/pkg/util/version/doc.go rename to vendor/sigs.k8s.io/kind/pkg/internal/version/doc.go index 5b2b22b6d0..0bb753f51e 100644 --- a/vendor/k8s.io/apimachinery/pkg/util/version/doc.go +++ b/vendor/sigs.k8s.io/kind/pkg/internal/version/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors. +Copyright 2021 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. @@ -15,4 +15,8 @@ limitations under the License. */ // Package version provides utilities for version number comparisons -package version // import "k8s.io/apimachinery/pkg/util/version" +// +// This is forked from k8s.io/apimachinery/pkg/util/version to make +// kind easier to import (k8s.io/apimachinery/pkg/util/version is a stable, +// mature package with no externaldependencies within a large, heavy module) +package version diff --git a/vendor/k8s.io/apimachinery/pkg/util/version/version.go b/vendor/sigs.k8s.io/kind/pkg/internal/version/version.go similarity index 100% rename from vendor/k8s.io/apimachinery/pkg/util/version/version.go rename to vendor/sigs.k8s.io/kind/pkg/internal/version/version.go