Skip to content

Commit 6684692

Browse files
authored
Merge pull request #634 from mplsgrant/2024-10-install-kubectl
Offer to install kubectl
2 parents b900df5 + 945ff6b commit 6684692

File tree

2 files changed

+131
-12
lines changed

2 files changed

+131
-12
lines changed

src/warnet/constants.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,31 @@
166166
"checksum": "fc370a291ed926da5e77acf42006de48e7fd5ff94d20c3f6aa10c04fea66e53c",
167167
},
168168
]
169+
170+
171+
# Kubectl binary
172+
KUBECTL_BINARY_NAME = "kubectl"
173+
KUBECTL_BLESSED_VERSION = "v1.31.1"
174+
KUBECTL_DOWNLOAD_URL_STUB = f"https://dl.k8s.io/release/{KUBECTL_BLESSED_VERSION}/bin"
175+
KUBECTL_BLESSED_NAME_AND_CHECKSUMS = [
176+
{
177+
"system": "linux",
178+
"arch": "amd64",
179+
"checksum": "57b514a7facce4ee62c93b8dc21fda8cf62ef3fed22e44ffc9d167eab843b2ae",
180+
},
181+
{
182+
"system": "linux",
183+
"arch": "arm64",
184+
"checksum": "3af2451191e27ecd4ac46bb7f945f76b71e934d54604ca3ffc7fe6f5dd123edb",
185+
},
186+
{
187+
"system": "darwin",
188+
"arch": "amd64",
189+
"checksum": "4b86d3fb8dee8dd61f341572f1ba13c1030d493f4dc1b4831476f61f3cbb77d0",
190+
},
191+
{
192+
"system": "darwin",
193+
"arch": "arm64",
194+
"checksum": "08909b92e62004f4f1222dfd39214085383ea368bdd15c762939469c23484634",
195+
},
196+
]

src/warnet/project.py

Lines changed: 103 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
HELM_BLESSED_NAME_AND_CHECKSUMS,
2121
HELM_BLESSED_VERSION,
2222
HELM_DOWNLOAD_URL_STUB,
23+
KUBECTL_BINARY_NAME,
24+
KUBECTL_BLESSED_NAME_AND_CHECKSUMS,
25+
KUBECTL_BLESSED_VERSION,
26+
KUBECTL_DOWNLOAD_URL_STUB,
2327
)
2428
from .graph import inquirer_create_network
2529
from .network import copy_network_defaults, copy_scenario_defaults
@@ -147,7 +151,7 @@ def is_docker_desktop_running() -> tuple[bool, str]:
147151
except FileNotFoundError as err:
148152
return False, str(err)
149153

150-
def is_kubectl_installed() -> tuple[bool, str]:
154+
def is_kubectl_installed_and_offer_if_not() -> tuple[bool, str]:
151155
try:
152156
version_result = subprocess.run(
153157
["kubectl", "version", "--client"],
@@ -163,8 +167,30 @@ def is_kubectl_installed() -> tuple[bool, str]:
163167
return True, location_result.stdout.strip()
164168
else:
165169
return False, ""
166-
except FileNotFoundError as err:
167-
return False, str(err)
170+
except FileNotFoundError:
171+
print()
172+
kubectl_answer = inquirer.prompt(
173+
[
174+
inquirer.Confirm(
175+
"install_kubectl",
176+
message=click.style(
177+
"Would you like Warnet to install Kubectl into your virtual environment?",
178+
fg="blue",
179+
bold=True,
180+
),
181+
default=True,
182+
),
183+
]
184+
)
185+
if kubectl_answer is None:
186+
msg = "Setup cancelled by user."
187+
click.secho(msg, fg="yellow")
188+
return False, msg
189+
if kubectl_answer["install_kubectl"]:
190+
click.secho(" Installing Kubectl...", fg="yellow", bold=True)
191+
install_kubectl_rootlessly_to_venv()
192+
return is_kubectl_installed_and_offer_if_not()
193+
return False, "Please install Kubectl."
168194

169195
def is_helm_installed_and_offer_if_not() -> tuple[bool, str]:
170196
try:
@@ -246,7 +272,7 @@ def check_installation(tool_info: ToolInfo) -> ToolStatus:
246272
)
247273
kubectl_info = ToolInfo(
248274
tool_name="Kubectl",
249-
is_installed_func=is_kubectl_installed,
275+
is_installed_func=is_kubectl_installed_and_offer_if_not,
250276
install_instruction="Install kubectl.",
251277
install_url="https://kubernetes.io/docs/tasks/tools/install-kubectl/",
252278
)
@@ -446,7 +472,23 @@ def query_arch_from_uname(arch: str) -> Optional[str]:
446472
return None
447473

448474

