Skip to content

Commit

Permalink
Merge pull request #3393 from GeorgianaElena/dedicated-cluster-is3294
Browse files Browse the repository at this point in the history
[deployer] Don't allow overwriting existing cluster infrastructure files unless `--force` is used and working tree is clean
  • Loading branch information
GeorgianaElena authored Nov 13, 2023
2 parents 1fb434f + 0b2f81a commit 2173328
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 10 deletions.
45 changes: 38 additions & 7 deletions deployer/commands/generate/dedicated_cluster/aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,28 @@
from deployer.utils.file_acquisition import REPO_ROOT_PATH
from deployer.utils.rendering import print_colour

from .common import generate_config_directory, generate_support_files
from .common import (
check_before_continuing_with_generate_command,
generate_config_directory,
generate_support_files,
)
from .dedicated_cluster_app import dedicated_cluster_app


def get_infra_files_to_be_created(cluster_name):
return [
REPO_ROOT_PATH / "eksctl" / f"{cluster_name}.jsonnet",
REPO_ROOT_PATH / "terraform/aws/projects" / f"{cluster_name}.tfvars",
REPO_ROOT_PATH / "eksctl/ssh-keys/secret" / f"{cluster_name}.key",
REPO_ROOT_PATH / "config/clusters" / cluster_name / "support.values.yaml",
REPO_ROOT_PATH
/ "config/clusters"
/ cluster_name
/ "enc-support.secret.values.yaml",
REPO_ROOT_PATH / "config/clusters" / cluster_name / "cluster.yaml",
]


def generate_infra_files(vars):
cluster_name = vars["cluster_name"]
with open(REPO_ROOT_PATH / "eksctl/template.jsonnet") as f:
Expand All @@ -42,14 +60,14 @@ def generate_infra_files(vars):
print_colour("Generating the terraform infrastructure file...", "yellow")
with open(REPO_ROOT_PATH / "terraform/aws/projects/template.tfvars") as f:
tfvars_template = jinja2.Template(f.read())

tfvars_file_path = (
REPO_ROOT_PATH / "terraform/aws/projects" / f"{cluster_name}.tfvars"
)
with open(tfvars_file_path, "w") as f:
f.write(tfvars_template.render(**vars))
print_colour(f"{tfvars_file_path} created")

print_colour("Generate, encrypt and store the ssh private key...", "yellow")
subprocess.check_call(
[
"ssh-keygen",
Expand All @@ -67,15 +85,17 @@ def generate_infra_files(vars):
f"{REPO_ROOT_PATH}/eksctl/ssh-keys/secret/{cluster_name}.key",
)

ssh_key_file = REPO_ROOT_PATH / "eksctl/ssh-keys/secret" / f"{cluster_name}.key"
# Encrypt the private key
subprocess.check_call(
[
"sops",
"--in-place",
"--encrypt",
f"{REPO_ROOT_PATH}/eksctl/ssh-keys/secret/{cluster_name}.key",
ssh_key_file,
]
)
print_colour(f"{ssh_key_file} created")


@dedicated_cluster_app.command()
Expand All @@ -87,23 +107,34 @@ def aws(
cluster_region: str = typer.Option(
..., prompt="The region where to deploy the cluster"
),
force: bool = typer.Option(
False,
"--force",
help="Whether or not to force the override of the files that already exist",
),
):
"""
Automatically generate the files required to setup a new cluster on AWS
Automatically generate the files required to setup a new cluster on AWS if they don't exist.
Use --force to force existing configuration files to be overwritten by this command.
"""

# These are the variables needed by the templates used to generate the cluster config file
# and support files

vars = {
"cluster_name": cluster_name,
"hub_type": hub_type,
"cluster_region": cluster_region,
}

generate_infra_files(vars)
if not check_before_continuing_with_generate_command(
get_infra_files_to_be_created, cluster_name, force
):
raise typer.Abort()

# If we are here, then either no existing infrastructure files for this cluster have been found
# or the `--force` flag was provided and we can override existing files.
generate_infra_files(vars)
# Automatically generate the config directory
cluster_config_directory = generate_config_directory(vars)

# Generate the support files
generate_support_files(cluster_config_directory, vars)
62 changes: 61 additions & 1 deletion deployer/commands/generate/dedicated_cluster/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,81 @@
import subprocess

import jinja2
from git import Repo

from deployer.utils.file_acquisition import REPO_ROOT_PATH
from deployer.utils.rendering import print_colour


def check_git_status_clean(infra_files):
"""
Check if running `git status` doesn't return any file that the generate command should create/update.
If any of the files in `infra_files` (the files that the script will generate) are in the `git status` output,
then return False, otherwise True.
"""
repo = Repo(REPO_ROOT_PATH)
status = repo.git.status("--porcelain")
list_of_modified_files = status.split("\n")

for file in list_of_modified_files:
file = file.lstrip("?? ") if file.startswith("?? ") else file.lstrip(" M ")
full_filepath = REPO_ROOT_PATH / file
if full_filepath in infra_files:
print_colour(
f"{full_filepath} was not committed. Commit or restore the file in order to proceed with the generation.",
"yellow",
)
return False
return True


def check_before_continuing_with_generate_command(
get_infra_files_func, cluster_name, force
):
"""
This function does a sanity check of the current state of infrastructure files of the desired cluster.
These checks will then be used to decide if the functions generating the files will be ran.
The checks are:
1. if any of the files that will be generated are found in the `git status` output, then return False
2. else, if none of the infrastructure files that the script will generate exist, then return True
3. else, if there are infrastructure files that already exist and the --force flag was not used, then return False
$. Otherwise, return True
"""
infra_files = get_infra_files_func(cluster_name)
if not check_git_status_clean(infra_files):
return False

if not (any(os.path.exists(path) for path in infra_files)):
return True

if not force:
print_colour(
f"Found existing infrastructure files for cluster {cluster_name}. Use --force if you want to allow this script to overwrite them.",
"red",
)
return False

print_colour(
f"Attention! Found existing infrastructure files for {cluster_name}. They will be overwritten by the --force flag!",
"red",
)
return True


def generate_cluster_config_file(cluster_config_directory, provider, vars):
"""
Generates the `config/<cluster_name>/cluster.yaml` config
"""
print_colour("Generating the cluster.yaml config file...", "yellow")
with open(
REPO_ROOT_PATH / f"config/clusters/templates/{provider}/cluster.yaml"
) as f:
cluster_yaml_template = jinja2.Template(f.read())
with open(cluster_config_directory / "cluster.yaml", "w") as f:
cluster_yaml_file = cluster_config_directory / "cluster.yaml"
with open(cluster_yaml_file, "w") as f:
f.write(cluster_yaml_template.render(**vars))
print_colour(f"{cluster_yaml_file} created")


def generate_support_files(cluster_config_directory, vars):
Expand Down
31 changes: 30 additions & 1 deletion deployer/commands/generate/dedicated_cluster/gcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,26 @@
from deployer.utils.rendering import print_colour

from .common import (
check_before_continuing_with_generate_command,
generate_cluster_config_file,
generate_config_directory,
generate_support_files,
)
from .dedicated_cluster_app import dedicated_cluster_app


def get_infra_files_to_be_created(cluster_name):
return [
REPO_ROOT_PATH / "terraform/gcp/projects" / f"{cluster_name}.tfvars",
REPO_ROOT_PATH / "config/clusters" / cluster_name / "support.values.yaml",
REPO_ROOT_PATH
/ "config/clusters"
/ cluster_name
/ "enc-support.secret.values.yaml",
REPO_ROOT_PATH / "config/clusters" / cluster_name / "cluster.yaml",
]


def generate_terraform_file(vars):
"""
Generates the `terraform/gcp/projects/<cluster_name>.tfvars` terraform file
Expand Down Expand Up @@ -63,9 +76,17 @@ def gcp(
hub_type: Annotated[
str, typer.Option(prompt="Please insert the hub type of the first hub")
] = "basehub",
force: Annotated[
bool,
typer.Option(
"--force",
help="Whether or not to force the override of the files that already exist",
),
] = False,
):
"""
Automatically generates the initial files, required to setup a new cluster on GCP
Automatically generates the initial files, required to setup a new cluster on GCP if they don't exist.
Use --force to force existing configuration files to be overwritten by this command.
"""
# These are the variables needed by the templates used to generate the cluster config file
# and support files
Expand All @@ -77,6 +98,14 @@ def gcp(
"hub_name": hub_name,
}

if not check_before_continuing_with_generate_command(
get_infra_files_to_be_created, cluster_name, force
):
raise typer.Abort()

# If we are here, then either no existing infrastructure files for this cluster have been found
# or the `--force` flag was provided and we can override existing files.

# Automatically generate the terraform config file
generate_terraform_file(vars)

Expand Down
5 changes: 4 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ google-cloud-bigquery==3.13.*
google-cloud-bigquery[pandas]==3.13.*
gspread==5.12.*

# requests is used by deployer/cilogon_app.py
# requests is used by deployer/commands/cilogon.py
requests==2.*

# this is used by the generator of dedicated cluster files deployer/commands/dedicated_cluster
GitPython==3.1.40

# Used to parse units that kubernetes understands (like GiB)
kubernetes

0 comments on commit 2173328

Please sign in to comment.