Skip to content

Commit 41335d4

Browse files
committed
setup shoehorn in an option to download helm
1 parent 0af04f8 commit 41335d4

File tree

1 file changed

+153
-6
lines changed

1 file changed

+153
-6
lines changed

src/warnet/project.py

+153-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1+
import hashlib
12
import os
23
import platform
4+
import shutil
35
import subprocess
46
import sys
7+
import tarfile
8+
import tempfile
59
from dataclasses import dataclass
610
from enum import Enum, auto
711
from pathlib import Path
8-
from typing import Callable
12+
from typing import Callable, Optional
913

1014
import click
1115
import inquirer
16+
import requests
1217

18+
from .constants import HELM_BINARY_NAME, HELM_DOWNLOAD_URL_STUB, HELM_LATEST_URL
1319
from .graph import inquirer_create_network
1420
from .network import copy_network_defaults, copy_scenario_defaults
1521

@@ -19,6 +25,7 @@ def setup():
1925
"""Setup warnet"""
2026

2127
class ToolStatus(Enum):
28+
NeedsHelm = auto()
2229
Satisfied = auto()
2330
Unsatisfied = auto()
2431

@@ -155,7 +162,7 @@ def is_kubectl_installed() -> tuple[bool, str]:
155162
except FileNotFoundError as err:
156163
return False, str(err)
157164