449-
def write_blessed_checksum(helm_filename: str, dest_path: str):
475+
def write_blessed_kubectl_checksum(system: str, arch: str, dest_path: str):
476+
checksum = next(
477+
(
478+
b["checksum"]
479+
for b in KUBECTL_BLESSED_NAME_AND_CHECKSUMS
480+
if b["system"] == system and b["arch"] == arch
481+
),
482+
None,
483+
)
484+
if checksum:
485+
with open(dest_path, "w") as f:
486+
f.write(checksum)
487+
else:
488+
click.secho("Could not find a matching kubectl binary and checksum", fg="red")
489+
490+
491+
def write_blessed_helm_checksum(helm_filename: str, dest_path: str):
450492
checksum = next(
451493
(b["checksum"] for b in HELM_BLESSED_NAME_AND_CHECKSUMS if b["name"] == helm_filename), None
452494
)
@@ -472,12 +514,12 @@ def verify_checksum(file_path, checksum_path):
472514
click.secho(" Checksum verified.", fg="blue")
473515

474516

475-
def install_helm_to_venv(helm_bin_path):
517+
def install_to_venv(bin_path, binary_name):
476518
venv_bin_dir = os.path.join(sys.prefix, "bin")
477-
helm_dst_path = os.path.join(venv_bin_dir, HELM_BINARY_NAME)
478-
shutil.move(helm_bin_path, helm_dst_path)
479-
os.chmod(helm_dst_path, 0o755)
480-
click.secho(f" {HELM_BINARY_NAME} installed into {helm_dst_path}", fg="blue")
519+
dst_path = os.path.join(venv_bin_dir, binary_name)
520+
shutil.move(bin_path, dst_path)
521+
os.chmod(dst_path, 0o755)
522+
click.secho(f" {binary_name} installed into {dst_path}", fg="blue")
481523

482524

483525
def install_helm_rootlessly_to_venv():
@@ -512,14 +554,14 @@ def install_helm_rootlessly_to_venv():
512554
checksum_path = os.path.join(temp_dir, f"{helm_filename}.sha256")
513555

514556
download_file(helm_url, helm_archive_path)
515-
write_blessed_checksum(helm_filename, checksum_path)
557+
write_blessed_helm_checksum(helm_filename, checksum_path)
516558
verify_checksum(helm_archive_path, checksum_path)
517559

518560
# Extract Helm and install it in the virtual environment's bin folder
519561
with tarfile.open(helm_archive_path, "r:gz") as tar:
520562
tar.extractall(path=temp_dir)
521563
helm_bin_path = os.path.join(temp_dir, os_name + "-" + arch, HELM_BINARY_NAME)
522-
install_helm_to_venv(helm_bin_path)
564+
install_to_venv(helm_bin_path, HELM_BINARY_NAME)
523565

524566
click.secho(
525567
f" {HELM_BINARY_NAME} {version} installed successfully to your virtual environment!\n",
@@ -529,3 +571,52 @@ def install_helm_rootlessly_to_venv():
529571
except Exception as e:
530572
click.secho(f"Error: {e}\nCould not install helm.", fg="yellow")
531573
sys.exit(1)
574+
575+
576+
def install_kubectl_rootlessly_to_venv():
577+
if not is_in_virtualenv():
578+
click.secho(
579+
"Error: You are not in a virtual environment. Please activate a virtual environment and try again.",
580+
fg="yellow",
581+
)
582+
sys.exit(1)
583+
584+
os_name = get_os_name_for_helm()
585+
if os_name is None:
586+
click.secho(
587+
"Error: Could not determine the operating system of this computer.", fg="yellow"
588+
)
589+
sys.exit(1)
590+
591+
uname_arch = os.uname().machine
592+
arch = query_arch_from_uname(uname_arch)
593+
if arch not in ["arm64", "amd64"]:
594+
click.secho(f"No Kubectl binary candidate for arch: {uname_arch}", fg="red")
595+
sys.exit(1)
596+
597+
uname_sys = os.uname().sysname.lower()
598+
if uname_sys not in ["linux", "darwin"]:
599+
click.secho(f"The following system is not supported: {uname_sys}", fg="red")
600+
sys.exit(1)
601+
602+
kubectl_url = f"{KUBECTL_DOWNLOAD_URL_STUB}/{uname_sys}/{arch}/{KUBECTL_BINARY_NAME}"
603+
604+
try:
605+
with tempfile.TemporaryDirectory() as temp_dir:
606+
binary_path = os.path.join(temp_dir, KUBECTL_BINARY_NAME)
607+
checksum_path = os.path.join(temp_dir, f"{KUBECTL_BINARY_NAME}.sha256")
608+
609+
download_file(kubectl_url, binary_path)
610+
write_blessed_kubectl_checksum(uname_sys, arch, checksum_path)
611+
verify_checksum(binary_path, checksum_path)
612+
613+
install_to_venv(binary_path, KUBECTL_BINARY_NAME)
614+
615+
click.secho(
616+
f" {KUBECTL_BINARY_NAME} {KUBECTL_BLESSED_VERSION} installed successfully to your virtual environment!\n",
617+
fg="blue",
618+
)
619+
620+
except Exception as e:
621+
click.secho(f"Error: {e}\nCould not install helm.", fg="yellow")
622+
sys.exit(1)

0 commit comments

Comments
 (0)