diff --git a/e2e/node_config.go b/e2e/node_config.go index a1c038d2c4f..33c1a8c82c8 100644 --- a/e2e/node_config.go +++ b/e2e/node_config.go @@ -10,6 +10,7 @@ import ( "github.com/Masterminds/semver" "github.com/Azure/agentbaker/e2e/config" + "github.com/Azure/agentbaker/e2e/toolkit" "github.com/Azure/agentbaker/pkg/agent" "github.com/Azure/agentbaker/pkg/agent/datamodel" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" @@ -306,6 +307,14 @@ func nbcToAKSNodeConfigV1(nbc *datamodel.NodeBootstrappingConfiguration) *aksnod // this is what we previously used for bash e2e from e2e/nodebootstrapping_template.json. // which itself was extracted from baker_test.go logic, which was inherited from aks-engine. func baseTemplateLinux(t testing.TB, location string, k8sVersion string, arch string) *datamodel.NodeBootstrappingConfiguration { + customKubeProxyImage := fmt.Sprintf("mcr.microsoft.com/oss/kubernetes/kube-proxy:v%s", k8sVersion) + customKubeBinaryURL := fmt.Sprintf("https://packages.aks.azure.com/kubernetes/v%s/binaries/kubernetes-node-linux-%s.tar.gz", k8sVersion, arch) + is134OrAbove, pErr := toolkit.CheckK8sConstraint(k8sVersion, ">=1.34.0") + require.NoError(t, pErr, "failed to parse Kubernetes version") + if is134OrAbove { + customKubeProxyImage = "" + customKubeBinaryURL = "" + } config := &datamodel.NodeBootstrappingConfiguration{ ContainerService: &datamodel.ContainerService{ ID: "", @@ -336,8 +345,8 @@ func baseTemplateLinux(t testing.TB, location string, k8sVersion string, arch st UserAssignedID: "", UserAssignedClientID: "", CustomHyperkubeImage: "", - CustomKubeProxyImage: fmt.Sprintf("mcr.microsoft.com/oss/kubernetes/kube-proxy:v%s", k8sVersion), - CustomKubeBinaryURL: fmt.Sprintf("https://packages.aks.azure.com/kubernetes/v%s/binaries/kubernetes-node-linux-%s.tar.gz", k8sVersion, arch), + CustomKubeProxyImage: customKubeProxyImage, + CustomKubeBinaryURL: customKubeBinaryURL, MobyVersion: "", ContainerdVersion: "", WindowsNodeBinariesURL: "", diff --git a/e2e/toolkit/k8s.go b/e2e/toolkit/k8s.go new file mode 100644 index 00000000000..86008cbbffd --- /dev/null +++ b/e2e/toolkit/k8s.go @@ -0,0 +1,17 @@ +package toolkit + +import ( + "github.com/Masterminds/semver" +) + +func CheckK8sConstraint(kubernetesVersion string, constraintStr string) (bool, error) { + version, err := semver.NewVersion(kubernetesVersion) + if err != nil { + return false, err + } + constraint, err := semver.NewConstraint(constraintStr) + if err != nil { + return false, err + } + return constraint.Check(version), nil +} diff --git a/parts/linux/cloud-init/artifacts/azlosguard/cse_install_osguard.sh b/parts/linux/cloud-init/artifacts/azlosguard/cse_install_osguard.sh index 239b7936862..bb0c35e29c5 100644 --- a/parts/linux/cloud-init/artifacts/azlosguard/cse_install_osguard.sh +++ b/parts/linux/cloud-init/artifacts/azlosguard/cse_install_osguard.sh @@ -6,14 +6,18 @@ stub() { installKubeletKubectlFromPkg() { local desiredVersion="${1}" - installRPMPackageFromFile "kubelet" $desiredVersion || exit $ERR_KUBELET_INSTALL_FAIL - installRPMPackageFromFile "kubectl" $desiredVersion || exit $ERR_KUBECTL_INSTALL_FAIL + + installRPMPackageFromFile "kubelet" "${desiredVersion}" "/opt/bin/kubelet" || exit "$ERR_KUBELET_INSTALL_FAIL" + installRPMPackageFromFile "kubectl" "${desiredVersion}" "/opt/bin/kubectl" || exit "$ERR_KUBECTL_INSTALL_FAIL" } installRPMPackageFromFile() { local packageName="${1}" local desiredVersion="${2}" - local targetBinDir="${3:-"/opt/bin"}" + local targetPath="${3:-/opt/bin/${packageName}}" + local downloadDir="/opt/${packageName}/downloads" + local rpmFile="" + local fullPackageVersion="" echo "installing ${packageName} version ${desiredVersion} by manually unpacking the RPM" if [ "${packageName}" != "kubelet" ] && [ "${packageName}" != "kubectl" ] && [ "${packageName}" != "azure-acr-credential-provider" ]; then @@ -21,12 +25,11 @@ installRPMPackageFromFile() { exit 1 fi echo "installing ${packageName} version ${desiredVersion}" - downloadDir="/opt/${packageName}/downloads" rpmFile=$(ls "${downloadDir}" | grep "${packageName}" | grep "${desiredVersion}" | sort -V | tail -n 1) || rpmFile="" if [ -z "${rpmFile}" ] && { [ "${packageName}" = "kubelet" ] || [ "${packageName}" = "kubectl" ]; } && fallbackToKubeBinaryInstall "${packageName}" "${desiredVersion}"; then echo "Successfully installed ${packageName} version ${desiredVersion} from binary fallback" - rm -rf ${downloadDir} + rm -rf "${downloadDir}" return 0 fi if [ -z "${rpmFile}" ]; then @@ -37,7 +40,7 @@ installRPMPackageFromFile() { return 1 fi echo "Did not find cached rpm file, downloading ${packageName} version ${fullPackageVersion}" - downloadPkgFromVersion "${packageName}" ${fullPackageVersion} "${downloadDir}" + downloadPkgFromVersion "${packageName}" "${fullPackageVersion}" "${downloadDir}" rpmFile=$(ls "${downloadDir}" | grep "${packageName}" | grep "${desiredVersion}" | sort -V | tail -n 1) || rpmFile="" fi if [ -z "${rpmFile}" ]; then @@ -46,17 +49,11 @@ installRPMPackageFromFile() { fi rpmFile="${downloadDir}/${rpmFile}" - local rpmBinaryName="${packageName}" - local targetBinaryName="${packageName}" - if [ "${packageName}" = "azure-acr-credential-provider" ]; then - targetBinaryName="acr-credential-provider" - fi - - echo "Unpacking usr/bin/${rpmBinaryName} from ${downloadDir}/${packageName}-${desiredVersion}*" - mkdir -p "${targetBinDir}" + echo "Unpacking usr/bin/${packageName} from ${downloadDir}/${packageName}-${desiredVersion}*" + mkdir -p "$(dirname "${targetPath}")" # This assumes that the binary will either be in /usr/bin or /usr/local/bin, but not both. - rpm2cpio "${rpmFile}" | cpio -i --to-stdout "./usr/bin/${rpmBinaryName}" "./usr/local/bin/${rpmBinaryName}" | install -m0755 /dev/stdin "${targetBinDir}/${targetBinaryName}" - rm -rf ${downloadDir} + rpm2cpio "${rpmFile}" | cpio -i --to-stdout "./usr/bin/${packageName}" "./usr/local/bin/${packageName}" | install -m0755 /dev/stdin "${targetPath}" + rm -rf "${downloadDir}" } downloadPkgFromVersion() { @@ -83,7 +80,7 @@ installCredentialProviderFromPkg() { echo "installing azure-acr-credential-provider package version: $packageVersion" mkdir -p "${CREDENTIAL_PROVIDER_BIN_DIR}" chown -R root:root "${CREDENTIAL_PROVIDER_BIN_DIR}" - installRPMPackageFromFile "azure-acr-credential-provider" "${packageVersion}" "${CREDENTIAL_PROVIDER_BIN_DIR}" || exit $ERR_CREDENTIAL_PROVIDER_DOWNLOAD_TIMEOUT + installRPMPackageFromFile "azure-acr-credential-provider" "${packageVersion}" "${CREDENTIAL_PROVIDER_BIN_DIR}/acr-credential-provider" || exit "$ERR_CREDENTIAL_PROVIDER_DOWNLOAD_TIMEOUT" } installDeps() { diff --git a/parts/linux/cloud-init/artifacts/cse_helpers.sh b/parts/linux/cloud-init/artifacts/cse_helpers.sh index c0160032a28..c49ce0da3a2 100755 --- a/parts/linux/cloud-init/artifacts/cse_helpers.sh +++ b/parts/linux/cloud-init/artifacts/cse_helpers.sh @@ -1074,14 +1074,16 @@ getLatestPkgVersionFromK8sVersion() { fallbackToKubeBinaryInstall() { packageName="${1:-}" packageVersion="${2:-}" + local targetPath="${3:-/opt/bin/${packageName}}" if [ "${packageName}" = "kubelet" ] || [ "${packageName}" = "kubectl" ]; then if [ "${SHOULD_ENFORCE_KUBE_PMC_INSTALL}" = "true" ]; then echo "Kube PMC install is enforced, skipping fallback to kube binary install for ${packageName}" return 1 elif [ -f "/opt/bin/${packageName}-${packageVersion}" ]; then - mv "/opt/bin/${packageName}-${packageVersion}" "/opt/bin/${packageName}" - chmod a+x /opt/bin/${packageName} - rm -rf /opt/bin/${packageName}-* & + mv "/opt/bin/${packageName}-${packageVersion}" "${targetPath}" + chown root:root "${targetPath}" + chmod 0755 "${targetPath}" + rm -rf "/opt/bin/${packageName}-*" & return 0 else echo "No binary fallback found for ${packageName} version ${packageVersion}" diff --git a/parts/linux/cloud-init/artifacts/mariner/cse_install_mariner.sh b/parts/linux/cloud-init/artifacts/mariner/cse_install_mariner.sh index b3fa3d81cda..011c7768d66 100755 --- a/parts/linux/cloud-init/artifacts/mariner/cse_install_mariner.sh +++ b/parts/linux/cloud-init/artifacts/mariner/cse_install_mariner.sh @@ -213,8 +213,7 @@ installCredentialProviderFromPkg() { echo "installing azure-acr-credential-provider package version: $packageVersion" mkdir -p "${CREDENTIAL_PROVIDER_BIN_DIR}" chown -R root:root "${CREDENTIAL_PROVIDER_BIN_DIR}" - installRPMPackageFromFile "azure-acr-credential-provider" "${packageVersion}" || exit $ERR_CREDENTIAL_PROVIDER_DOWNLOAD_TIMEOUT - ln -snf /usr/bin/azure-acr-credential-provider "$CREDENTIAL_PROVIDER_BIN_DIR/acr-credential-provider" + installRPMPackageFromFile "azure-acr-credential-provider" "${packageVersion}" "${CREDENTIAL_PROVIDER_BIN_DIR}/acr-credential-provider" || exit "$ERR_CREDENTIAL_PROVIDER_DOWNLOAD_TIMEOUT" } getPackageCacheRoot() { @@ -233,8 +232,9 @@ installCredentialProviderFromPkg() { installKubeletKubectlFromPkg() { local desiredVersion="${1}" - installRPMPackageFromFile "kubelet" $desiredVersion || exit $ERR_KUBELET_INSTALL_FAIL - installRPMPackageFromFile "kubectl" $desiredVersion || exit $ERR_KUBECTL_INSTALL_FAIL + + installRPMPackageFromFile "kubelet" "${desiredVersion}" "/opt/bin/kubelet" || exit "$ERR_KUBELET_INSTALL_FAIL" + installRPMPackageFromFile "kubectl" "${desiredVersion}" "/opt/bin/kubectl" || exit "$ERR_KUBECTL_INSTALL_FAIL" } installToolFromLocalRepo() { @@ -403,22 +403,60 @@ installNvidiaManagedExpPkgFromCache() { done } +extractBinaryFromRPM() { + local rpmFile="${1}" + local packageName="${2}" + local targetPath="${3:-/opt/bin/${packageName}}" + local extractDir + local binaryPath="" + + extractDir=$(mktemp -d) || return 1 + if ! (cd "${extractDir}" && rpm2cpio "${rpmFile}" | cpio -idm >/dev/null 2>&1); then + rm -rf "${extractDir}" + return 1 + fi + + for candidate in "${extractDir}/usr/bin/${packageName}" "${extractDir}/usr/local/bin/${packageName}"; do + if [ -f "${candidate}" ]; then + binaryPath="${candidate}" + break + fi + done + + if [ -z "${binaryPath}" ]; then + echo "Failed to locate ${packageName} binary in ${rpmFile}" + rm -rf "${extractDir}" + return 1 + fi + + mkdir -p "$(dirname "${targetPath}")" + + mv "${binaryPath}" "${targetPath}" + chown root:root "${targetPath}" + chmod 0755 "${targetPath}" + + rm -rf "${extractDir}" +} + installRPMPackageFromFile() { local packageName="${1}" local desiredVersion="${2}" + local targetPath="${3:-/opt/bin/${packageName}}" echo "installing ${packageName} version ${desiredVersion}" local downloadDir + local rpmFile="" + local fullPackageVersion="" downloadDir="$(getPackageDownloadDir "${packageName}")" + if fallbackToKubeBinaryInstall "${packageName}" "${desiredVersion}" "${targetPath}"; then + echo "Successfully installed ${packageName} version ${desiredVersion} from binary fallback" + rm -rf "${downloadDir}" + return 0 + fi + # check cached rpms for matching filename rpmFile=$(ls "${downloadDir}" | grep "${packageName}" | grep "${desiredVersion}" | sort -V | tail -n 1) || rpmFile="" if [ -z "${rpmFile}" ]; then - if fallbackToKubeBinaryInstall "${packageName}" "${desiredVersion}"; then - echo "Successfully installed ${packageName} version ${desiredVersion} from binary fallback" - rm -rf "${downloadDir}" - return 0 - fi - # query all package versions and get the latest version for matching k8s version # e.g. 1.34.0-5.azl3 fullPackageVersion=$(dnf list ${packageName} --showduplicates | grep ${desiredVersion}- | awk '{print $2}' | sort -V | tail -n 1) @@ -427,7 +465,7 @@ installRPMPackageFromFile() { return 1 fi echo "Did not find cached rpm file, downloading ${packageName} version ${fullPackageVersion}" - downloadPkgFromVersion "${packageName}" ${fullPackageVersion} "${downloadDir}" + downloadPkgFromVersion "${packageName}" "${fullPackageVersion}" "${downloadDir}" rpmFile=$(ls "${downloadDir}" | grep "${packageName}" | grep "${desiredVersion}" | sort -V | tail -n 1) || rpmFile="" fi if [ -z "${rpmFile}" ]; then @@ -436,41 +474,7 @@ installRPMPackageFromFile() { fi rpmFile="${downloadDir}/${rpmFile}" - local rpmArgs=("${rpmFile}") - local -a cachedRpmFiles=() - mapfile -t cachedRpmFiles < <(find "${downloadDir}" -maxdepth 1 -type f -name "*.rpm" -print 2>/dev/null | sort) - - # selecting the correct version of dependency rpms from the cache - for cachedRpm in "${cachedRpmFiles[@]}"; do - if [ "${cachedRpm}" = "${rpmFile}" ]; then - continue - fi - - local cachedBaseName - cachedBaseName=$(basename "${cachedRpm}") - - case "${cachedBaseName}" in - *${packageName}*) - echo "Skipping cached ${packageName} rpm ${cachedBaseName} because it does not match desired version ${desiredVersion}" - continue - ;; - esac - - rpmArgs+=("${cachedRpm}") - done - - if [ ${#rpmArgs[@]} -gt 1 ]; then - echo "Installing ${packageName} with cached dependency RPMs: ${rpmArgs[*]}" - fi - - # When dependency RPMs are cached, they are included in the argument list to dnf_install. - # When no dependency RPM is cached, only the main package RPM is included. - # And dnf_install will handle installing dependencies from configured repos (downloading from network) as needed. - if ! dnf_install 30 1 600 "${rpmArgs[@]}"; then - exit $ERR_APT_INSTALL_TIMEOUT - fi - mkdir -p /opt/bin - ln -snf "/usr/bin/${packageName}" "/opt/bin/${packageName}" + logs_to_events "AKS.CSE.install${packageName}.extractBinaryFromRPM" "extractBinaryFromRPM ${rpmFile} ${packageName} ${targetPath}" || exit "$ERR_APT_INSTALL_TIMEOUT" rm -rf "${downloadDir}" } diff --git a/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh b/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh index 2ae170da6a2..c13d21c4c64 100755 --- a/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh +++ b/parts/linux/cloud-init/artifacts/ubuntu/cse_install_ubuntu.sh @@ -217,14 +217,14 @@ installCredentialProviderFromPkg() { echo "installing azure-acr-credential-provider package version: $packageVersion" mkdir -p "${CREDENTIAL_PROVIDER_BIN_DIR}" chown -R root:root "${CREDENTIAL_PROVIDER_BIN_DIR}" - installPkgWithAptGet "azure-acr-credential-provider" "${packageVersion}" || exit $ERR_CREDENTIAL_PROVIDER_DOWNLOAD_TIMEOUT - ln -snf /usr/bin/azure-acr-credential-provider "$CREDENTIAL_PROVIDER_BIN_DIR/acr-credential-provider" + installPkgWithAptGet "azure-acr-credential-provider" "${packageVersion}" "${CREDENTIAL_PROVIDER_BIN_DIR}/acr-credential-provider" || exit "$ERR_CREDENTIAL_PROVIDER_DOWNLOAD_TIMEOUT" } installKubeletKubectlFromPkg() { - k8sVersion="${1}" - installPkgWithAptGet "kubelet" "${k8sVersion}" || exit $ERR_KUBELET_INSTALL_FAIL - installPkgWithAptGet "kubectl" "${k8sVersion}" || exit $ERR_KUBECTL_INSTALL_FAIL + local k8sVersion="${1}" + + installPkgWithAptGet "kubelet" "${k8sVersion}" "/opt/bin/kubelet" || exit "$ERR_KUBELET_INSTALL_FAIL" + installPkgWithAptGet "kubectl" "${k8sVersion}" "/opt/bin/kubectl" || exit "$ERR_KUBECTL_INSTALL_FAIL" } installToolFromLocalRepo() { @@ -289,23 +289,55 @@ installCredentialProviderPackageFromBootstrapProfileRegistry() { fi } +extractDebBinaryFromFile() { + local debFile="${1}" + local packageName="${2}" + local targetPath="${3:-/opt/bin/${packageName}}" + local extractDir + + extractDir=$(mktemp -d) || return 1 + if ! dpkg-deb -x "${debFile}" "${extractDir}"; then + rm -rf "${extractDir}" + return 1 + fi + + local sourceBinary="${extractDir}/usr/bin/${packageName}" + if [ ! -f "${sourceBinary}" ]; then + echo "Failed to locate usr/bin/${packageName} in ${debFile}" + rm -rf "${extractDir}" + return 1 + fi + + mkdir -p "$(dirname "${targetPath}")" + + mv "${sourceBinary}" "${targetPath}" + chown root:root "${targetPath}" + chmod 0755 "${targetPath}" + + rm -rf "${extractDir}" +} + installPkgWithAptGet() { - packageName="${1:-}" - packageVersion="${2}" - downloadDir="/opt/${packageName}/downloads" + local packageName="${1:-}" + local packageVersion="${2}" + local targetPath="${3:-/opt/bin/${packageName}}" + local downloadDir="/opt/${packageName}/downloads" + local debFile="" + local fullPackageVersion="" + + if fallbackToKubeBinaryInstall "${packageName}" "${packageVersion}" "${targetPath}"; then + echo "Successfully installed ${packageName} version ${packageVersion} from binary fallback" + rm -rf "${downloadDir}" + return 0 + fi debFile=$(ls "${downloadDir}" | grep "${packageName}" | grep "${packageVersion}" | sort -V | tail -n 1) || debFile="" if [ -z "${debFile}" ]; then - if fallbackToKubeBinaryInstall "${packageName}" "${packageVersion}"; then - echo "Successfully installed ${packageName} version ${packageVersion} from binary fallback" - rm -rf ${downloadDir} - return 0 - fi # update pmc repo to get latest versions - updatePMCRepository ${packageVersion} + updatePMCRepository "${packageVersion}" # query all package versions and get the latest version for matching k8s version - fullPackageVersion=$(apt list ${packageName} --all-versions | grep ${packageVersion} | awk '{print $2}' | sort -V | tail -n 1) + fullPackageVersion=$(apt list "${packageName}" --all-versions | grep "${packageVersion}" | awk '{print $2}' | sort -V | tail -n 1) if [ -z "${fullPackageVersion}" ]; then echo "Failed to find valid ${packageName} version for ${packageVersion}" return 1 @@ -321,11 +353,9 @@ installPkgWithAptGet() { fi debFile="${downloadDir}/${debFile}" - logs_to_events "AKS.CSE.install${packageName}.installDebPackageFromFile" "installDebPackageFromFile ${debFile}" || exit $ERR_APT_INSTALL_TIMEOUT + logs_to_events "AKS.CSE.install${packageName}.extractDebBinaryFromFile" "extractDebBinaryFromFile ${debFile} ${packageName} ${targetPath}" || exit "$ERR_APT_INSTALL_TIMEOUT" - mkdir -p /opt/bin - ln -snf "/usr/bin/${packageName}" "/opt/bin/${packageName}" - rm -rf ${downloadDir} + rm -rf "${downloadDir}" } downloadPkgFromVersion() { diff --git a/spec/parts/linux/cloud-init/artifacts/cse_install_mariner_spec.sh b/spec/parts/linux/cloud-init/artifacts/cse_install_mariner_spec.sh index 00b5bb4f4a2..deba7c6c1b1 100644 --- a/spec/parts/linux/cloud-init/artifacts/cse_install_mariner_spec.sh +++ b/spec/parts/linux/cloud-init/artifacts/cse_install_mariner_spec.sh @@ -16,6 +16,13 @@ Describe 'cse_install_mariner.sh' function systemctl() { return 0 } + function logs_to_events() { + echo "$2" + return 0 + } + function fallbackToKubeBinaryInstall() { + return 1 + } } BeforeAll 'setup' Include "./parts/linux/cloud-init/artifacts/cse_install.sh" @@ -45,14 +52,10 @@ Describe 'cse_install_mariner.sh' rm -rf "$rpm_cache_root" } - ln() { - echo "ln $@" - } - BeforeEach 'setup_rpm_cache' AfterEach 'cleanup_rpm_cache' - It 'installs cached dependency RPMs when they are present' + It 'extracts the requested RPM when cached dependency RPMs are present' desiredVersion="1.34.0-5.azl3" rpmDir="$RPM_PACKAGE_CACHE_BASE_DIR/kubelet/downloads" kubeletRpm="$rpmDir/kubelet-${desiredVersion}.x86_64.rpm" @@ -62,25 +65,19 @@ Describe 'cse_install_mariner.sh' touch "$dependencyRpm" touch "$conflictRpm" When call installRPMPackageFromFile kubelet "$desiredVersion" - The output should include "Skipping cached kubelet rpm $(basename "$conflictRpm") because it does not match desired version $desiredVersion" - The output should include "Installing kubelet with cached dependency RPMs" - The output should include "$dependencyRpm" - The output should include "$kubeletRpm" - The output should include "dnf install 30 1 600" - The output should include "ln -snf /usr/bin/kubelet /opt/bin/kubelet" + The output should include "extractBinaryFromRPM $kubeletRpm kubelet /opt/bin/kubelet" End - It 'installs only the requested RPM when no cached dependencies exist' + It 'extracts only the requested RPM when no cached dependencies exist' desiredVersion="1.34.0-5.azl3" rpmDir="$RPM_PACKAGE_CACHE_BASE_DIR/kubelet/downloads" kubeletRpm="$rpmDir/kubelet-${desiredVersion}.x86_64.rpm" touch "$kubeletRpm" When call installRPMPackageFromFile kubelet "$desiredVersion" - The output should include "dnf install 30 1 600 $kubeletRpm" - The output should include "ln -snf /usr/bin/kubelet /opt/bin/kubelet" + The output should include "extractBinaryFromRPM $kubeletRpm kubelet /opt/bin/kubelet" End - It 'does not pass duplicate release versions to dnf causing conflicts' + It 'selects the latest matching release when multiple cached RPMs exist' desiredVersion="1.34.3" rpmDir="$RPM_PACKAGE_CACHE_BASE_DIR/kubelet/downloads" release1="$rpmDir/kubelet-1.34.3-1.azl3.x86_64.rpm" @@ -88,10 +85,7 @@ Describe 'cse_install_mariner.sh' touch "$release1" touch "$release2" When call installRPMPackageFromFile kubelet "$desiredVersion" - # sort -V | tail -n 1 should pick the latest release as the primary RPM - The output should include "dnf install 30 1 600 $release2" - # the older release should be skipped, not added as a dependency - The output should include "Skipping cached kubelet rpm" + The output should include "extractBinaryFromRPM $release2 kubelet /opt/bin/kubelet" The output should not include "$release1" End diff --git a/vhdbuilder/packer/install-dependencies.sh b/vhdbuilder/packer/install-dependencies.sh index f2305e725b0..469a2468526 100644 --- a/vhdbuilder/packer/install-dependencies.sh +++ b/vhdbuilder/packer/install-dependencies.sh @@ -262,6 +262,62 @@ unpackTgzToCNIDownloadsDIR() { echo " - Ran tar -xzf on the CNI downloaded then rm -rf to clean up" } +cacheVersionedKubernetesPackageBinary() { + local package_name=${1} + local package_version=${2} + local download_dir=${3} + local version_no_epoch + local k8s_version + local binary_path="/opt/bin/${package_name}" + + version_no_epoch="${package_version#*:}" + k8s_version="${version_no_epoch%%-*}" + binary_path="${binary_path}-${k8s_version}" + + mkdir -p /opt/bin + + if isUbuntu "$OS"; then + local deb_file + local tmp_dir + + deb_file=$(find "${download_dir}" -maxdepth 1 -name "${package_name}_${version_no_epoch}*" -print -quit 2>/dev/null) || deb_file="" + if [ -z "${deb_file}" ]; then + echo "Failed to locate cached ${package_name} deb for ${package_version}" + return 1 + fi + + tmp_dir=$(mktemp -d) + if ! dpkg-deb -x "${deb_file}" "${tmp_dir}"; then + rm -rf "${tmp_dir}" + return 1 + fi + + if [ ! -f "${tmp_dir}/usr/bin/${package_name}" ]; then + echo "Failed to find /usr/bin/${package_name} in ${deb_file}" + rm -rf "${tmp_dir}" + return 1 + fi + + install -m0755 "${tmp_dir}/usr/bin/${package_name}" "${binary_path}" + rm -rf "${tmp_dir}" + elif isMarinerOrAzureLinux "$OS"; then + local rpm_file + + rpm_file=$(find "${download_dir}" -maxdepth 1 -name "${package_name}-${version_no_epoch}*" -print -quit 2>/dev/null) || rpm_file="" + if [ -z "${rpm_file}" ]; then + echo "Failed to locate cached ${package_name} rpm for ${package_version}" + return 1 + fi + + rpm2cpio "${rpm_file}" | cpio -i --to-stdout "./usr/bin/${package_name}" "./usr/local/bin/${package_name}" | install -m0755 /dev/stdin "${binary_path}" + else + echo "Skipping versioned binary extraction for unsupported OS ${OS}" + return 0 + fi + + echo " - cached ${package_name} binary at ${binary_path}" >> "${VHD_LOGS_FILEPATH}" +} + # this is for the old package not coming from Dalec, currently fixed at 1.6.2. # The binary is expected to be present during bootstrapping, no dynamic download logic exists for this one downloadCNIPlugins() { @@ -469,7 +525,7 @@ while IFS= read -r p; do echo " - kubernetes-binaries version ${version}" >> ${VHD_LOGS_FILEPATH} done ;; - azure-acr-credential-provider-pmc|kubelet|kubectl) + azure-acr-credential-provider-pmc) name=${name%-pmc} for version in ${PACKAGE_VERSIONS[@]}; do if isMarinerOrAzureLinux || isUbuntu; then @@ -481,6 +537,18 @@ while IFS= read -r p; do echo " - ${name} version ${version}" >> ${VHD_LOGS_FILEPATH} done ;; + kubelet|kubectl) + for version in ${PACKAGE_VERSIONS[@]}; do + if isMarinerOrAzureLinux || isUbuntu; then + downloadPkgFromVersion "${name}" "${version}" "${downloadDir}" + cacheVersionedKubernetesPackageBinary "${name}" "${version}" "${downloadDir}" || exit $ERR_K8S_INSTALL_ERR + elif isFlatcar || isACL "$OS" "$OS_VARIANT"; then + evaluatedURL=$(evalPackageDownloadURL ${PACKAGE_DOWNLOAD_URL}) + downloadSysextFromVersion "${name}" "${evaluatedURL}" "${downloadDir}" || exit $? + fi + echo " - ${name} version ${version}" >> ${VHD_LOGS_FILEPATH} + done + ;; "${K8S_DEVICE_PLUGIN_PKG}") for version in ${PACKAGE_VERSIONS[@]}; do if [ "${OS}" = "${UBUNTU_OS_NAME}" ] || isMarinerOrAzureLinux "$OS"; then diff --git a/vhdbuilder/packer/test/linux-vhd-content-test.sh b/vhdbuilder/packer/test/linux-vhd-content-test.sh index bedfd10f81f..774f8b48cb5 100644 --- a/vhdbuilder/packer/test/linux-vhd-content-test.sh +++ b/vhdbuilder/packer/test/linux-vhd-content-test.sh @@ -221,7 +221,7 @@ testPackagesInstalled() { case "${name}" in "kubernetes-binaries") # kubernetes-binaries, namely, kubelet and kubectl are installed in a different way so we test them separately - # Intentionally remove leading 'v' from each element in the array + # Intentionally remove leading 'v' from each element in the array. testKubeBinariesPresent "${PACKAGE_VERSIONS[@]#v}" continue ;; @@ -231,8 +231,6 @@ testPackagesInstalled() { continue ;; "azure-acr-credential-provider-pmc"|\ - "kubelet"|\ - "kubectl"|\ "nvidia-device-plugin"|\ "datacenter-gpu-manager-4-core"|\ "datacenter-gpu-manager-4-proprietary"|\ @@ -240,6 +238,16 @@ testPackagesInstalled() { testPkgDownloaded "${name%-pmc}" "${downloadLocation}" "${PACKAGE_VERSIONS[@]}" continue ;; + "kubelet"|\ + "kubectl") + testPkgDownloaded "${name}" "${downloadLocation}" "${PACKAGE_VERSIONS[@]}" + if [ "$OS" = "$UBUNTU_OS_NAME" ] || [ "$OS" = "$MARINER_OS_NAME" ]; then + testVersionedKubernetesPackageBinariesPresent "${name}" "${PACKAGE_VERSIONS[@]}" + else + echo "Skipping testVersionedKubernetesPackageBinariesPresent for ${OS}${OS_VARIANT:+ ${OS_VARIANT}}" + fi + continue + ;; "cni-plugins") testCNIPluginsInstalled "${downloadLocation}" "${PACKAGE_VERSIONS[@]}" continue @@ -939,6 +947,40 @@ testKubeBinariesPresent() { echo "$test:Finish" } +testVersionedKubernetesPackageBinariesPresent() { + local packageName=$1 + shift + local test="testVersioned${packageName}PackageBinaries" + local packageVersions=("$@") + local binaryDir=/opt/bin + local packageVersion k8sVersion binaryPath versionOutput + + echo "$test:Start" + for packageVersion in "${packageVersions[@]}"; do + packageVersion="${packageVersion#*:}" + k8sVersion="${packageVersion%%-*}" + binaryPath="${binaryDir}/${packageName}-${k8sVersion}" + + if [ ! -s "${binaryPath}" ]; then + err "$test" "Binary ${binaryPath} does not exist" + continue + fi + + chmod a+x "${binaryPath}" + if [ "${packageName}" = "kubectl" ]; then + versionOutput=$("${binaryPath}" version 2>/dev/null) + else + versionOutput=$("${binaryPath}" --version 2>/dev/null) + fi + + # shellcheck disable=SC3010 + if [[ ! ${versionOutput} =~ ${k8sVersion} ]]; then + err "$test" "The ${packageName} version is not correct: expected ${k8sVersion}, existing: ${versionOutput}" + fi + done + echo "$test:Finish" +} + testPkgDownloaded() { local test="testPkgDownloaded" echo "$test:Start"