Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pathlib everywhere #1773

Merged
merged 18 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Configuration file for the Sphinx documentation builder.

import os
import sys
from pathlib import Path

HERE = Path(__file__).parent

# -- Project information -----------------------------------------------------

Expand Down Expand Up @@ -30,7 +32,7 @@
source_suffix = ".md .rst .ipynb .py".split()

# To find the local substitute extension
sys.path.append(os.path.abspath("./ext"))
sys.path.append(str(HERE / "ext"))

extensions = [
"myst_parser",
Expand Down Expand Up @@ -96,9 +98,8 @@
myst_heading_anchors = 5

# Import qhub version number
here = os.path.abspath(os.path.dirname(__file__))
__version__ = None
exec(open(os.path.join(here, "../nebari/version.py")).read())
exec(open(HERE.parent / "nebari" / "version.py").read())

qhub_version_string = __version__

Expand Down
173 changes: 58 additions & 115 deletions nebari/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,34 @@
DEV_COMMAND_MSG = "Development tools and advanced features."


def path_callback(value: Path) -> Path:
return value.expanduser().resolve()


def config_path_callback(value: Path) -> Path:
value = path_callback(value)
if not value.is_file():
raise ValueError(f"Passed configuration path {value} does not exist!")
return value


CONFIG_PATH_OPTION: Path = typer.Option(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only refactor I've made. Since the configuration file and output path is used by multiple subcommands, I've defined them once at the top. This avoids duplication in the code and makes it easier to maintain.

...,
"--config",
"-c",
help="nebari configuration yaml file path, please pass in as -c/--config flag",
callback=config_path_callback,
)

OUTPUT_PATH_OPTION: Path = typer.Option(
Path.cwd(),
"-o",
"--output",
help="output directory",
callback=path_callback,
)


class OrderCommands(TyperGroup):
def list_commands(self, ctx: Context):
"""Return list of commands in the order appear."""
Expand Down Expand Up @@ -192,26 +220,15 @@ def init(

@app.command(rich_help_panel=SECOND_COMMAND_GROUP_NAME)
def validate(
config: str = typer.Option(
...,
"--config",
"-c",
help="nebari configuration yaml file path, please pass in as -c/--config flag",
),
config_path=CONFIG_PATH_OPTION,
enable_commenting: bool = typer.Option(
False, "--enable-commenting", help="Toggle PR commenting on GitHub Actions"
),
):
"""
Validate the values in the [purple]nebari-config.yaml[/purple] file are acceptable.
"""
config_filename = Path(config)
if not config_filename.is_file():
raise ValueError(
f"Passed in configuration filename={config_filename} must exist."
)

config = load_yaml(config_filename)
config = load_yaml(config_path)

if enable_commenting:
# for PR's only
Expand All @@ -224,18 +241,8 @@ def validate(

@app.command(rich_help_panel=SECOND_COMMAND_GROUP_NAME)
def render(
output: str = typer.Option(
"./",
"-o",
"--output",
help="output directory",
),
config: str = typer.Option(
...,
"-c",
"--config",
help="nebari configuration yaml file path",
),
output_path=OUTPUT_PATH_OPTION,
config_path=CONFIG_PATH_OPTION,
dry_run: bool = typer.Option(
False,
"--dry-run",
Expand All @@ -245,34 +252,17 @@ def render(
"""
Dynamically render the Terraform scripts and other files from your [purple]nebari-config.yaml[/purple] file.
"""
config_filename = Path(config)

if not config_filename.is_file():
raise ValueError(
f"passed in configuration filename={config_filename} must exist"
)

config_yaml = load_yaml(config_filename)
config = load_yaml(config_path)

verify(config_yaml)
verify(config)

render_template(output, config, force=True, dry_run=dry_run)
render_template(output_path, config_path, force=True, dry_run=dry_run)


@app.command()
def deploy(
config: str = typer.Option(
...,
"--config",
"-c",
help="nebari configuration yaml file path",
),
output: str = typer.Option(
"./",
"-o",
"--output",
help="output directory",
),
config_path=CONFIG_PATH_OPTION,
output_path=OUTPUT_PATH_OPTION,
dns_provider: str = typer.Option(
False,
"--dns-provider",
Expand Down Expand Up @@ -307,22 +297,15 @@ def deploy(
"""
Deploy the Nebari cluster from your [purple]nebari-config.yaml[/purple] file.
"""
config_filename = Path(config)

if not config_filename.is_file():
raise ValueError(
f"passed in configuration filename={config_filename} must exist"
)
config = load_yaml(config_path)

config_yaml = load_yaml(config_filename)

verify(config_yaml)
verify(config)

if not disable_render:
render_template(output, config, force=True)
render_template(output_path, config_path, force=True)

deploy_configuration(
config_yaml,
config,
dns_provider=dns_provider,
dns_auto_provision=dns_auto_provision,
disable_prompt=disable_prompt,
Expand All @@ -333,15 +316,8 @@ def deploy(

@app.command()
def destroy(
config: str = typer.Option(
..., "-c", "--config", help="nebari configuration file path"
),
output: str = typer.Option(
"./",
"-o",
"--output",
help="output directory",
),
config_path=CONFIG_PATH_OPTION,
output_path=OUTPUT_PATH_OPTION,
disable_render: bool = typer.Option(
False,
"--disable-render",
Expand All @@ -357,21 +333,15 @@ def destroy(
Destroy the Nebari cluster from your [purple]nebari-config.yaml[/purple] file.
"""

def _run_destroy(config=config, disable_render=disable_render):
config_filename = Path(config)
if not config_filename.is_file():
raise ValueError(
f"passed in configuration filename={config_filename} must exist"
)
def _run_destroy(config_path=config_path, disable_render=disable_render):
config = load_yaml(config_path)

config_yaml = load_yaml(config_filename)

verify(config_yaml)
verify(config)

if not disable_render:
render_template(output, config, force=True)
render_template(output_path, config_path, force=True)

destroy_configuration(config_yaml)
destroy_configuration(config)

if disable_prompt:
_run_destroy()
Expand All @@ -383,7 +353,7 @@ def _run_destroy(config=config, disable_render=disable_render):

@app.command(rich_help_panel=SECOND_COMMAND_GROUP_NAME)
def cost(
path: str = typer.Option(
path: Path = typer.Option(
None,
"-p",
"--path",
Expand All @@ -395,7 +365,7 @@ def cost(
"--dashboard",
help="Enable the cost dashboard",
),
file: str = typer.Option(
file: Path = typer.Option(
None,
"-f",
"--file",
Expand Down Expand Up @@ -431,12 +401,7 @@ def cost(

@app.command(rich_help_panel=SECOND_COMMAND_GROUP_NAME)
def upgrade(
config: str = typer.Option(
...,
"-c",
"--config",
help="nebari configuration file path",
),
config_path=CONFIG_PATH_OPTION,
attempt_fixes: bool = typer.Option(
False,
"--attempt-fixes",
Expand All @@ -450,29 +415,13 @@ def upgrade(

See the project [green]RELEASE.md[/green] for details.
"""
config_filename = Path(config)
if not config_filename.is_file():
raise ValueError(
f"passed in configuration filename={config_filename} must exist"
)

do_upgrade(config_filename, attempt_fixes=attempt_fixes)
do_upgrade(config_path, attempt_fixes=attempt_fixes)


@app.command(rich_help_panel=SECOND_COMMAND_GROUP_NAME)
def support(
config_filename: str = typer.Option(
...,
"-c",
"--config",
help="nebari configuration file path",
),
output: str = typer.Option(
"./nebari-support-logs.zip",
"-o",
"--output",
help="output filename",
),
config_path=CONFIG_PATH_OPTION,
output_path=OUTPUT_PATH_OPTION,
):
"""
Support tool to write all Kubernetes logs locally and compress them into a zip file.
Expand All @@ -484,7 +433,7 @@ def support(

v1 = client.CoreV1Api()

namespace = get_config_namespace(config=config_filename)
namespace = get_config_namespace(config_path)

pods = v1.list_namespaced_pod(namespace=namespace)

Expand Down Expand Up @@ -523,20 +472,14 @@ def support(
file.write("%s not available" % pod.metadata.name)
raise e

with ZipFile(output, "w") as zip:
with ZipFile(output_path, "w") as zip:
for file in list(Path(f"./log/{namespace}").glob("*.txt")):
print(file)
zip.write(file)


def get_config_namespace(config):
config_filename = Path(config)
if not config_filename.is_file():
raise ValueError(
f"passed in configuration filename={config_filename} must exist"
)

with config_filename.open() as f:
def get_config_namespace(config_path):
with open(config_path) as f:
config = yaml.safe_load(f.read())

return config["namespace"]
Expand Down
17 changes: 7 additions & 10 deletions nebari/cost.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import json
import logging
import os
import re
import subprocess
from pathlib import Path

from rich.console import Console
from rich.markdown import Markdown
Expand Down Expand Up @@ -66,7 +66,7 @@ def _run_infracost(path):
"""
try:
process = subprocess.Popen(
["infracost", "breakdown", "--path", path, "--format", "json"],
["infracost", "breakdown", f"--path={path}", "--format=json"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
Expand Down Expand Up @@ -119,12 +119,9 @@ def infracost_diff(path, compare_to_path):
[
"infracost",
"diff",
"--path",
path,
"--compare-to",
compare_to_path,
"--format",
"json",
f"--path={path}",
f"--compare-to={compare_to_path}",
"--format=json",
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
Expand All @@ -150,7 +147,7 @@ def infracost_report(path, dashboard, file, currency_code, compare):
console = Console()
# If path is not provided, use the current directory with `stages` subdirectory
if not path:
path = os.path.join(os.getcwd(), "stages")
path = Path.cwd() / "stages"

# Checks if infracost is installed and an API key is configured
if _check_infracost() and _check_infracost_api_key():
Expand All @@ -161,7 +158,7 @@ def infracost_report(path, dashboard, file, currency_code, compare):
_enable_infracost_dashboard()

# Check if the deployment is available on the given path
if not os.path.exists(path):
if not path.exists():
logger.error("Deployment is not available")
else:
data = _run_infracost(path)
Expand Down
Loading