diff --git a/components/kubebins/action.pb.go b/components/kubebins/action.pb.go index 22866ffb..2d127943 100644 --- a/components/kubebins/action.pb.go +++ b/components/kubebins/action.pb.go @@ -7,12 +7,11 @@ package kubebins import ( - reflect "reflect" - unsafe "unsafe" - api "github.com/Azure/AKSFlexNode/components/api" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + unsafe "unsafe" ) const ( @@ -219,15 +218,20 @@ func (b0 DownloadKubeBinariesSpec_builder) Build() *DownloadKubeBinariesSpec { } type DownloadKubeBinariesStatus struct { - state protoimpl.MessageState `protogen:"opaque.v1"` - xxx_hidden_DownloadUrl *string `protobuf:"bytes,1,opt,name=download_url,json=downloadUrl"` - xxx_hidden_KubeletPath *string `protobuf:"bytes,2,opt,name=kubelet_path,json=kubeletPath"` - xxx_hidden_KubeadmPath *string `protobuf:"bytes,3,opt,name=kubeadm_path,json=kubeadmPath"` - xxx_hidden_KubectlPath *string `protobuf:"bytes,4,opt,name=kubectl_path,json=kubectlPath"` - XXX_raceDetectHookData protoimpl.RaceDetectHookData - XXX_presence [1]uint32 - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_DownloadUrl *string `protobuf:"bytes,1,opt,name=download_url,json=downloadUrl"` + xxx_hidden_KubeletPath *string `protobuf:"bytes,2,opt,name=kubelet_path,json=kubeletPath"` + xxx_hidden_KubeadmPath *string `protobuf:"bytes,3,opt,name=kubeadm_path,json=kubeadmPath"` + xxx_hidden_KubectlPath *string `protobuf:"bytes,4,opt,name=kubectl_path,json=kubectlPath"` + xxx_hidden_KubeProxyPath *string `protobuf:"bytes,5,opt,name=kube_proxy_path,json=kubeProxyPath"` + xxx_hidden_KubeletDownloadUrl *string `protobuf:"bytes,6,opt,name=kubelet_download_url,json=kubeletDownloadUrl"` + xxx_hidden_KubeadmDownloadUrl *string `protobuf:"bytes,7,opt,name=kubeadm_download_url,json=kubeadmDownloadUrl"` + xxx_hidden_KubectlDownloadUrl *string `protobuf:"bytes,8,opt,name=kubectl_download_url,json=kubectlDownloadUrl"` + xxx_hidden_KubeProxyDownloadUrl *string `protobuf:"bytes,9,opt,name=kube_proxy_download_url,json=kubeProxyDownloadUrl"` + XXX_raceDetectHookData protoimpl.RaceDetectHookData + XXX_presence [1]uint32 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *DownloadKubeBinariesStatus) Reset() { @@ -255,6 +259,7 @@ func (x *DownloadKubeBinariesStatus) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } +// Deprecated: Marked as deprecated in components/kubebins/action.proto. func (x *DownloadKubeBinariesStatus) GetDownloadUrl() string { if x != nil { if x.xxx_hidden_DownloadUrl != nil { @@ -295,26 +300,103 @@ func (x *DownloadKubeBinariesStatus) GetKubectlPath() string { return "" } +func (x *DownloadKubeBinariesStatus) GetKubeProxyPath() string { + if x != nil { + if x.xxx_hidden_KubeProxyPath != nil { + return *x.xxx_hidden_KubeProxyPath + } + return "" + } + return "" +} + +func (x *DownloadKubeBinariesStatus) GetKubeletDownloadUrl() string { + if x != nil { + if x.xxx_hidden_KubeletDownloadUrl != nil { + return *x.xxx_hidden_KubeletDownloadUrl + } + return "" + } + return "" +} + +func (x *DownloadKubeBinariesStatus) GetKubeadmDownloadUrl() string { + if x != nil { + if x.xxx_hidden_KubeadmDownloadUrl != nil { + return *x.xxx_hidden_KubeadmDownloadUrl + } + return "" + } + return "" +} + +func (x *DownloadKubeBinariesStatus) GetKubectlDownloadUrl() string { + if x != nil { + if x.xxx_hidden_KubectlDownloadUrl != nil { + return *x.xxx_hidden_KubectlDownloadUrl + } + return "" + } + return "" +} + +func (x *DownloadKubeBinariesStatus) GetKubeProxyDownloadUrl() string { + if x != nil { + if x.xxx_hidden_KubeProxyDownloadUrl != nil { + return *x.xxx_hidden_KubeProxyDownloadUrl + } + return "" + } + return "" +} + +// Deprecated: Marked as deprecated in components/kubebins/action.proto. func (x *DownloadKubeBinariesStatus) SetDownloadUrl(v string) { x.xxx_hidden_DownloadUrl = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 4) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 9) } func (x *DownloadKubeBinariesStatus) SetKubeletPath(v string) { x.xxx_hidden_KubeletPath = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 1, 4) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 1, 9) } func (x *DownloadKubeBinariesStatus) SetKubeadmPath(v string) { x.xxx_hidden_KubeadmPath = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 2, 4) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 2, 9) } func (x *DownloadKubeBinariesStatus) SetKubectlPath(v string) { x.xxx_hidden_KubectlPath = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 3, 4) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 3, 9) +} + +func (x *DownloadKubeBinariesStatus) SetKubeProxyPath(v string) { + x.xxx_hidden_KubeProxyPath = &v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 4, 9) +} + +func (x *DownloadKubeBinariesStatus) SetKubeletDownloadUrl(v string) { + x.xxx_hidden_KubeletDownloadUrl = &v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 5, 9) +} + +func (x *DownloadKubeBinariesStatus) SetKubeadmDownloadUrl(v string) { + x.xxx_hidden_KubeadmDownloadUrl = &v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 6, 9) +} + +func (x *DownloadKubeBinariesStatus) SetKubectlDownloadUrl(v string) { + x.xxx_hidden_KubectlDownloadUrl = &v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 7, 9) } +func (x *DownloadKubeBinariesStatus) SetKubeProxyDownloadUrl(v string) { + x.xxx_hidden_KubeProxyDownloadUrl = &v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 8, 9) +} + +// Deprecated: Marked as deprecated in components/kubebins/action.proto. func (x *DownloadKubeBinariesStatus) HasDownloadUrl() bool { if x == nil { return false @@ -343,6 +425,42 @@ func (x *DownloadKubeBinariesStatus) HasKubectlPath() bool { return protoimpl.X.Present(&(x.XXX_presence[0]), 3) } +func (x *DownloadKubeBinariesStatus) HasKubeProxyPath() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 4) +} + +func (x *DownloadKubeBinariesStatus) HasKubeletDownloadUrl() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 5) +} + +func (x *DownloadKubeBinariesStatus) HasKubeadmDownloadUrl() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 6) +} + +func (x *DownloadKubeBinariesStatus) HasKubectlDownloadUrl() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 7) +} + +func (x *DownloadKubeBinariesStatus) HasKubeProxyDownloadUrl() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 8) +} + +// Deprecated: Marked as deprecated in components/kubebins/action.proto. func (x *DownloadKubeBinariesStatus) ClearDownloadUrl() { protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0) x.xxx_hidden_DownloadUrl = nil @@ -363,13 +481,44 @@ func (x *DownloadKubeBinariesStatus) ClearKubectlPath() { x.xxx_hidden_KubectlPath = nil } +func (x *DownloadKubeBinariesStatus) ClearKubeProxyPath() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 4) + x.xxx_hidden_KubeProxyPath = nil +} + +func (x *DownloadKubeBinariesStatus) ClearKubeletDownloadUrl() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 5) + x.xxx_hidden_KubeletDownloadUrl = nil +} + +func (x *DownloadKubeBinariesStatus) ClearKubeadmDownloadUrl() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 6) + x.xxx_hidden_KubeadmDownloadUrl = nil +} + +func (x *DownloadKubeBinariesStatus) ClearKubectlDownloadUrl() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 7) + x.xxx_hidden_KubectlDownloadUrl = nil +} + +func (x *DownloadKubeBinariesStatus) ClearKubeProxyDownloadUrl() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 8) + x.xxx_hidden_KubeProxyDownloadUrl = nil +} + type DownloadKubeBinariesStatus_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. - DownloadUrl *string - KubeletPath *string - KubeadmPath *string - KubectlPath *string + // Deprecated: Marked as deprecated in components/kubebins/action.proto. + DownloadUrl *string + KubeletPath *string + KubeadmPath *string + KubectlPath *string + KubeProxyPath *string + KubeletDownloadUrl *string + KubeadmDownloadUrl *string + KubectlDownloadUrl *string + KubeProxyDownloadUrl *string } func (b0 DownloadKubeBinariesStatus_builder) Build() *DownloadKubeBinariesStatus { @@ -377,21 +526,41 @@ func (b0 DownloadKubeBinariesStatus_builder) Build() *DownloadKubeBinariesStatus b, x := &b0, m0 _, _ = b, x if b.DownloadUrl != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 4) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 9) x.xxx_hidden_DownloadUrl = b.DownloadUrl } if b.KubeletPath != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 1, 4) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 1, 9) x.xxx_hidden_KubeletPath = b.KubeletPath } if b.KubeadmPath != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 2, 4) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 2, 9) x.xxx_hidden_KubeadmPath = b.KubeadmPath } if b.KubectlPath != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 3, 4) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 3, 9) x.xxx_hidden_KubectlPath = b.KubectlPath } + if b.KubeProxyPath != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 4, 9) + x.xxx_hidden_KubeProxyPath = b.KubeProxyPath + } + if b.KubeletDownloadUrl != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 5, 9) + x.xxx_hidden_KubeletDownloadUrl = b.KubeletDownloadUrl + } + if b.KubeadmDownloadUrl != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 6, 9) + x.xxx_hidden_KubeadmDownloadUrl = b.KubeadmDownloadUrl + } + if b.KubectlDownloadUrl != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 7, 9) + x.xxx_hidden_KubectlDownloadUrl = b.KubectlDownloadUrl + } + if b.KubeProxyDownloadUrl != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 8, 9) + x.xxx_hidden_KubeProxyDownloadUrl = b.KubeProxyDownloadUrl + } return m0 } @@ -405,12 +574,17 @@ const file_components_kubebins_action_proto_rawDesc = "" + "\x04spec\x18\x02 \x01(\v26.aks.flex.components.kubebins.DownloadKubeBinariesSpecR\x04spec\x12P\n" + "\x06status\x18\x03 \x01(\v28.aks.flex.components.kubebins.DownloadKubeBinariesStatusR\x06status\"I\n" + "\x18DownloadKubeBinariesSpec\x12-\n" + - "\x12kubernetes_version\x18\x01 \x01(\tR\x11kubernetesVersion\"\xa8\x01\n" + - "\x1aDownloadKubeBinariesStatus\x12!\n" + - "\fdownload_url\x18\x01 \x01(\tR\vdownloadUrl\x12!\n" + + "\x12kubernetes_version\x18\x01 \x01(\tR\x11kubernetesVersion\"\xa1\x03\n" + + "\x1aDownloadKubeBinariesStatus\x12%\n" + + "\fdownload_url\x18\x01 \x01(\tB\x02\x18\x01R\vdownloadUrl\x12!\n" + "\fkubelet_path\x18\x02 \x01(\tR\vkubeletPath\x12!\n" + "\fkubeadm_path\x18\x03 \x01(\tR\vkubeadmPath\x12!\n" + - "\fkubectl_path\x18\x04 \x01(\tR\vkubectlPathB2Z0github.com/Azure/AKSFlexNode/components/kubebinsb\beditionsp\xe9\a" + "\fkubectl_path\x18\x04 \x01(\tR\vkubectlPath\x12&\n" + + "\x0fkube_proxy_path\x18\x05 \x01(\tR\rkubeProxyPath\x120\n" + + "\x14kubelet_download_url\x18\x06 \x01(\tR\x12kubeletDownloadUrl\x120\n" + + "\x14kubeadm_download_url\x18\a \x01(\tR\x12kubeadmDownloadUrl\x120\n" + + "\x14kubectl_download_url\x18\b \x01(\tR\x12kubectlDownloadUrl\x125\n" + + "\x17kube_proxy_download_url\x18\t \x01(\tR\x14kubeProxyDownloadUrlB2Z0github.com/Azure/AKSFlexNode/components/kubebinsb\beditionsp\xe9\a" var file_components_kubebins_action_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_components_kubebins_action_proto_goTypes = []any{ diff --git a/components/kubebins/action.proto b/components/kubebins/action.proto index ba10948b..d2b60bc8 100644 --- a/components/kubebins/action.proto +++ b/components/kubebins/action.proto @@ -19,8 +19,13 @@ message DownloadKubeBinariesSpec { } message DownloadKubeBinariesStatus { - string download_url = 1; + string download_url = 1 [deprecated = true]; string kubelet_path = 2; string kubeadm_path = 3; string kubectl_path = 4; + string kube_proxy_path = 5; + string kubelet_download_url = 6; + string kubeadm_download_url = 7; + string kubectl_download_url = 8; + string kube_proxy_download_url = 9; } \ No newline at end of file diff --git a/components/kubebins/v20260301/download.go b/components/kubebins/v20260301/download.go index 53f0c80c..2697b586 100644 --- a/components/kubebins/v20260301/download.go +++ b/components/kubebins/v20260301/download.go @@ -5,8 +5,10 @@ import ( "fmt" "path/filepath" "strings" + "time" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "golang.org/x/sync/errgroup" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/anypb" @@ -15,26 +17,28 @@ import ( "github.com/Azure/AKSFlexNode/components/kubebins" "github.com/Azure/AKSFlexNode/components/services/actions" "github.com/Azure/AKSFlexNode/pkg/config" + "github.com/Azure/AKSFlexNode/pkg/logger" "github.com/Azure/AKSFlexNode/pkg/utils/utilhost" "github.com/Azure/AKSFlexNode/pkg/utils/utilio" "github.com/Azure/AKSFlexNode/pkg/utils/utilpb" ) const ( - defaultKubernetesURLTemplate = "https://acs-mirror.azureedge.net/kubernetes/v%s/binaries/kubernetes-node-linux-%s.tar.gz" - kubernetesTarPath = "kubernetes/node/bin/" + // kubernetesBinaryURLTemplate is the download URL template for individual Kubernetes binaries + // from the official Kubernetes release CDN (https://dl.k8s.io). + // Parameters: version, arch, binary name. + kubernetesBinaryURLTemplate = "https://dl.k8s.io/v%s/bin/linux/%s/%s" ) -var ( - binPathKubelet = filepath.Join(config.DefaultBinaryPath, "kubelet") +// requiredBinaries lists the Kubernetes binaries that must be present for a valid installation. +var requiredBinaries = []string{ + "kubeadm", + "kubelet", + "kubectl", + "kube-proxy", +} - binariesRequired = []string{ - filepath.Join(config.DefaultBinaryPath, "kubeadm"), - filepath.Join(config.DefaultBinaryPath, "kubelet"), - filepath.Join(config.DefaultBinaryPath, "kubectl"), - filepath.Join(config.DefaultBinaryPath, "kube-proxy"), - } -) +var binPathKubelet = filepath.Join(config.DefaultBinaryPath, "kubelet") type downloadKubeBinariesAction struct{} @@ -55,18 +59,23 @@ func (d *downloadKubeBinariesAction) ApplyAction( spec := settings.GetSpec() - downloadURL := d.constructDownloadURL(spec.GetKubernetesVersion()) + version := spec.GetKubernetesVersion() + arch := utilhost.GetArch() st := kubebins.DownloadKubeBinariesStatus_builder{ - DownloadUrl: to.Ptr(downloadURL), - KubeletPath: to.Ptr(binPathKubelet), - KubeadmPath: to.Ptr(filepath.Join(config.DefaultBinaryPath, "kubeadm")), - KubectlPath: to.Ptr(filepath.Join(config.DefaultBinaryPath, "kubectl")), + KubeletPath: to.Ptr(binPathKubelet), + KubeadmPath: to.Ptr(filepath.Join(config.DefaultBinaryPath, "kubeadm")), + KubectlPath: to.Ptr(filepath.Join(config.DefaultBinaryPath, "kubectl")), + KubeProxyPath: to.Ptr(filepath.Join(config.DefaultBinaryPath, "kube-proxy")), + KubeletDownloadUrl: to.Ptr(fmt.Sprintf(kubernetesBinaryURLTemplate, version, arch, "kubelet")), + KubeadmDownloadUrl: to.Ptr(fmt.Sprintf(kubernetesBinaryURLTemplate, version, arch, "kubeadm")), + KubectlDownloadUrl: to.Ptr(fmt.Sprintf(kubernetesBinaryURLTemplate, version, arch, "kubectl")), + KubeProxyDownloadUrl: to.Ptr(fmt.Sprintf(kubernetesBinaryURLTemplate, version, arch, "kube-proxy")), } - needDownload := !hasRequiredBinaries() || !kubeletVersionMatch(ctx, spec.GetKubernetesVersion()) + needDownload := !hasRequiredBinaries() || !kubeletVersionMatch(ctx, version) if needDownload { - if err := d.download(ctx, downloadURL); err != nil { + if err := d.download(ctx, version); err != nil { return nil, err } } @@ -81,36 +90,39 @@ func (d *downloadKubeBinariesAction) ApplyAction( return actions.ApplyActionResponse_builder{Item: item}.Build(), nil } -func (d *downloadKubeBinariesAction) download(ctx context.Context, downloadURL string) error { - for tarFile, err := range utilio.DecompressTarGzFromRemote(ctx, downloadURL) { - if err != nil { - return status.Errorf(codes.Internal, "decompress tar: %s", err) - } +// download fetches all required Kubernetes binaries in parallel from dl.k8s.io. +func (d *downloadKubeBinariesAction) download(ctx context.Context, kubernetesVersion string) error { + arch := utilhost.GetArch() + log := logger.GetLoggerFromContext(ctx) - if !strings.HasPrefix(tarFile.Name, kubernetesTarPath) { - continue - } + eg, ctx := errgroup.WithContext(ctx) - binaryName := strings.TrimPrefix(tarFile.Name, kubernetesTarPath) + for _, binary := range requiredBinaries { + binaryURL := fmt.Sprintf(kubernetesBinaryURLTemplate, kubernetesVersion, arch, binary) + targetFilePath := filepath.Join(config.DefaultBinaryPath, binary) - targetFilePath := filepath.Join(config.DefaultBinaryPath, binaryName) + eg.Go(func() error { + log.WithField("binary", binary).WithField("url", binaryURL).Info("downloading kubernetes binary") - if err := utilio.InstallFile(targetFilePath, tarFile.Body, 0755); err != nil { - return status.Errorf(codes.Internal, "install file %q: %s", targetFilePath, err) - } - } + start := time.Now() - return nil -} + if err := utilio.DownloadToLocalFile(ctx, binaryURL, targetFilePath, 0755); err != nil { + return status.Errorf(codes.Internal, "download kubernetes binary %q: %s", binary, err) + } -// constructDownloadURL constructs the download URL for the specified Kubernetes version. -func (d *downloadKubeBinariesAction) constructDownloadURL(kubernetesVersion string) string { - arch := utilhost.GetArch() - return fmt.Sprintf(defaultKubernetesURLTemplate, kubernetesVersion, arch) + log.WithField("binary", binary).WithField("duration", time.Since(start)).Info("downloaded kubernetes binary") + + return nil + }) + } + + return eg.Wait() } +// hasRequiredBinaries checks if all required Kubernetes binaries are installed and executable. func hasRequiredBinaries() bool { - for _, binaryPath := range binariesRequired { + for _, binary := range requiredBinaries { + binaryPath := filepath.Join(config.DefaultBinaryPath, binary) if !utilio.IsExecutable(binaryPath) { return false } @@ -118,6 +130,7 @@ func hasRequiredBinaries() bool { return true } +// kubeletVersionMatch checks if the installed kubelet version matches the expected version. func kubeletVersionMatch(ctx context.Context, version string) bool { output, err := utilexec.New(). CommandContext(ctx, binPathKubelet, "--version"). diff --git a/go.mod b/go.mod index 139fd849..476f4bf2 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.10.1 github.com/spf13/viper v1.21.0 + golang.org/x/sync v0.19.0 google.golang.org/grpc v1.72.3 google.golang.org/protobuf v1.36.8 k8s.io/api v0.35.0 @@ -102,7 +103,6 @@ require ( golang.org/x/crypto v0.47.0 // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/oauth2 v0.33.0 // indirect - golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/term v0.39.0 // indirect golang.org/x/text v0.33.0 // indirect