Skip to content

Commit

Permalink
Merge pull request #148 from GoogleCloudPlatform/check-project-id
Browse files Browse the repository at this point in the history
Improves CLI: requires namespace when deploy; check project-id; update component CLI params.
  • Loading branch information
jonchenn authored Feb 5, 2024
2 parents a73f341 + afd2928 commit 2ae9271
Show file tree
Hide file tree
Showing 20 changed files with 423 additions and 51 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "solutions-builder"
version = "1.17.20"
version = "1.17.23"
description = "A solution framework to generate a project with built-in structure and modules"
authors = ["Jon Chen <[email protected]>"]
license = "Apache"
Expand Down
53 changes: 39 additions & 14 deletions solutions_builder/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
from typing import Optional
from typing_extensions import Annotated
from copier import run_auto
from .component import component_app
from .component import component_app, info as components_info
from .infra import infra_app
from .template import template_app
from .set import set_app
from .set import set_app, project_id as set_project_id
from .vars import vars_app
from .cli_utils import *
from .cli_constants import DEBUG
from .cli_constants import DEBUG, PLACEHOLDER_VALUES

__version__ = importlib.metadata.version("solutions-builder")
DEFAULT_DEPLOY_PROFILE = "default-deploy"
Expand Down Expand Up @@ -148,6 +148,22 @@ def deploy(
project_id = global_variables.get("project_id", None)
assert project_id, "project_id is not set in 'global_variables' in sb.yaml."

# Check namespace
allow_deploy_without_namespace = sb_yaml.get("allow_deploy_without_namespace")
if allow_deploy_without_namespace in [None, False, ""] and not namespace:
assert namespace, "Please set namespace with --namespace or -n"

if project_id in PLACEHOLDER_VALUES:
project_id = None
while not project_id:
project_id = input("Please set the GCP project ID: ")
print()
set_project_id(project_id)

# Reload sb.yaml
sb_yaml = read_yaml(f"{solution_path}/sb.yaml")
global_variables = sb_yaml.get("global_variables", {})

# Get terraform_gke component settings.
terraform_gke = sb_yaml["components"].get("terraform_gke")
env_vars = {
Expand Down Expand Up @@ -179,17 +195,27 @@ def deploy(
commands.append(
f"{skaffold_command} -p {profile} {component_flag} {namespace_flag} --default-repo=\"gcr.io/{project_id}\" {skaffold_args}"
)
print("This will build and deploy all services using the command below:")
print("This will build and deploy all services using the command "\
"and variables below:")
for command in commands:
print_success(f"- {command}")

print("\nwith the following environment variables:")
namespace_str = namespace or "default"
print("\nnamespace:")
print_success(f"- {namespace_str}")

print("\nenvironment variables:")
env_var_str = ""
for key, value in env_vars.items():
print_success(f"- {key}={value}")
env_var_str += f"{key}={value} "

confirm("\nThis may take a few minutes. Continue?", skip=yes)
print("\nglobal_variables in sb.yaml:")
for key, value in sb_yaml.get("global_variables", {}).items():
print_success(f"- {key}: {value}")

print()
confirm("This may take a few minutes. Continue?", skip=yes)

for command in commands:
exec_shell(env_var_str + command, working_dir=solution_path)
Expand Down Expand Up @@ -236,17 +262,16 @@ def info(solution_path: Annotated[Optional[str],
Print info from ./sb.yaml.
"""
sb_yaml = read_yaml(f"{solution_path}/sb.yaml")
print(f"Printing info of the solution folder at '{solution_path}'\n")
print(f"Printing info of the solution folder at '{solution_path}/'\n")

for key, value in sb_yaml.items():
if key not in ["components", "_metadata"]:
print(f"{key}: {value}")
# Global variables
print("global_variables in sb.yaml:")
for key, value in sb_yaml.get("global_variables", {}).items():
print(f"- {key}: {value}")
print()

print("Installed components:")
for key, value in sb_yaml["components"].items():
print(f" - {key}")
print()
# List of installed components.
components_info()


@app.command()
Expand Down
5 changes: 5 additions & 0 deletions solutions_builder/cli/cli_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@

# Global DEBUG flag for all CLI routes.
DEBUG = (os.environ.get("SB_DEBUG", "").lower() == "true")
PLACEHOLDER_VALUES = [
"your-project-id",
"project-id-placeholder",
"example.com"
]
6 changes: 6 additions & 0 deletions solutions_builder/cli/cli_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ def list_subfolders(path):
print()


def list_component_templates():
current_dir = os.path.dirname(__file__)
path = current_dir + "/../modules"
list_subfolders(path)


def check_git_url(url):
regex_str = "((git|ssh|http(s)?)|(git@[\\w\\.]+))(:(//)?)([\\w\\.\\@\\:/\\-~]+)(\\.git)(/)?"
regex = re.compile(regex_str)
Expand Down
67 changes: 43 additions & 24 deletions solutions_builder/cli/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"""

import typer
import traceback
from typing import Optional
from typing_extensions import Annotated
from copier import run_auto
Expand All @@ -26,18 +25,29 @@

@component_app.command()
def add(component_name,
component_template: Annotated[str, typer.Option("--template", "-t")] = None,
solution_path: Annotated[Optional[str],
typer.Argument()] = ".",
yes: Optional[bool] = False,
answers=None):
validate_solution_folder(solution_path)

# Check if component_template is empty.
if not component_template:
print("Missing component_template. Please set --template or -t with one " \
" of the component templates:")
list_component_templates()
return

confirm(
f"This will add component '{component_name}' to '{solution_path}'. " +
"Continue?",
f"This will add component '{component_name}' to " \
f"{solution_path}/components folder. Continue?",
skip=yes)

answers_dict = get_answers_dict(answers)
process_component("add", component_name, solution_path, data=answers_dict)
process_component("add",
component_name, component_template,
solution_path, data=answers_dict)
print_success(
f"Complete. Component {component_name} added to solution at {solution_path}\n"
)
Expand All @@ -51,13 +61,19 @@ def update(component_name,
answers=None):
validate_solution_folder(solution_path)
confirm(
f"This will update the existing component '{component_name}' in '{solution_path}'. "
+ "Continue?",
f"This will update '{component_name}' in " \
f"'{solution_path}/components'. Continue?",
skip=yes)

sb_yaml = read_yaml(f"{solution_path}/sb.yaml")
components = sb_yaml.get("components", {})
component_dict = components.get(component_name, {})
component_template = component_dict.get("component_template")

answers_dict = get_answers_dict(answers)
process_component("update",
component_name,
component_template,
solution_path,
data=answers_dict,
use_existing_answers=yes)
Expand Down Expand Up @@ -93,32 +109,37 @@ def update_component_to_root_yaml(component_name, answers, solution_path):

def process_component(method,
component_name,
component_template,
solution_path,
data={},
use_existing_answers=False):

assert component_template, f"component_template is not empty."

destination_path = "."
current_dir = os.path.dirname(__file__)
answers_file = None

# Get basic info from root sb.yaml.
root_st_yaml = read_yaml(f"{solution_path}/sb.yaml")
sb_yaml = read_yaml(f"{solution_path}/sb.yaml")
global_variables = sb_yaml.get("global_variables", {})
component_answers = {}

# If the component name is a Git URL, use the URL as-is in copier.
if check_git_url(component_name):
print(f"Loading component from remote Git URL: {component_name}")
template_path = component_name
if check_git_url(component_template):
print(f"Loading component from remote Git URL: {component_template}")
template_path = component_template

# Otherwise, try to locate the component in local modules/ folder.
else:

if method == "update":
data["component_name"] = component_name
if component_name not in root_st_yaml["components"]:
if component_name not in sb_yaml["components"]:
raise NameError(
f"Component {component_name} is not defined in the root yaml 'sb.yaml' file."
)
component_answers = root_st_yaml["components"][component_name]
component_answers = sb_yaml["components"][component_name]
component_template = component_answers["component_template"]
template_path = f"{current_dir}/../modules/{component_template}"
answers_file = f".st/module_answers/{component_name}.yaml"
Expand All @@ -130,20 +151,20 @@ def process_component(method,
data[key] = value

else:
component_template = component_name
template_path = f"{current_dir}/../modules/{component_template}"
if not os.path.exists(template_path):
raise FileNotFoundError(
f"Component {component_name} does not exist in modules folder.")
f"Component {component_template} does not exist in modules folder.")

# Get destination_path defined in copier.yaml
copier_dict = get_copier_yaml(template_path)
destination_path = solution_path + "/" + copier_dict["_metadata"].get(
"destination_path")
destination_path = destination_path.replace("//", "/")

data["project_id"] = root_st_yaml["project_id"]
data["project_number"] = root_st_yaml["project_number"]
data["component_name"] = component_name
data["project_id"] = global_variables["project_id"]
data["project_number"] = global_variables["project_number"]
data["solution_path"] = solution_path
data["template_path"] = template_path

Expand Down Expand Up @@ -179,10 +200,10 @@ def process_component(method,

# List installed components.
@component_app.command()
def list(solution_path: Annotated[Optional[str], typer.Argument()] = ".", ):
root_st_yaml = read_yaml(f"{solution_path}/sb.yaml")
components = root_st_yaml.get("components", [])
print("Installed components:\n")
def info(solution_path: Annotated[Optional[str], typer.Argument()] = ".", ):
sb_yaml = read_yaml(f"{solution_path}/sb.yaml")
components = sb_yaml.get("components", [])
print("Installed components:")
for component_name, properties in components.items():
typer.echo(
typer.style(f"- {component_name} ", fg=typer.colors.WHITE, bold=True) +
Expand All @@ -194,8 +215,6 @@ def list(solution_path: Annotated[Optional[str], typer.Argument()] = ".", ):

# List available components to add.
@component_app.command()
def available():
current_dir = os.path.dirname(__file__)
path = current_dir + "/../modules"
def list():
print("Available components to add:\n")
list_subfolders(path)
list_component_templates()
15 changes: 9 additions & 6 deletions solutions_builder/cli/infra.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ def init(solution_path: Annotated[Optional[str],
auto_approve_flag = "-auto-approve"

# Get project ID from the existing root yaml.
st_yaml = read_yaml(f"{solution_path}/sb.yaml")
project_id = st_yaml["project_id"]
sb_yaml = read_yaml(f"{solution_path}/sb.yaml")
global_variables = sb_yaml.get("global_variables", {})
project_id = global_variables["project_id"]

confirm(f"""
This will initialize the solution with the following steps:
Expand Down Expand Up @@ -109,8 +110,9 @@ def apply(stage,
skip=yes)

# Get project_id
st_yaml = read_yaml(f"{solution_path}/sb.yaml")
project_id = st_yaml["project_id"]
sb_yaml = read_yaml(f"{solution_path}/sb.yaml")
global_variables = sb_yaml.get("global_variables", {})
project_id = global_variables["project_id"]

# Get impersonate service account email
env_var_clause = get_impersonate_clause(impersonate, impersonate_email,
Expand Down Expand Up @@ -155,8 +157,9 @@ def destroy(stage,
skip=yes)

# Get project ID from the existing root yaml.
st_yaml = read_yaml(f"{solution_path}/sb.yaml")
project_id = st_yaml["project_id"]
sb_yaml = read_yaml(f"{solution_path}/sb.yaml")
global_variables = sb_yaml.get("global_variables", {})
project_id = global_variables["project_id"]

# Get impersonate service account email
env_var_clause = get_impersonate_clause(impersonate, impersonate_email,
Expand Down
3 changes: 2 additions & 1 deletion solutions_builder/cli/set.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ def project_id(

# Update Root sb.yaml
new_project_number = get_project_number(new_project_id)
assert new_project_number, "Unable to receive project number for project '{new_project_id}'"
assert new_project_number, "Unable to receive project number for project " \
f"'{new_project_id}'. Does this GCP project exist?"

global_variables["project_id"] = new_project_id
global_variables["project_number"] = new_project_number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ Main components after setup:

## Setup

Run `st components add [COMPONENT_NAME]` to add this module.
Run `sb components add [COMPONENT_NAME]` to add this module.
```
cd my-solution-folder
st components add terraform_gke .
sb components add terraform_gke .
```

Fill in the variables.
Expand All @@ -20,7 +20,7 @@ Fill in the variables.
🎤 Which Google Cloud region?
us-central1
🎤 Kubernetes version?
1.24.11-gke.1000
latest
🎤 Allow domains for CORS? (comma-seperated)
http://localhost:4200,http://localhost:3000
🎤 Cert Issuer Email
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Changes here will be overwritten by Copier
{{_copier_answers|to_nice_yaml -}}
Loading

0 comments on commit 2ae9271

Please sign in to comment.