@@ -2147,10 +2147,19 @@ def k8s_install_kubectl(cmd, client_version='latest', install_location=None, sou
21472147 """
21482148
21492149 if not source_url :
2150- source_url = "https://dl.k8s.io/release"
21512150 cloud_name = cmd .cli_ctx .cloud .name
21522151 if cloud_name .lower () == 'azurechinacloud' :
21532152 source_url = 'https://mirror.azure.cn/kubernetes/kubectl'
2153+ else :
2154+ # Only try Microsoft packages for Linux systems (packages are Linux-only)
2155+ system = platform .system ()
2156+ if system == 'Linux' :
2157+ try :
2158+ return _k8s_install_kubectl_from_microsoft_packages (cmd , client_version , install_location , arch )
2159+ except Exception as e :
2160+ logger .warning ("Failed to install from Microsoft packages, falling back to Google Storage: %s" , str (e ))
2161+ # For non-Linux systems or fallback, use Google Storage
2162+ source_url = "https://storage.googleapis.com/kubernetes-release/release"
21542163
21552164 if client_version == 'latest' :
21562165 latest_version_url = source_url + '/stable.txt'
@@ -2205,6 +2214,244 @@ def k8s_install_kubectl(cmd, client_version='latest', install_location=None, sou
22052214 install_dir , cli )
22062215
22072216
2217+ def _k8s_install_kubectl_from_microsoft_packages (cmd , client_version = 'latest' , install_location = None , arch = None ):
2218+ """
2219+ Install kubectl from Microsoft packages repository by downloading and extracting .deb package.
2220+ Note: This method is only supported on Linux systems as Microsoft packages contain Linux binaries.
2221+ """
2222+ system = platform .system ()
2223+ if system != 'Linux' :
2224+ raise CLIError (f"Microsoft packages method is only supported on Linux (current system: { system } ). Use '--source-url' to specify an alternative source." )
2225+
2226+ if arch is None :
2227+ arch = get_arch_for_cli_binary ()
2228+
2229+ # Map architecture to package architecture
2230+ if arch == 'amd64' :
2231+ pkg_arch = 'amd64'
2232+ elif arch == 'arm64' :
2233+ pkg_arch = 'arm64'
2234+ else :
2235+ raise CLIError (f"Unsupported architecture '{ arch } ' for Microsoft packages" )
2236+
2237+ # Get available kubectl packages from Microsoft
2238+ packages_url = f"https://packages.microsoft.com/ubuntu/22.04/prod/dists/jammy/main/binary-{ pkg_arch } /Packages.gz"
2239+
2240+ logger .warning ('Fetching kubectl package information from Microsoft packages repository...' )
2241+ try :
2242+ packages_data = _urlopen_read (packages_url )
2243+ import gzip
2244+ packages_text = gzip .decompress (packages_data ).decode ('utf-8' )
2245+ except Exception as e :
2246+ raise CLIError (f'Failed to fetch package information from Microsoft: { e } ' )
2247+
2248+ # Parse packages and find kubectl
2249+ kubectl_packages = []
2250+ current_package = {}
2251+
2252+ for line in packages_text .split ('\n ' ):
2253+ if line .startswith ('Package: ' ):
2254+ if current_package .get ('Package' ) == 'kubectl' :
2255+ kubectl_packages .append (current_package )
2256+ current_package = {'Package' : line .split (': ' , 1 )[1 ]}
2257+ elif line .startswith ('Version: ' ):
2258+ current_package ['Version' ] = line .split (': ' , 1 )[1 ]
2259+ elif line .startswith ('Filename: ' ):
2260+ current_package ['Filename' ] = line .split (': ' , 1 )[1 ]
2261+ elif line .startswith ('SHA256: ' ):
2262+ current_package ['SHA256' ] = line .split (': ' , 1 )[1 ]
2263+ elif line == '' :
2264+ if current_package .get ('Package' ) == 'kubectl' :
2265+ kubectl_packages .append (current_package )
2266+ current_package = {}
2267+
2268+ if not kubectl_packages :
2269+ raise CLIError ('No kubectl packages found in Microsoft repository' )
2270+
2271+ # Select package version
2272+ if client_version == 'latest' :
2273+ # Sort by version and get latest (simple string sort should work for semver)
2274+ kubectl_packages .sort (key = lambda x : x .get ('Version' , '' ), reverse = True )
2275+ selected_package = kubectl_packages [0 ]
2276+ logger .warning ('Using latest kubectl version from Microsoft packages: %s' , selected_package ['Version' ])
2277+ else :
2278+ # Find specific version
2279+ version_to_find = client_version .lstrip ('v' ) # Remove 'v' prefix if present
2280+ selected_package = None
2281+ for pkg in kubectl_packages :
2282+ if pkg .get ('Version' , '' ).startswith (version_to_find ):
2283+ selected_package = pkg
2284+ break
2285+ if not selected_package :
2286+ raise CLIError (f'kubectl version { client_version } not found in Microsoft packages' )
2287+
2288+ # Download the .deb package
2289+ base_url = "https://packages.microsoft.com/ubuntu/22.04/prod/"
2290+ package_url = base_url + selected_package ['Filename' ]
2291+
2292+ logger .warning ('Downloading kubectl package from Microsoft: %s' , package_url )
2293+
2294+ # Create temporary directory for package extraction
2295+ with tempfile .TemporaryDirectory () as temp_dir :
2296+ deb_path = os .path .join (temp_dir , 'kubectl.deb' )
2297+
2298+ try :
2299+ _urlretrieve (package_url , deb_path )
2300+ except Exception as e :
2301+ raise CLIError (f'Failed to download kubectl package: { e } ' )
2302+
2303+ # Extract .deb package using ar and tar
2304+ try :
2305+ _extract_kubectl_from_deb (deb_path , temp_dir , install_location , system )
2306+ except Exception as e :
2307+ raise CLIError (f'Failed to extract kubectl from package: { e } ' )
2308+
2309+ # Handle post-installation
2310+ install_dir , cli = os .path .dirname (install_location ), os .path .basename (install_location )
2311+ if system == 'Windows' :
2312+ handle_windows_post_install (install_dir , cli )
2313+ else :
2314+ logger .warning ('Please ensure that %s is in your search PATH, so the `%s` command can be found.' ,
2315+ install_dir , cli )
2316+
2317+ logger .warning ('Successfully installed kubectl from Microsoft packages' )
2318+
2319+
2320+ def _extract_kubectl_from_deb (deb_path , temp_dir , install_location , system ):
2321+ """
2322+ Extract kubectl binary from .deb package.
2323+ """
2324+ import subprocess
2325+
2326+ # Ensure installation directory exists
2327+ install_dir = os .path .dirname (install_location )
2328+ if not os .path .exists (install_dir ):
2329+ os .makedirs (install_dir )
2330+
2331+ # Determine binary name
2332+ if system == 'Windows' :
2333+ binary_name = 'kubectl.exe'
2334+ else :
2335+ binary_name = 'kubectl'
2336+
2337+ # validate install location
2338+ validate_install_location (install_location , binary_name )
2339+
2340+ # Extract .deb using ar command or dpkg-deb if available
2341+ try :
2342+ # Try using dpkg-deb first (handles all compression formats)
2343+ if shutil .which ('dpkg-deb' ):
2344+ subprocess .run (['dpkg-deb' , '-x' , deb_path , temp_dir ], check = True , capture_output = True )
2345+ elif shutil .which ('ar' ):
2346+ # Extract using ar command
2347+ subprocess .run (['ar' , 'x' , deb_path ], cwd = temp_dir , check = True , capture_output = True )
2348+
2349+ # Find and extract the data archive
2350+ data_files = [f for f in os .listdir (temp_dir ) if f .startswith ('data.tar' )]
2351+ if not data_files :
2352+ raise CLIError ('No data archive found in .deb package' )
2353+
2354+ data_file = os .path .join (temp_dir , data_files [0 ])
2355+
2356+ # Handle different compression formats
2357+ if data_files [0 ].endswith ('.xz' ):
2358+ import lzma
2359+ with lzma .open (data_file , 'rb' ) as f_in :
2360+ with open (os .path .join (temp_dir , 'data.tar' ), 'wb' ) as f_out :
2361+ shutil .copyfileobj (f_in , f_out )
2362+ data_file = os .path .join (temp_dir , 'data.tar' )
2363+ elif data_files [0 ].endswith ('.gz' ):
2364+ import gzip
2365+ with gzip .open (data_file , 'rb' ) as f_in :
2366+ with open (os .path .join (temp_dir , 'data.tar' ), 'wb' ) as f_out :
2367+ shutil .copyfileobj (f_in , f_out )
2368+ data_file = os .path .join (temp_dir , 'data.tar' )
2369+ elif data_files [0 ].endswith ('.zst' ):
2370+ # Try using zstd command if available
2371+ if shutil .which ('zstd' ):
2372+ uncompressed_file = os .path .join (temp_dir , 'data.tar' )
2373+ subprocess .run (['zstd' , '-d' , data_file , '-o' , uncompressed_file ], check = True , capture_output = True )
2374+ data_file = uncompressed_file
2375+ else :
2376+ logger .warning ('zstd compression detected but zstd command not found. Please install zstd: brew install zstd (macOS) or apt install zstd (Ubuntu)' )
2377+ raise CLIError ('zstd compression not supported (zstd command not found)' )
2378+
2379+ # Extract tar archive
2380+ import tarfile
2381+ with tarfile .open (data_file , 'r' ) as tar :
2382+ tar .extractall (temp_dir )
2383+ else :
2384+ # Fallback: Use Python to extract .deb (it's an ar archive)
2385+ _extract_ar_archive (deb_path , temp_dir )
2386+
2387+ # This method is complex for newer compression formats, so we'll try a simpler approach
2388+ raise CLIError ('No suitable extraction tool found (dpkg-deb, ar). Please install dpkg-deb or ar.' )
2389+
2390+ # Find kubectl binary in extracted files
2391+ kubectl_path = None
2392+ for root , dirs , files in os .walk (temp_dir ):
2393+ for file in files :
2394+ if file == 'kubectl' and os .access (os .path .join (root , file ), os .X_OK ):
2395+ kubectl_path = os .path .join (root , file )
2396+ break
2397+ if kubectl_path :
2398+ break
2399+
2400+ if not kubectl_path :
2401+ raise CLIError ('kubectl binary not found in extracted package' )
2402+
2403+ # Copy to final location
2404+ shutil .copy2 (kubectl_path , install_location )
2405+
2406+ # Set executable permissions
2407+ os .chmod (install_location ,
2408+ os .stat (install_location ).st_mode | stat .S_IXUSR | stat .S_IXGRP | stat .S_IXOTH )
2409+
2410+ except subprocess .CalledProcessError as e :
2411+ stderr = e .stderr .decode ('utf-8' ) if e .stderr else str (e )
2412+ raise CLIError (f'Failed to extract .deb package: { stderr } ' )
2413+ except Exception as e :
2414+ raise CLIError (f'Error during package extraction: { e } ' )
2415+
2416+
2417+ def _extract_ar_archive (ar_path , extract_dir ):
2418+ """
2419+ Simple Python implementation to extract .deb (ar archive) files.
2420+ """
2421+ with open (ar_path , 'rb' ) as f :
2422+ # Check for ar archive signature
2423+ signature = f .read (8 )
2424+ if signature != b'!<arch>\n ' :
2425+ raise CLIError ('Invalid .deb file format' )
2426+
2427+ while True :
2428+ # Read file header (60 bytes)
2429+ header = f .read (60 )
2430+ if len (header ) < 60 :
2431+ break
2432+
2433+ # Parse header
2434+ filename = header [0 :16 ].decode ('ascii' ).strip ()
2435+ size = int (header [48 :58 ].decode ('ascii' ).strip ())
2436+
2437+ # Skip debian-binary
2438+ if filename == 'debian-binary' :
2439+ f .read (size )
2440+ if size % 2 : # ar archives are padded to even boundaries
2441+ f .read (1 )
2442+ continue
2443+
2444+ # Read file content
2445+ content = f .read (size )
2446+ if size % 2 : # ar archives are padded to even boundaries
2447+ f .read (1 )
2448+
2449+ # Write to extract directory
2450+ output_path = os .path .join (extract_dir , filename .rstrip ('/' ))
2451+ with open (output_path , 'wb' ) as out_f :
2452+ out_f .write (content )
2453+
2454+
22082455# install kubelogin
22092456def k8s_install_kubelogin (cmd , client_version = 'latest' , install_location = None , source_url = None , arch = None ):
22102457 """
0 commit comments