158-
def is_helm_installed() -> tuple[bool, str]:
165+
def is_helm_installed_and_offer_if_not() -> tuple[bool, str]:
159166
try:
160167
version_result = subprocess.run(["helm", "version"], capture_output=True, text=True)
161168
location_result = subprocess.run(
@@ -167,8 +174,31 @@ def is_helm_installed() -> tuple[bool, str]:
167174
return version_result.returncode == 0, location_result.stdout.strip()
168175
else:
169176
return False, ""
170-
except FileNotFoundError as err:
171-
return False, str(err)
177+
178+
except FileNotFoundError:
179+
print()
180+
helm_answer = inquirer.prompt(
181+
[
182+
inquirer.Confirm(
183+
"install_helm",
184+
message=click.style(
185+
"Would you like to use Warnet's downloader to install Helm into your virtual environment?",
186+
fg="blue",
187+
bold=True,
188+
),
189+
default=True,
190+
),
191+
]
192+
)
193+
if helm_answer is None:
194+
msg = "Setup cancelled by user."
195+
click.secho(msg, fg="yellow")
196+
return False, msg
197+
if helm_answer["install_helm"]:
198+
click.secho(" Installing Helm...", fg="yellow", bold=True)
199+
install_helm_rootlessly_to_venv()
200+
return is_helm_installed_and_offer_if_not()
201+
return False, "Please install Helm."
172202

173203
def check_installation(tool_info: ToolInfo) -> ToolStatus:
174204
has_good_version, location = tool_info.is_installed_func()
@@ -218,8 +248,8 @@ def check_installation(tool_info: ToolInfo) -> ToolStatus:
218248
)
219249
helm_info = ToolInfo(
220250
tool_name="Helm",
221-
is_installed_func=is_helm_installed,
222-
install_instruction="Install Helm from Helm's official site.",
251+
is_installed_func=is_helm_installed_and_offer_if_not,
252+
install_instruction="Install Helm from Helm's official site, or rootlessly install Helm using Warnet's downloader when prompted.",
223253
install_url="https://helm.sh/docs/intro/install/",
224254
)
225255
minikube_info = ToolInfo(
@@ -361,3 +391,120 @@ def init():
361391
"""Initialize a warnet project in the current directory"""
362392
current_dir = Path.cwd()
363393
new_internal(directory=current_dir, from_init=True)
394+
395+
396+
def get_os_name_for_helm() -> Optional[str]:
397+
"""Return a short operating system name suitable for downloading a helm binary."""
398+
uname_sys = platform.system().lower()
399+
if "linux" in uname_sys:
400+
return "linux"
401+
elif uname_sys == "darwin":
402+
return "darwin"
403+
elif "win" in uname_sys:
404+
return "windows"
405+
return None
406+
407+
408+
def is_in_virtualenv() -> bool:
409+
"""Check if the user is in a virtual environment."""
410+
return hasattr(sys, "real_prefix") or (
411+
hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix
412+
)
413+
414+
415+
def download_file(url, destination):
416+
click.secho(f" Downloading {url}", fg="blue")
417+
response = requests.get(url, stream=True)
418+
if response.status_code == 200:
419+
with open(destination, "wb") as f:
420+
for chunk in response.iter_content(1024):
421+
f.write(chunk)
422+
else:
423+
raise Exception(f"Failed to download {url} (status code {response.status_code})")
424+
425+
426+
def get_latest_version_of_helm() -> Optional[str]:
427+
response = requests.get(HELM_LATEST_URL)
428+
if response.status_code == 200:
429+
return response.text.strip()
430+
else:
431+
return None
432+
433+
434+
def verify_checksum(file_path, checksum_path):
435+
click.secho(" Verifying checksum...", fg="blue")
436+
sha256_hash = hashlib.sha256()
437+
with open(file_path, "rb") as f:
438+
for byte_block in iter(lambda: f.read(4096), b""):
439+
sha256_hash.update(byte_block)
440+
441+
with open(checksum_path) as f:
442+
expected_checksum = f.read().strip()
443+
444+
if sha256_hash.hexdigest() != expected_checksum:
445+
raise Exception("Checksum verification failed!")
446+
click.secho(" Checksum verified.", fg="blue")
447+
448+
449+
def install_helm_to_venv(helm_bin_path):
450+
venv_bin_dir = os.path.join(sys.prefix, "bin")
451+
helm_dst_path = os.path.join(venv_bin_dir, HELM_BINARY_NAME)
452+
shutil.move(helm_bin_path, helm_dst_path)
453+
os.chmod(helm_dst_path, 0o755)
454+
click.secho(f" {HELM_BINARY_NAME} installed into {helm_dst_path}", fg="blue")
455+
456+
457+
def install_helm_rootlessly_to_venv():
458+
if not is_in_virtualenv():
459+
click.secho(
460+
"Error: You are not in a virtual environment. Please activate a virtual environment and try again.",
461+
fg="yellow",
462+
)
463+
sys.exit(1)
464+
465+
version = get_latest_version_of_helm()
466+
if version is None:
467+
click.secho(
468+
"Error: Could not fetch the latest version of Helm. Please check your internet connection.",
469+
fg="yellow",
470+
)
471+
sys.exit(1)
472+
473+
os_name = get_os_name_for_helm()
474+
if os_name is None:
475+
click.secho(
476+
"Error: Could not determine the operating system of this computer.", fg="yellow"
477+
)
478+
sys.exit(1)
479+
480+
arch = os.uname().machine
481+
arch_map = {"x86_64": "amd64", "i386": "386", "aarch64": "arm64", "armv7l": "arm"}
482+
arch = arch_map.get(arch, arch)
483+
484+
helm_filename = f"{HELM_BINARY_NAME}-{version}-{os_name}-{arch}.tar.gz"
485+
helm_url = f"{HELM_DOWNLOAD_URL_STUB}{helm_filename}"
486+
checksum_url = f"{helm_url}.sha256"
487+
488+
try:
489+
with tempfile.TemporaryDirectory() as temp_dir:
490+
helm_archive_path = os.path.join(temp_dir, helm_filename)
491+
checksum_path = os.path.join(temp_dir, f"{helm_filename}.sha256")
492+
493+
download_file(helm_url, helm_archive_path)
494+
download_file(checksum_url, checksum_path)
495+
verify_checksum(helm_archive_path, checksum_path)
496+
497+
# Extract Helm and install it in the virtual environment's bin folder
498+
with tarfile.open(helm_archive_path, "r:gz") as tar:
499+
tar.extractall(path=temp_dir)
500+
helm_bin_path = os.path.join(temp_dir, os_name + "-" + arch, HELM_BINARY_NAME)
501+
install_helm_to_venv(helm_bin_path)
502+
503+
click.secho(
504+
f" {HELM_BINARY_NAME} {version} installed successfully to your virtual environment!\n",
505+
fg="blue",
506+
)
507+
508+
except Exception as e:
509+
click.secho(f"Error: {e}\nCould not install helm.", fg="yellow")
510+
sys.exit(1)

0 commit comments

Comments
 (0)