diff --git a/Makefile b/Makefile index eb67172a3bd5..c54fa88395cd 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,9 @@ CONVERSION_GEN := $(abspath $(TOOLS_BIN_DIR)/conversion-gen) # Bindata. GOBINDATA := $(abspath $(TOOLS_BIN_DIR)/go-bindata) GOBINDATA_CLUSTERCTL_DIR := cmd/clusterctl/config +CLOUDINIT_PKG_DIR := bootstrap/kubeadm/internal/cloudinit +CLOUDINIT_GENERATED := $(CLOUDINIT_PKG_DIR)/zz_generated.bindata.go +CLOUDINIT_SCRIPT := $(CLOUDINIT_PKG_DIR)/kubeadm-bootstrap-script.sh CERTMANAGER_COMPONENTS_GENERATED_FILE := cert-manager.yaml # Define Docker related variables. Releases should modify and double check these vars. @@ -242,7 +245,7 @@ generate-go-kubeadm-control-plane: $(CONTROLLER_GEN) $(CONVERSION_GEN) ## Runs G paths=./controlplane/kubeadm/api/... .PHONY: generate-bindata -generate-bindata: $(KUSTOMIZE) $(GOBINDATA) clean-bindata ## Generate code for embedding the clusterctl api manifest +generate-bindata: $(KUSTOMIZE) $(GOBINDATA) clean-bindata $(CLOUDINIT_GENERATED) ## Generate code for embedding the clusterctl api manifest # Package manifest YAML into a single file. mkdir -p $(GOBINDATA_CLUSTERCTL_DIR)/manifest/ $(KUSTOMIZE) build $(GOBINDATA_CLUSTERCTL_DIR)/crd > $(GOBINDATA_CLUSTERCTL_DIR)/manifest/clusterctl-api.yaml @@ -255,6 +258,11 @@ generate-bindata: $(KUSTOMIZE) $(GOBINDATA) clean-bindata ## Generate code for e # Cleanup the manifest folder. $(MAKE) clean-bindata +$(CLOUDINIT_GENERATED): $(GOBINDATA) $(CLOUDINIT_SCRIPT) + $(GOBINDATA) -mode=420 -modtime=1 -pkg=cloudinit -o=$(CLOUDINIT_GENERATED).tmp $(CLOUDINIT_SCRIPT) + cat ./hack/boilerplate/boilerplate.generatego.txt $(CLOUDINIT_GENERATED).tmp > $(CLOUDINIT_GENERATED) + rm $(CLOUDINIT_GENERATED).tmp + .PHONY: generate-manifests generate-manifests: ## Generate manifests e.g. CRD, RBAC etc. $(MAKE) generate-core-manifests diff --git a/bootstrap/kubeadm/api/v1alpha2/conversion.go b/bootstrap/kubeadm/api/v1alpha2/conversion.go index aef0b655cc71..33e121058dc4 100644 --- a/bootstrap/kubeadm/api/v1alpha2/conversion.go +++ b/bootstrap/kubeadm/api/v1alpha2/conversion.go @@ -38,6 +38,7 @@ func (src *KubeadmConfig) ConvertTo(dstRaw conversion.Hub) error { dst.Status.DataSecretName = restored.Status.DataSecretName dst.Spec.Verbosity = restored.Spec.Verbosity + dst.Spec.UseExperimentalRetryJoin = restored.Spec.UseExperimentalRetryJoin return nil } @@ -119,7 +120,6 @@ func Convert_v1alpha3_KubeadmConfigStatus_To_v1alpha2_KubeadmConfigStatus(in *ku return nil } - // Convert_v1alpha2_KubeadmConfigSpec_To_v1alpha3_KubeadmConfigSpec converts this KubeadmConfigSpec to the Hub version (v1alpha3). func Convert_v1alpha2_KubeadmConfigSpec_To_v1alpha3_KubeadmConfigSpec(in *KubeadmConfigSpec, out *kubeadmbootstrapv1alpha3.KubeadmConfigSpec, s apiconversion.Scope) error { return autoConvert_v1alpha2_KubeadmConfigSpec_To_v1alpha3_KubeadmConfigSpec(in, out, s) diff --git a/bootstrap/kubeadm/api/v1alpha2/zz_generated.conversion.go b/bootstrap/kubeadm/api/v1alpha2/zz_generated.conversion.go index 3d2e5705dad4..147e2f07ddb8 100644 --- a/bootstrap/kubeadm/api/v1alpha2/zz_generated.conversion.go +++ b/bootstrap/kubeadm/api/v1alpha2/zz_generated.conversion.go @@ -275,6 +275,7 @@ func autoConvert_v1alpha3_KubeadmConfigSpec_To_v1alpha2_KubeadmConfigSpec(in *v1 out.NTP = (*NTP)(unsafe.Pointer(in.NTP)) out.Format = Format(in.Format) // WARNING: in.Verbosity requires manual conversion: does not exist in peer-type + // WARNING: in.UseExperimentalRetryJoin requires manual conversion: does not exist in peer-type return nil } diff --git a/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go b/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go index a82c5e7ed373..5a99cca6150c 100644 --- a/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go +++ b/bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go @@ -73,6 +73,19 @@ type KubeadmConfigSpec struct { // It overrides the `--v` flag in kubeadm commands. // +optional Verbosity *int32 `json:"verbosity,omitempty"` + + // UseExperimentalRetryJoin replaces a basic kubeadm command with a shell + // script with retries for joins. + // + // This is meant to be an experimental temporary workaround on some environments + // where joins fail due to timing (and other issues). The long term goal is to add retries to + // kubeadm proper and use that functionality. + // + // This will add about 40KB to userdata + // + // For more information, refer to https://github.com/kubernetes-sigs/cluster-api/pull/2763#discussion_r397306055. + // +optional + UseExperimentalRetryJoin bool `json:"useExperimentalRetryJoin,omitempty"` } // KubeadmConfigStatus defines the observed state of KubeadmConfig diff --git a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml index 598d243cdf69..f28d045a5e4e 100644 --- a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml +++ b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigs.yaml @@ -1554,6 +1554,15 @@ spec: items: type: string type: array + useExperimentalRetryJoin: + description: "UseExperimentalRetryJoin replaces a basic kubeadm command + with a shell script with retries for joins. \n This is meant to + be an experimental temporary workaround on some environments where + joins fail due to timing (and other issues). The long term goal + is to add retries to kubeadm proper and use that functionality. + \n This will add about 40KB to userdata \n For more information, + refer to https://github.com/kubernetes-sigs/cluster-api/pull/2763#discussion_r397306055." + type: boolean users: description: Users specifies extra users to add items: diff --git a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml index 337dc76f510e..3dcc64dccab0 100644 --- a/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml +++ b/bootstrap/kubeadm/config/crd/bases/bootstrap.cluster.x-k8s.io_kubeadmconfigtemplates.yaml @@ -1633,6 +1633,15 @@ spec: items: type: string type: array + useExperimentalRetryJoin: + description: "UseExperimentalRetryJoin replaces a basic kubeadm + command with a shell script with retries for joins. \n This + is meant to be an experimental temporary workaround on some + environments where joins fail due to timing (and other issues). + The long term goal is to add retries to kubeadm proper and + use that functionality. \n This will add about 40KB to userdata + \n For more information, refer to https://github.com/kubernetes-sigs/cluster-api/pull/2763#discussion_r397306055." + type: boolean users: description: Users specifies extra users to add items: diff --git a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go index 0e613b88c2b0..31ef04533367 100644 --- a/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go +++ b/bootstrap/kubeadm/controllers/kubeadmconfig_controller.go @@ -489,8 +489,9 @@ func (r *KubeadmConfigReconciler) joinControlplane(ctx context.Context, scope *S } cloudJoinData, err := cloudinit.NewJoinControlPlane(&cloudinit.ControlPlaneJoinInput{ - JoinConfiguration: joinData, - Certificates: certificates, + JoinConfiguration: joinData, + Certificates: certificates, + UseExperimentalRetry: scope.Config.Spec.UseExperimentalRetryJoin, BaseUserData: cloudinit.BaseUserData{ AdditionalFiles: scope.Config.Spec.Files, NTP: scope.Config.Spec.NTP, diff --git a/bootstrap/kubeadm/internal/cloudinit/controlplane_join.go b/bootstrap/kubeadm/internal/cloudinit/controlplane_join.go index b8475b18c996..ae0ff0520f7f 100644 --- a/bootstrap/kubeadm/internal/cloudinit/controlplane_join.go +++ b/bootstrap/kubeadm/internal/cloudinit/controlplane_join.go @@ -17,12 +17,19 @@ limitations under the License. package cloudinit import ( - "github.com/pkg/errors" + "fmt" + "github.com/pkg/errors" + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha3" "sigs.k8s.io/cluster-api/util/secret" ) const ( + standardJoinCommand = "kubeadm join --config /tmp/kubeadm-controlplane-join-config.yaml %s" + retriableJoinScriptName = "/usr/local/bin/kubeadm-bootstrap-script" + retriableJoinScriptOwner = "root" + retriableJoinScriptPermissions = "0755" + controlPlaneJoinCloudInit = `{{.Header}} {{template "files" .WriteFiles}} - path: /tmp/kubeadm-controlplane-join-config.yaml @@ -32,7 +39,7 @@ const ( {{.JoinConfiguration | Indent 6}} runcmd: {{- template "commands" .PreKubeadmCommands }} - - 'kubeadm join --config /tmp/kubeadm-controlplane-join-config.yaml {{.KubeadmVerbosity}}' + - {{ .KubeadmCommand }} {{- template "commands" .PostKubeadmCommands }} {{- template "ntp" .NTP }} {{- template "users" .Users }} @@ -43,9 +50,10 @@ runcmd: type ControlPlaneJoinInput struct { BaseUserData secret.Certificates - - BootstrapToken string - JoinConfiguration string + UseExperimentalRetry bool + KubeadmCommand string + BootstrapToken string + JoinConfiguration string } // NewJoinControlPlane returns the user data string to be used on a new control plane instance. @@ -54,6 +62,13 @@ func NewJoinControlPlane(input *ControlPlaneJoinInput) ([]byte, error) { // TODO: Consider validating that the correct certificates exist. It is different for external/stacked etcd input.WriteFiles = input.Certificates.AsFiles() input.WriteFiles = append(input.WriteFiles, input.AdditionalFiles...) + input.KubeadmCommand = fmt.Sprintf(standardJoinCommand, input.KubeadmVerbosity) + if input.UseExperimentalRetry { + err := input.useBootstrapScript() + if err != nil { + return nil, err + } + } userData, err := generate("JoinControlplane", controlPlaneJoinCloudInit, input) if err != nil { return nil, errors.Wrapf(err, "failed to generate user data for machine joining control plane") @@ -61,3 +76,23 @@ func NewJoinControlPlane(input *ControlPlaneJoinInput) ([]byte, error) { return userData, err } + +func (input *ControlPlaneJoinInput) useBootstrapScript() error { + scriptBytes, err := bootstrapKubeadmInternalCloudinitKubeadmBootstrapScriptShBytes() + if err != nil { + return errors.Wrap(err, "couldn't read bootstrap script") + } + joinScript, err := generate("JoinControlplaneScript", string(scriptBytes), input) + if err != nil { + return errors.Wrap(err, "failed to generate user data for machine joining control plane") + } + joinScriptFile := bootstrapv1.File{ + Path: retriableJoinScriptName, + Owner: retriableJoinScriptOwner, + Permissions: retriableJoinScriptPermissions, + Content: string(joinScript), + } + input.WriteFiles = append(input.WriteFiles, joinScriptFile) + input.KubeadmCommand = retriableJoinScriptName + return nil +} diff --git a/bootstrap/kubeadm/internal/cloudinit/kubeadm-bootstrap-script.sh b/bootstrap/kubeadm/internal/cloudinit/kubeadm-bootstrap-script.sh new file mode 100644 index 000000000000..8b42d367ec3b --- /dev/null +++ b/bootstrap/kubeadm/internal/cloudinit/kubeadm-bootstrap-script.sh @@ -0,0 +1,129 @@ +#!/bin/bash +# 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. + +# Log an error and exit. +# Args: +# $1 Message to log with the error +# $2 The error code to return +log::error_exit() { + local message="${1}" + local code="${2}" + + log::error "${message}" + log::info "Removing member from cluster status" + kubeadm reset -f update-cluster-status || true + log::info "Removing etcd member" + kubeadm reset -f remove-etcd-member || true + log::info "Resetting kubeadm" + kubeadm reset -f || true + log::error "cluster.x-k8s.io kubeadm bootstrap script $0 exiting with status ${code}" + exit "${code}" +} + +log::success_exit() { + log::info "cluster.x-k8s.io kubeadm bootstrap script $0 finished" + exit 0 +} + +# Log an error but keep going. +log::error() { + local message="${1}" + timestamp=$(date --iso-8601=seconds) + echo "!!! [${timestamp}] ${1}" >&2 + shift + for message; do + echo " ${message}" >&2 + done +} + +# Print a status line. Formatted to show up in a stream of output. +log::info() { + timestamp=$(date --iso-8601=seconds) + echo "+++ [${timestamp}] ${1}" + shift + for message; do + echo " ${message}" + done +} + +check_kubeadm_command() { + local command="${1}" + local code="${2}" + case ${code} in + "0") + log::info "kubeadm reported successful execution for ${command}" + ;; + "1") + log::error "kubeadm reported failed action(s) for ${command}" + ;; + "2") + log::error "kubeadm reported preflight check error during ${command}" + ;; + "3") + log::error_exit "kubeadm reported validation error for ${command}" + ;; + *) + log::error "kubeadm reported unknown error ${code} for ${command}" + ;; + esac +} + +function retry-command() { + n=0 + local kubeadm_return + until [ $n -ge 5 ]; do + log::info "running '$*'" + # shellcheck disable=SC1083 + "$@" --config /tmp/kubeadm-controlplane-join-config.yaml {{.KubeadmVerbosity}} + kubeadm_return=$? + check_kubeadm_command "'$*'" "${kubeadm_return}" + if [ ${kubeadm_return} -eq 0 ]; then + break + fi + # We allow preflight errors to pass + if [ ${kubeadm_return} -eq 2 ]; then + break + fi + n=$((n + 1)) + sleep 15 + done + if [ ${kubeadm_return} -ne 0 ]; then + log::error_exit "too many errors, exiting" + fi +} + +function try-or-die-command() { + local kubeadm_return + log::info "running '$*'" + # shellcheck disable=SC1083 + "$@" --config /tmp/kubeadm-controlplane-join-config.yaml {{.KubeadmVerbosity}} + kubeadm_return=$? + check_kubeadm_command "'$*'" "${kubeadm_return}" + if [ ${kubeadm_return} -ne 0 ]; then + log::error_exit "fatal error, exiting" + fi +} + +retry-command kubeadm join phase preflight +retry-command kubeadm join phase control-plane-prepare download-certs +retry-command kubeadm join phase control-plane-prepare certs +retry-command kubeadm join phase control-plane-prepare kubeconfig +retry-command kubeadm join phase control-plane-prepare control-plane +retry-command kubeadm join phase kubelet-start +try-or-die-command kubeadm join phase control-plane-join etcd +retry-command kubeadm join phase control-plane-join update-status +retry-command kubeadm join phase control-plane-join mark-control-plane + +log::success_exit diff --git a/bootstrap/kubeadm/internal/cloudinit/zz_generated.bindata.go b/bootstrap/kubeadm/internal/cloudinit/zz_generated.bindata.go new file mode 100644 index 000000000000..d0039238ed87 --- /dev/null +++ b/bootstrap/kubeadm/internal/cloudinit/zz_generated.bindata.go @@ -0,0 +1,268 @@ +/* +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 for package cloudinit by go-bindata DO NOT EDIT. (@generated) +// sources: +// bootstrap/kubeadm/internal/cloudinit/kubeadm-bootstrap-script.sh +package cloudinit + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +// Name return file name +func (fi bindataFileInfo) Name() string { + return fi.name +} + +// Size return file size +func (fi bindataFileInfo) Size() int64 { + return fi.size +} + +// Mode return file mode +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} + +// Mode return file modify time +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} + +// IsDir return file whether a directory +func (fi bindataFileInfo) IsDir() bool { + return fi.mode&os.ModeDir != 0 +} + +// Sys return file is sys mode +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _bootstrapKubeadmInternalCloudinitKubeadmBootstrapScriptSh = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x56\x71\x4f\x1b\xb9\x13\xfd\x7f\x3f\xc5\x23\x89\x7e\x85\xc2\x26\x21\x55\x7f\xaa\x40\xdc\x5d\x8e\xb6\xba\xa8\x3d\x38\x11\xda\xaa\xaa\x2a\xe4\xec\xce\xee\xfa\xf0\xda\x5b\xdb\xdb\x10\x51\xbe\xfb\xc9\x5e\x27\x4d\x48\x80\x96\xbb\xfc\x15\x8d\x67\xde\xcc\xbc\x79\x1e\x6f\x7b\xab\x37\xe1\xb2\x37\x61\xa6\x88\xda\x38\x56\xd5\x4c\xf3\xbc\xb0\x18\xf4\x07\x7d\x9c\x17\x84\x37\xf5\x84\xb4\x24\x4b\x06\xc3\xda\x16\x4a\x9b\x6e\xd4\x8e\xda\x78\xcb\x13\x92\x86\x52\xd4\x32\x25\x0d\x5b\x10\x86\x15\x4b\x0a\x9a\x9f\xec\xe1\x3d\x69\xc3\x95\xc4\xa0\xdb\xc7\xb6\x73\x68\x85\xa3\xd6\xce\x61\xd4\xc6\x4c\xd5\x28\xd9\x0c\x52\x59\xd4\x86\x60\x0b\x6e\x90\x71\x41\xa0\xab\x84\x2a\x0b\x2e\x91\xa8\xb2\x12\x9c\xc9\x84\x30\xe5\xb6\xf0\x69\x02\x48\x37\x6a\xe3\x63\x80\x50\x13\xcb\xb8\x04\x43\xa2\xaa\x19\x54\xb6\xec\x07\x66\x7d\xc1\xee\x57\x58\x5b\x1d\xf4\x7a\xd3\xe9\xb4\xcb\x7c\xb1\x5d\xa5\xf3\x9e\x68\x1c\x4d\xef\xed\xe8\xf8\xd5\xc9\xf8\x55\x3c\xe8\xf6\x7d\xc8\x3b\x29\xc8\x18\x68\xfa\x52\x73\x4d\x29\x26\x33\xb0\xaa\x12\x3c\x61\x13\x41\x10\x6c\x0a\xa5\xc1\x72\x4d\x94\xc2\x2a\x57\xef\x54\x73\xcb\x65\xbe\x07\xa3\x32\x3b\x65\x9a\xa2\x36\x52\x6e\xac\xe6\x93\xda\xae\x90\x35\xaf\x8e\x9b\x15\x07\x25\xc1\x24\x5a\xc3\x31\x46\xe3\x16\x7e\x1f\x8e\x47\xe3\xbd\xa8\x8d\x0f\xa3\xf3\x3f\x4e\xdf\x9d\xe3\xc3\xf0\xec\x6c\x78\x72\x3e\x7a\x35\xc6\xe9\x19\x8e\x4f\x4f\x5e\x8e\xce\x47\xa7\x27\x63\x9c\xbe\xc6\xf0\xe4\x23\xde\x8c\x4e\x5e\xee\x81\xb8\x2d\x48\x83\xae\x2a\xed\xea\x57\x1a\xdc\xd1\x48\xa9\xe3\x6c\x4c\xb4\x52\x40\xa6\x9a\x82\x4c\x45\x09\xcf\x78\x02\xc1\x64\x5e\xb3\x9c\x90\xab\xaf\xa4\x25\x97\x39\x2a\xd2\x25\x37\x6e\x98\x06\x4c\xa6\x51\x1b\x82\x97\xdc\x32\xeb\x2d\x6b\x4d\x75\x23\x27\x10\x95\xbb\x56\x48\x6b\x47\x92\x4c\x41\x57\xdc\xba\x02\x86\x3a\x37\x07\x7e\x20\x9d\x7d\xfc\x49\xc6\xb8\x5c\x56\x41\xa8\xfc\xfb\x90\x7d\x58\xe3\x34\xf0\x3a\x6c\x70\x12\x95\x7a\x5f\x4d\xb6\xd6\x32\x12\x2a\x3f\x38\xf0\x27\x17\x0e\x7d\x7b\x07\xd7\x11\x20\x54\xc2\x04\xca\x06\xf9\xa8\xd5\xb9\xde\xbf\x69\x2d\xcc\x0e\xc1\xd9\x06\x37\xad\xc8\x1b\xe7\x08\x68\x75\xae\x43\x4c\x70\xcf\x0f\x0e\xb8\xcc\x14\x5a\x67\x54\xaa\xaf\x8e\x87\x92\xca\x09\x69\x64\x5a\x95\x48\x44\x6d\x2c\x69\x18\xcb\x6c\x6d\x5c\xc4\x65\x3d\x21\x96\x96\xd0\x64\xc8\x22\xce\x50\x57\x29\xb3\x14\x07\xcf\xb8\xf1\xc4\xb7\x6f\xb0\xba\xa6\x3b\x52\x90\x4d\xd2\x90\x67\x23\xa6\x76\x8e\x14\x3b\xb7\x38\x94\x73\x07\xa0\x21\xeb\xb4\x38\x87\xd8\x88\x76\x2b\x34\x10\x11\x0a\xee\x5e\xc5\x97\x2f\x4c\x97\xab\x45\xdc\x44\x29\x6b\xac\x66\x15\x4c\xa2\x79\x65\xd1\xe9\xfb\xb1\xba\x34\x7e\x74\xa1\xc5\xce\xb5\xa3\xd9\xd3\xe8\x8e\x1d\xb5\xc1\x70\x13\x35\x43\x33\x75\x92\x90\x31\xab\x63\x5b\x14\xff\x53\x05\x64\x5c\x72\x53\x50\xba\xc8\xd6\x77\x59\x6e\x09\x70\x52\x5b\x5c\x12\x55\xc8\x15\x97\x79\x77\x49\x39\xf7\x8b\xc6\xf2\x92\x8c\x65\x65\x75\xd4\xd9\x76\xc3\x44\x1c\x73\xa3\xe2\x17\xff\xef\xef\x1f\x19\x4a\x94\x4c\xcd\x8e\xcb\x9b\x14\x0a\xad\xad\xad\x2d\x7c\xea\x5c\x2f\x62\x6e\x3e\xc3\xe3\xe0\x97\xff\x0d\x22\xc0\x14\x3c\xb3\x11\xfc\x8d\x0b\x89\x0e\x91\xaa\xc8\x6d\xa6\x06\xc0\xfd\x5b\x52\x61\x88\x4b\x95\xa4\xa6\xa5\xbf\x34\x97\x16\x6c\x4e\xb3\xe0\x92\xba\xc0\x6b\xa5\x4b\x66\x6d\xb3\x84\x4c\xa1\xa6\xa8\x2b\xf8\x75\x68\xac\x26\x56\xba\x85\xa8\x6a\x5b\xd5\x36\xf4\xed\x48\x0e\x6d\xff\x54\x7f\xbb\xbb\xbb\x1b\xfb\x7b\x4c\x6f\x4b\x7d\x25\x05\x25\x97\x17\x61\xc4\x17\x89\x2a\x4b\x26\xd3\x95\xb1\x04\xdb\x7d\x77\x19\x48\x98\xa1\xb9\xf2\xc0\x65\x04\xb4\xfa\xad\x1d\x5f\xc1\x92\xb4\xbe\x5f\x81\x4a\x69\xc7\x59\x50\x62\x56\x0b\xd0\x15\x25\xb5\xdb\x69\xbe\x0d\x07\xe5\xd3\x7a\x74\xe0\xf0\xd0\x41\xee\x2f\x43\x86\xfb\xb2\x86\x99\x31\x2e\x28\x05\x4b\x1c\xd8\xb6\xd9\xb9\x07\x6f\xf0\x23\x78\x95\xa6\x4c\xf8\x77\xd9\x73\x15\x34\x9d\xd6\xda\x5d\xbc\xcd\xb8\xcf\xd6\x70\x2f\x9a\xab\xb8\x06\xfe\x95\x09\x9e\xfa\x55\x1e\x70\xef\x2c\xf6\xe9\x0f\x94\x5a\xcb\x4b\xa9\xa6\x73\xa8\xf9\x38\xee\x84\x24\xc3\x12\xa7\x81\xac\x96\x9e\x2c\xb7\xd9\xf5\x2c\x5e\x15\x81\x3c\xea\x2f\x66\x3e\x97\x49\x78\x01\x80\x5a\x5a\x2e\xf0\x09\x1d\x89\x38\x27\x3c\xc7\xe7\x85\xf0\x96\xc6\xae\x6b\xe9\x5f\xb2\x27\x9d\xa7\x4f\x9a\xf4\x6d\x98\x82\x84\x68\x08\x4d\xb9\x71\x6f\xfa\xd1\xf8\x78\xbf\xff\xe2\x99\x3f\x6f\x75\x7e\x6b\x21\x8e\x13\x25\x33\x9e\xa3\x67\xcb\xaa\x17\x72\x3b\x9b\xd5\x4a\x54\x82\x49\x8a\xff\x56\x5c\x06\xaf\xee\x8c\x95\x02\xd7\xd7\xdd\x37\x8d\xe3\x7b\xd2\x13\x65\xb8\x9d\xdd\xdc\x78\xc8\xd5\xda\x8f\x3a\xbf\x7a\xeb\x46\xf9\xa3\xe5\x2b\x75\x9b\x73\x35\x2a\x90\xc7\x33\xd7\xf2\xed\x33\xc4\xf4\x05\x7d\xc7\x80\x2d\x48\x7a\x47\x60\xa2\x89\x5d\xfa\xff\x19\x0f\x9d\x7f\x20\x30\x21\xd4\x74\x49\x58\x7e\x5e\xc6\x6d\x90\x8a\x19\xf3\x50\x8e\xc1\x43\x39\xe4\x51\x67\x7b\x5b\x62\x17\xfb\x3b\x8d\x68\x8c\x70\xdb\x77\xff\xf9\xfc\xde\xdf\x0d\x2f\xe9\x56\x0b\x6b\x12\xb6\x4a\xa1\x64\x72\x16\x8a\xde\x9b\xbf\x41\x8e\x9a\x8c\xaf\xc8\xc9\x89\x49\xe9\x38\xe5\x14\x6f\x5a\x2c\x6b\x6a\xba\x47\x32\xf7\x0b\xe6\x3f\x97\xcb\x26\xb1\x3c\x42\x2a\x8f\x67\x39\x63\x96\x89\x86\xe2\x0d\x0c\xaf\xdc\xd3\xc5\xfb\xec\xba\x43\x55\xb8\x4d\xbc\x90\xd6\xc3\xae\x81\xa0\xb8\x61\xa8\xd2\x54\x31\x4d\x48\xd5\x54\x0a\xc5\xd2\x38\x21\x6d\xcd\x63\x51\xfe\x55\xb0\x73\x6c\x66\xf5\xe8\xf4\xcb\xd6\x87\x41\x9c\x49\x90\x75\x9f\x8a\xda\x46\xeb\xe2\x7d\x38\xb1\x3f\x70\x5f\x87\x3f\x5b\xb1\x3f\x08\x5f\xac\xcd\xf7\xc5\xa3\x10\x4a\xa6\x2f\xe3\xd5\xae\xd7\xbf\xf8\xa2\x7f\x02\x00\x00\xff\xff\x0b\x2a\xd2\x84\x77\x0e\x00\x00") + +func bootstrapKubeadmInternalCloudinitKubeadmBootstrapScriptShBytes() ([]byte, error) { + return bindataRead( + _bootstrapKubeadmInternalCloudinitKubeadmBootstrapScriptSh, + "bootstrap/kubeadm/internal/cloudinit/kubeadm-bootstrap-script.sh", + ) +} + +func bootstrapKubeadmInternalCloudinitKubeadmBootstrapScriptSh() (*asset, error) { + bytes, err := bootstrapKubeadmInternalCloudinitKubeadmBootstrapScriptShBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "bootstrap/kubeadm/internal/cloudinit/kubeadm-bootstrap-script.sh", size: 3703, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "bootstrap/kubeadm/internal/cloudinit/kubeadm-bootstrap-script.sh": bootstrapKubeadmInternalCloudinitKubeadmBootstrapScriptSh, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "bootstrap": &bintree{nil, map[string]*bintree{ + "kubeadm": &bintree{nil, map[string]*bintree{ + "internal": &bintree{nil, map[string]*bintree{ + "cloudinit": &bintree{nil, map[string]*bintree{ + "kubeadm-bootstrap-script.sh": &bintree{bootstrapKubeadmInternalCloudinitKubeadmBootstrapScriptSh, map[string]*bintree{}}, + }}, + }}, + }}, + }}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} diff --git a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml index 61e0a46ce701..6ce27516bb7d 100644 --- a/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml +++ b/controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml @@ -840,6 +840,15 @@ spec: items: type: string type: array + useExperimentalRetryJoin: + description: "UseExperimentalRetryJoin replaces a basic kubeadm + command with a shell script with retries for joins. \n This + is meant to be an experimental temporary workaround on some + environments where joins fail due to timing (and other issues). + The long term goal is to add retries to kubeadm proper and use + that functionality. \n This will add about 40KB to userdata + \n For more information, refer to https://github.com/kubernetes-sigs/cluster-api/pull/2763#discussion_r397306055." + type: boolean users: description: Users specifies extra users to add items: