Skip to content

Commit 12901ae

Browse files
Refactor Vault resources (#210)
* Refactor Vault resources Co-authored-by: Daniele Pompa <55095241+daniele20tab@users.noreply.github.com>
1 parent 3491df7 commit 12901ae

File tree

15 files changed

+419
-536
lines changed

15 files changed

+419
-536
lines changed

bootstrap/collector.py

Lines changed: 49 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def collect(
132132
deployment_type = clean_deployment_type(deployment_type)
133133
# The "digitalocean-k8s" deployment type includes Postgres by default
134134
if digitalocean_enabled := ("digitalocean" in deployment_type):
135-
digitalocean_token = validate_or_prompt_password(
135+
digitalocean_token = validate_or_prompt_secret(
136136
"DigitalOcean token", digitalocean_token
137137
)
138138
(
@@ -236,9 +236,9 @@ def collect(
236236
sentry_org,
237237
sentry_url,
238238
sentry_auth_token,
239-
backend_type,
239+
backend_service_slug,
240240
backend_sentry_dsn,
241-
frontend_type,
241+
frontend_service_slug,
242242
frontend_sentry_dsn,
243243
)
244244
(
@@ -394,8 +394,8 @@ def validate_or_prompt_email(message, value=None, default=None, required=True):
394394
return validate_or_prompt_email(message, None, default, required)
395395

396396

397-
def validate_or_prompt_password(message, value=None, default=None, required=True):
398-
"""Validate the given password or prompt until a valid value is provided."""
397+
def validate_or_prompt_secret(message, value=None, default=None, required=True):
398+
"""Validate the given secret or prompt until a valid value is provided."""
399399
if value is None:
400400
value = click.prompt(message, default=default, hide_input=True)
401401
try:
@@ -404,7 +404,7 @@ def validate_or_prompt_password(message, value=None, default=None, required=True
404404
except validators.ValidationFailure:
405405
pass
406406
click.echo(error("Please type at least 8 chars!"))
407-
return validate_or_prompt_password(message, None, default, required)
407+
return validate_or_prompt_secret(message, None, default, required)
408408

409409

410410
def validate_or_prompt_path(message, value=None, default=None, required=True):
@@ -543,7 +543,7 @@ def clean_terraform_backend(
543543
terraform_cloud_hostname,
544544
default="app.terraform.io",
545545
)
546-
terraform_cloud_token = validate_or_prompt_password(
546+
terraform_cloud_token = validate_or_prompt_secret(
547547
"Terraform Cloud User token",
548548
terraform_cloud_token,
549549
)
@@ -583,16 +583,21 @@ def clean_terraform_backend(
583583

584584
def clean_vault_data(vault_token, vault_url, quiet=False):
585585
"""Return the Vault data, if applicable."""
586-
if vault_token or (
587-
vault_token is None
586+
if vault_url or (
587+
vault_url is None
588588
and click.confirm(
589589
"Do you want to use Vault for secrets management?",
590590
)
591591
):
592-
vault_token = validate_or_prompt_password("Vault token", vault_token)
592+
vault_token = validate_or_prompt_secret(
593+
"Vault token (leave blank to perform a browser-based OIDC authentication)",
594+
vault_token,
595+
default="",
596+
required=False,
597+
)
593598
quiet or click.confirm(
594599
warning(
595-
"Make sure the Vault token has enough permissions to enable the "
600+
"Make sure your Vault permissions allow to enable the "
596601
"project secrets backends and manage the project secrets. Continue?"
597602
),
598603
abort=True,
@@ -635,7 +640,7 @@ def clean_kubernetes_credentials(
635640
kubernetes_host = kubernetes_host or validate_or_prompt_url(
636641
"Kubernetes host", kubernetes_host
637642
)
638-
kubernetes_token = kubernetes_token or validate_or_prompt_password(
643+
kubernetes_token = kubernetes_token or validate_or_prompt_secret(
639644
"Kubernetes token", kubernetes_token
640645
)
641646
return kubernetes_cluster_ca_certificate, kubernetes_host, kubernetes_token
@@ -710,30 +715,49 @@ def clean_letsencrypt_certificate_email(letsencrypt_certificate_email):
710715
)
711716

712717

718+
def clean_sentry_org(sentry_org):
719+
"""Return the Sentry organization."""
720+
return sentry_org if sentry_org is not None else click.prompt("Sentry organization")
721+
722+
723+
def clean_sentry_dsn(service_slug, sentry_dsn):
724+
"""Return the backend Sentry DSN."""
725+
if service_slug:
726+
return validate_or_prompt_url(
727+
f"Sentry DSN of the {service_slug} service (leave blank if unused)",
728+
sentry_dsn,
729+
default="",
730+
required=False,
731+
)
732+
733+
713734
def clean_sentry_data(
714735
sentry_org,
715736
sentry_url,
716737
sentry_auth_token,
717-
backend_type,
738+
backend_service_slug,
718739
backend_sentry_dsn,
719-
frontend_type,
740+
frontend_service_slug,
720741
frontend_sentry_dsn,
721742
):
722743
"""Return the Sentry configuration data."""
723-
if sentry_org or (
724-
sentry_org is None
725-
and click.confirm(warning("Do you want to use Sentry?"), default=False)
744+
if any((backend_service_slug, frontend_service_slug)) and (
745+
sentry_org
746+
or (
747+
sentry_org is None
748+
and click.confirm(warning("Do you want to use Sentry?"), default=False)
749+
)
726750
):
727751
sentry_org = clean_sentry_org(sentry_org)
728752
sentry_url = validate_or_prompt_url(
729753
"Sentry URL", sentry_url, default="https://sentry.io/"
730754
)
731-
sentry_auth_token = validate_or_prompt_password(
755+
sentry_auth_token = validate_or_prompt_secret(
732756
"Sentry auth token", sentry_auth_token
733757
)
734-
backend_sentry_dsn = clean_backend_sentry_dsn(backend_type, backend_sentry_dsn)
735-
frontend_sentry_dsn = clean_frontend_sentry_dsn(
736-
frontend_type, frontend_sentry_dsn
758+
backend_sentry_dsn = clean_sentry_dsn(backend_service_slug, backend_sentry_dsn)
759+
frontend_sentry_dsn = clean_sentry_dsn(
760+
frontend_service_slug, frontend_sentry_dsn
737761
)
738762
else:
739763
sentry_org = None
@@ -750,37 +774,6 @@ def clean_sentry_data(
750774
)
751775

752776

753-
def clean_sentry_org(sentry_org):
754-
"""Return the Sentry organization."""
755-
return sentry_org if sentry_org is not None else click.prompt("Sentry organization")
756-
757-
758-
def clean_backend_sentry_dsn(backend_type, backend_sentry_dsn):
759-
"""Return the backend Sentry DSN."""
760-
if backend_type:
761-
return (
762-
backend_sentry_dsn
763-
if backend_sentry_dsn is not None
764-
else click.prompt(
765-
"Backend Sentry DSN (leave blank if unused)",
766-
default="",
767-
)
768-
)
769-
770-
771-
def clean_frontend_sentry_dsn(frontend_type, frontend_sentry_dsn):
772-
"""Return the frontend Sentry DSN."""
773-
if frontend_type:
774-
return (
775-
frontend_sentry_dsn
776-
if frontend_sentry_dsn is not None
777-
else click.prompt(
778-
"Frontend Sentry DSN (leave blank if unused)",
779-
default="",
780-
)
781-
)
782-
783-
784777
def clean_digitalocean_options(
785778
digitalocean_domain_create,
786779
digitalocean_dns_records_create,
@@ -899,7 +892,7 @@ def clean_pact_broker_data(pact_broker_url, pact_broker_username, pact_broker_pa
899892
pact_broker_username = pact_broker_username or click.prompt(
900893
"Pact broker username",
901894
)
902-
pact_broker_password = validate_or_prompt_password(
895+
pact_broker_password = validate_or_prompt_secret(
903896
"Pact broker password", pact_broker_password
904897
)
905898
else:
@@ -1008,7 +1001,7 @@ def clean_s3_media_storage_data(
10081001
):
10091002
"""Return S3 media storage data."""
10101003
if media_storage == MEDIA_STORAGE_DIGITALOCEAN_S3:
1011-
digitalocean_token = validate_or_prompt_password(
1004+
digitalocean_token = validate_or_prompt_secret(
10121005
"DigitalOcean token", digitalocean_token
10131006
)
10141007
s3_region = s3_region or click.prompt(
@@ -1027,8 +1020,8 @@ def clean_s3_media_storage_data(
10271020
s3_bucket_name = s3_bucket_name or click.prompt(
10281021
"AWS S3 bucket name",
10291022
)
1030-
s3_access_id = validate_or_prompt_password("S3 Access Key ID", s3_access_id)
1031-
s3_secret_key = validate_or_prompt_password("S3 Secret Access Key", s3_secret_key)
1023+
s3_access_id = validate_or_prompt_secret("S3 Access Key ID", s3_access_id)
1024+
s3_secret_key = validate_or_prompt_secret("S3 Secret Access Key", s3_secret_key)
10321025
return (
10331026
digitalocean_token,
10341027
s3_region,

bootstrap/constants.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,72 @@
11
"""Web project initialization CLI constants."""
22

33
from pathlib import Path
4+
from typing import Dict
45

56
DUMPS_DIR = Path(__file__).parent.parent / ".dumps"
67

78
# Stacks
89

10+
# BEWARE: stack names must be suitable for inclusion in Vault paths
11+
12+
DEV_STACK_NAME = "development"
13+
914
DEV_STACK_SLUG = "dev"
1015

16+
STAGE_STACK_NAME = "staging"
17+
1118
STAGE_STACK_SLUG = "stage"
1219

20+
MAIN_STACK_NAME = "main"
21+
1322
MAIN_STACK_SLUG = "main"
1423

24+
STACKS_CHOICES = {
25+
"1": [{"name": MAIN_STACK_NAME, "slug": MAIN_STACK_SLUG}],
26+
"2": [
27+
{"name": DEV_STACK_NAME, "slug": DEV_STACK_SLUG},
28+
{"name": MAIN_STACK_NAME, "slug": MAIN_STACK_SLUG},
29+
],
30+
"3": [
31+
{"name": DEV_STACK_NAME, "slug": DEV_STACK_SLUG},
32+
{"name": STAGE_STACK_NAME, "slug": STAGE_STACK_SLUG},
33+
{"name": MAIN_STACK_NAME, "slug": MAIN_STACK_SLUG},
34+
],
35+
}
36+
1537
# Environments
1638

39+
# BEWARE: environment names must be suitable for inclusion in Vault paths
40+
1741
DEV_ENV_NAME = "development"
1842

1943
DEV_ENV_SLUG = "dev"
2044

45+
DEV_ENV_STACK_CHOICES: Dict[str, str] = {
46+
"1": MAIN_STACK_SLUG,
47+
}
48+
2149
STAGE_ENV_NAME = "staging"
2250

2351
STAGE_ENV_SLUG = "stage"
2452

53+
STAGE_ENV_STACK_CHOICES: Dict[str, str] = {
54+
"1": MAIN_STACK_SLUG,
55+
"2": DEV_STACK_SLUG,
56+
}
57+
2558
PROD_ENV_NAME = "production"
2659

2760
PROD_ENV_SLUG = "prod"
2861

62+
PROD_ENV_STACK_CHOICES: Dict[str, str] = {}
63+
2964
# Env vars
3065

3166
GITLAB_TOKEN_ENV_VAR = "GITLAB_PRIVATE_TOKEN"
3267

68+
VAULT_TOKEN_ENV_VAR = "VAULT_TOKEN"
69+
3370
# Subrepos
3471

3572
BACKEND_TEMPLATE_URLS = {

0 commit comments

Comments
 (0)