From 150aaa4813bc0aa043715c8fd3727e513fc3a2d9 Mon Sep 17 00:00:00 2001 From: Alexander Ulyanov Date: Tue, 19 Sep 2023 16:41:20 +0200 Subject: [PATCH] VCS and K8s tf integration (#6) * VCS and K8s tf integration * GitOps parametrisation and push --------- Co-authored-by: Serg Shalavin --- .gitignore | 14 +- cli/.flake8 | 2 +- cli/commands/setup.py | 283 +++++++++++++++--- cli/common/const/const.py | 6 +- cli/common/enums/state.py | 37 --- cli/common/state_store.py | 50 +++- cli/requirements.txt | 11 +- cli/services/cloud/aws/aws_manager.py | 40 ++- cli/services/cloud/aws/aws_sdk.py | 8 +- cli/services/cloud/cloud_provider_manager.py | 23 +- cli/services/dependency_manager.py | 14 +- cli/services/kctl_wrapper.py | 48 +++ cli/services/keys/key_manager.py | 37 ++- cli/services/tf_wrapper.py | 63 ++++ cli/services/vcs/git_provider_manager.py | 16 +- cli/services/vcs/github/github_manager.py | 44 ++- cli/services/vcs/template_manager.py | 152 +++++++++- cli/tests/parameters.yaml | 10 +- platform/README.md | 37 --- ...binding.yaml => 0-clusterrolebinding.yaml} | 0 .../{registry.yaml => 0-registry.yaml} | 2 +- ...cert-manager.yaml => 10-cert-manager.yaml} | 2 +- ...external-dns.yaml => 10-external-dns.yaml} | 2 +- .../{argocd.yaml => 100-argocd.yaml} | 2 +- .../{monitoring.yaml => 105-monitoring.yaml} | 2 +- .../{kubecost.yaml => 130-kubecost.yaml} | 2 +- .../{trivy.yaml => 130-trivy.yaml} | 2 +- .../{tracee.yaml => 140-tracee.yaml} | 2 +- .../{kyverno.yaml => 160-kyverno.yaml} | 2 +- ...cert-issuers.yaml => 20-cert-issuers.yaml} | 2 +- .../{vault.yaml => 20-vault.yaml} | 2 +- ...yaml => 30-external-secrets-operator.yaml} | 2 +- ...tore.yaml => 40-cluster-secret-store.yaml} | 2 +- ...yaml => 50-actions-runner-controller.yaml} | 2 +- ...-workflows.yaml => 50-argo-workflows.yaml} | 2 +- .../{atlantis.yml => 50-atlantis.yml} | 2 +- .../{reloader.yaml => 50-reloader.yaml} | 2 +- ...thub-runner.yaml => 60-github-runner.yaml} | 2 +- .../argo-workflows/application.yaml | 2 +- .../argo-workflows/argo-workflows-cwfts.yaml | 2 +- .../{registry.yaml => 0-registry.yaml} | 2 +- .../{README.md => tpl_README.md} | 0 platform/terraform/hosting_provider/main.tf | 31 +- platform/terraform/hosting_provider/output.tf | 32 ++ .../modules/cloud_aws/TERRAFORM-README.md | 42 +-- platform/terraform/modules/cloud_aws/iam.tf | 4 +- platform/terraform/modules/cloud_aws/main.tf | 2 +- .../terraform/modules/cloud_aws/outputs.tf | 4 +- .../terraform/modules/cloud_aws/policy.tf | 10 +- .../terraform/modules/cloud_aws/variables.tf | 25 +- .../terraform/modules/vcs_github/variable.tf | 5 +- .../modules/vcs_github/workload_repos.tf | 15 +- .../terraform/{README.md => tpl_README.md} | 0 platform/terraform/vcs/main.tf | 52 +--- platform/terraform/vcs/variable.tf | 5 +- platform/tpl_README.md | 45 +++ templating.md | 16 +- 57 files changed, 920 insertions(+), 305 deletions(-) delete mode 100644 cli/common/enums/state.py create mode 100644 cli/services/kctl_wrapper.py create mode 100644 cli/services/tf_wrapper.py delete mode 100644 platform/README.md rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{clusterrolebinding.yaml => 0-clusterrolebinding.yaml} (100%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{registry.yaml => 0-registry.yaml} (88%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{cert-manager.yaml => 10-cert-manager.yaml} (80%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{external-dns.yaml => 10-external-dns.yaml} (80%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{argocd.yaml => 100-argocd.yaml} (83%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{monitoring.yaml => 105-monitoring.yaml} (82%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{kubecost.yaml => 130-kubecost.yaml} (82%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{trivy.yaml => 130-trivy.yaml} (81%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{tracee.yaml => 140-tracee.yaml} (80%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{kyverno.yaml => 160-kyverno.yaml} (80%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{cert-issuers.yaml => 20-cert-issuers.yaml} (79%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{vault.yaml => 20-vault.yaml} (80%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{external-secrets-operator.yaml => 30-external-secrets-operator.yaml} (79%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{cluster-secret-store.yaml => 40-cluster-secret-store.yaml} (82%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{actions-runner-controller.yaml => 50-actions-runner-controller.yaml} (84%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{argo-workflows.yaml => 50-argo-workflows.yaml} (84%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{atlantis.yml => 50-atlantis.yml} (82%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{reloader.yaml => 50-reloader.yaml} (81%) rename platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/{github-runner.yaml => 60-github-runner.yaml} (86%) rename platform/gitops-pipelines/delivery/clusters/worker-cluster/core-services/{registry.yaml => 0-registry.yaml} (88%) rename platform/gitops-pipelines/{README.md => tpl_README.md} (100%) create mode 100644 platform/terraform/hosting_provider/output.tf rename platform/terraform/{README.md => tpl_README.md} (100%) create mode 100644 platform/tpl_README.md diff --git a/.gitignore b/.gitignore index 82aa5f5b..0b7b8690 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ .idea -**/*.env -**/.env -**/.terraform/ -**/.terraform.lock.hcl -**/.terraform.tfstate.lock.info -**/terraform.tfstate -**/terraform.tfstate.backup +*.env +.env +.terraform/ +.terraform.lock.hcl +.terraform.tfstate.lock.info +terraform.tfstate +terraform.tfstate.backup .vscode .DS_Store \ No newline at end of file diff --git a/cli/.flake8 b/cli/.flake8 index b6cd0aef..855da0bc 100644 --- a/cli/.flake8 +++ b/cli/.flake8 @@ -1,4 +1,4 @@ -flake8] +[flake8] extend-ignore = E722,W504,E501,Q000,N802,D401,D413 exclude = venv, diff --git a/cli/commands/setup.py b/cli/commands/setup.py index 9c4ad8ab..5d91f720 100644 --- a/cli/commands/setup.py +++ b/cli/commands/setup.py @@ -1,7 +1,10 @@ +import os +from pathlib import Path + import click import yaml -from cli.common.const.const import GITOPS_REPOSITORY_URL, GITOPS_REPOSITORY_BRANCH +from cli.common.const.const import GITOPS_REPOSITORY_URL, GITOPS_REPOSITORY_BRANCH, LOCAL_FOLDER from cli.common.const.parameter_names import * from cli.common.enums.cloud_providers import CloudProviders from cli.common.enums.dns_registrars import DnsRegistrars @@ -15,8 +18,10 @@ from cli.services.dns.dns_provider_manager import DNSManager from cli.services.dns.route53.route53 import Route53Manager from cli.services.keys.key_manager import KeyManager +from cli.services.tf_wrapper import TfWrapper from cli.services.vcs.git_provider_manager import GitProviderManager from cli.services.vcs.github.github_manager import GitHubProviderManager +from cli.services.vcs.template_manager import GitOpsTemplateManager @click.command() @@ -53,7 +58,7 @@ def setup(email: str, cloud_provider: CloudProviders, cloud_profile: str, cloud_ dns_reg_secret: str, domain: str, git_provider: GitProviders, git_org: str, git_token: str, gitops_repo_name: str, gitops_template_url: str, gitops_template_branch: str, install_demo: bool, config): """Creates new CG DevX installation.""" - click.echo("Setup CG DevX installation.") + click.echo("Setup CG DevX installation...") p: StateStore if config is not None: @@ -91,10 +96,10 @@ def setup(email: str, cloud_provider: CloudProviders, cloud_profile: str, cloud_ # validate parameters p.validate_input_params(validator=setup_param_validator) + # save checkpoint p.save_checkpoint() - click.echo("Executing pre-flight checks") # init proper cloud provider if p.cloud_provider == CloudProviders.AWS: cm: CloudProviderManager = AWSManager(p.get_input_param(CLOUD_REGION), @@ -119,57 +124,265 @@ def setup(email: str, cloud_provider: CloudProviders, cloud_profile: str, cloud_ if p.cloud_provider == CloudProviders.Azure: cm: CloudProviderManager = AzureManager() - cloud_provider_check(cm, p) - click.echo("Cloud provider pre-flight check. Done!") + p.parameters[""] = cm.region # init proper git provider if p.git_provider == GitProviders.GitHub: gm: GitProviderManager = GitHubProviderManager(p.get_input_param(GIT_ACCESS_TOKEN), p.get_input_param(GIT_ORGANIZATION_NAME)) - git_provider_check(gm, p) - click.echo("Git provider pre-flight check. Done!") - # init proper dns registrar provider # Note!: Route53 is initialised with AWS Cloud Provider - dns_provider_check(dm, p) - click.echo("DNS provider pre-flight check. Done!") + if not p.has_checkpoint("preflight"): + click.echo("Executing pre-flight checks...") - # create ssh keys - click.echo("Generating ssh keys") - public_key = KeyManager.create_keys() - click.echo("Generating ssh keys. Done!") + cloud_provider_check(cm, p) + click.echo("Cloud provider pre-flight check. Done!") - # create terraform storage backend - click.echo("Creating tf backend storage") - tf_backend_storage_name: str = f'{p.get_input_param(GITOPS_REPOSITORY_NAME)}-{random_string_generator()}'.lower() - tf_backend_location = cm.create_iac_state_storage(tf_backend_storage_name) - click.echo("Creating tf backend storage. Done!") - # tf_backend_location = 'http://cg-devx-gitops-lf49vhtx.s3.amazonaws.com/' + git_provider_check(gm, p) + click.echo("Git provider pre-flight check. Done!") - p.set_checkpoint("preflight") - p.save_checkpoint() + git_user_login, git_user_name, git_user_email = gm.get_current_user_info() + p.internals["GIT_USER_LOGIN"] = git_user_login + p.internals["GIT_USER_NAME"] = git_user_name + p.internals["GIT_USER_EMAIL"] = git_user_email + p.parameters["# "] = gm.create_tf_module_snippet() + + dns_provider_check(dm, p) + click.echo("DNS provider pre-flight check. Done!") + + # create ssh keys + click.echo("Generating ssh keys...") + default_public_key, public_key_path, private_key_path = KeyManager.create_ed_keys() + p.parameters[""] = default_public_key + p.internals["DEFAULT_SSH_PUBLIC_KEY_PATH"] = public_key_path + p.internals["DEFAULT_SSH_PRIVATE_KEY_PATH"] = private_key_path + + # Optional K8s cluster keys + k8s_public_key, k8s_public_key_path, k8s_private_key_path = KeyManager.create_rsa_keys() + p.parameters[""] = k8s_public_key + p.internals["CLUSTER_SSH_PUBLIC_KEY_PATH"] = k8s_public_key_path + p.internals["CLUSTER_SSH_PRIVATE_KEY_PATH"] = k8s_private_key_path + + click.echo("Generating ssh keys. Done!") - click.echo("Checking dependencies") + # create terraform storage backend + click.echo("Creating tf backend storage...") + tf_backend_storage_name: str = f'{p.get_input_param(GITOPS_REPOSITORY_NAME)}-{random_string_generator()}'.lower() + + tf_backend_location = cm.create_iac_state_storage(tf_backend_storage_name) + + p.parameters["# "] = cm.create_iac_backend_snippet(tf_backend_storage_name, cm.region, + "vcs") + p.parameters["# "] = cm.create_iac_backend_snippet(tf_backend_storage_name, + cm.region, + "hosting_provider") + p.parameters["# "] = cm.create_hosting_provider_snippet() + + p.parameters[""] = cm.create_k8s_rol_binding_snippet() + + click.echo("Creating tf backend storage. Done!") + + p.set_checkpoint("preflight") + p.save_checkpoint() + click.echo("Pre-flight checks. Done!") + + # end preflight check section + else: + click.echo("Skipped pre-flight checks.") dm: DependencyManager = DependencyManager() - # terraform - if dm.check_tf(): - click.echo("tf is installed. Continuing") + if not p.has_checkpoint("dependencies"): + click.echo("Dependencies check...") + + # terraform + if dm.check_tf(): + click.echo("tf is installed. Continuing...") + else: + click.echo("Downloading and installing tf...") + dm.install_tf() + click.echo("tf is installed.") + + # kubectl + if dm.check_kubectl(): + click.echo("kubectl is installed. Continuing...") + else: + click.echo("Downloading and installing kubectl...") + dm.install_kubectl() + click.echo("kubectl is installed.") + + click.echo("Dependencies check. Done!") + p.set_checkpoint("dependencies") + p.save_checkpoint() else: - click.echo("Downloading and installing tf") - dm.install_tf() - click.echo("tf is installed") + click.echo("Skipped dependencies check.") + + # promote input params + # TODO: move to appropriate place + p.parameters[""] = p.get_input_param(OWNER_EMAIL) + p.parameters[""] = p.cloud_provider + p.parameters[""] = p.get_input_param(PRIMARY_CLUSTER_NAME) + p.parameters[""] = p.git_provider + p.parameters[""] = p.get_input_param(GITOPS_REPOSITORY_NAME) + p.parameters[""] = p.get_input_param(GIT_ORGANIZATION_NAME) + p.parameters[""] = p.get_input_param(DOMAIN_NAME) + p.parameters[""] = f'github.com/{p.get_input_param(GIT_ORGANIZATION_NAME)}/*' + + p.parameters[""] = random_string_generator(20) + + # Ingress URLs for core components. Note!: URL does not contain protocol + cluster_fqdn = f'{p.get_input_param(PRIMARY_CLUSTER_NAME)}.{p.get_input_param(DOMAIN_NAME)}' + p.parameters[""] = cluster_fqdn + p.parameters[""] = f'vault.{cluster_fqdn}' + p.parameters[""] = f'argocd.{cluster_fqdn}' + p.parameters[""] = f'argo.{cluster_fqdn}' + p.parameters[""] = f'atlantis.{cluster_fqdn}' + p.parameters[""] = f'harbor.{cluster_fqdn}' + p.parameters[""] = f'grafana.{cluster_fqdn}' + p.parameters[""] = f'sonarqube.{cluster_fqdn}' + + # OIDC config + vault_i = p.parameters[""] + p.parameters[""] = f'{vault_i}/v1/identity/oidc/provider/cgdevx' + p.parameters[""] = f'{vault_i}/ui/vault/identity/oidc/provider/cgdevx/authorize' + p.parameters[""] = f'{vault_i}/v1/identity/oidc/provider/cgdevx/token' + p.parameters[""] = f'{vault_i}/v1/identity/oidc/provider/cgdevx/userinfo' + + p.parameters[""] = f'{p.parameters[""]}/oauth2/callback' + p.parameters[""] = f'{p.parameters[""]}' - # kubectl - if dm.check_kubectl(): - click.echo("kubectl is installed. Continuing") + p.save_checkpoint() + + # params section end + + tm = GitOpsTemplateManager(p.get_input_param(GITOPS_REPOSITORY_TEMPLATE_URL), + p.get_input_param(GITOPS_REPOSITORY_TEMPLATE_BRANCH), + p.get_input_param(GIT_ACCESS_TOKEN)) + + if not p.has_checkpoint("repo-prep"): + click.echo("Preparing your GitOps code...") + + tm.check_repository_existence() + tm.clone() + tm.restructure_template() + tm.parametrise_tf(p.parameters) + + p.set_checkpoint("repo-prep") + p.save_checkpoint() + + click.echo("Preparing your GitOps code. Done!") + else: + click.echo("Skipped GitOps code prep.") + + click.echo("Provisioning VCS...") + # use to enable tf debug + # "TF_LOG": "DEBUG", "TF_LOG_PATH": "/Users/a1m/.cgdevx/gitops/terraform/vcs/terraform.log", + # drop empty values + tf_env_vars = {k: v for k, v in { + "AWS_PROFILE": p.get_input_param(CLOUD_PROFILE), + "AWS_ACCESS_KEY_ID": p.get_input_param(CLOUD_ACCOUNT_ACCESS_KEY), + "AWS_SECRET_ACCESS_KEY": p.get_input_param(CLOUD_ACCOUNT_ACCESS_SECRET), + "AWS_DEFAULT_REGION": p.parameters[""], + }.items() if v} + tf_folder = Path().home() / LOCAL_FOLDER / "gitops" / "terraform" + + # VCS section + if not p.has_checkpoint("vcs-tf"): + # vcs env vars + vcs_tf_env_vars = tf_env_vars | {"GITHUB_TOKEN": p.get_input_param(GIT_ACCESS_TOKEN), + "GITHUB_OWNER": p.get_input_param(GIT_ORGANIZATION_NAME)} + + # set envs as required by tf + for k, vault_i in vcs_tf_env_vars.items(): + os.environ[k] = vault_i + + tf_wrapper = TfWrapper(tf_folder / "vcs") + tf_wrapper.init() + tf_wrapper.apply({"atlantis_repo_webhook_secret": p.parameters[""], + "vcs_bot_ssh_public_key": p.parameters[""]}) + vcs_out = tf_wrapper.output() + + # store out params + p.parameters[""] = vcs_out["gitops_repo_ssh_clone_url"] + + # unset envs as no longer needed + for k in vcs_tf_env_vars.keys(): + os.environ.pop(k) + + p.set_checkpoint("vcs-tf") + p.save_checkpoint() + + click.echo("Provisioning VCS. Done!") + else: + click.echo("Skipped VCS provisioning.") + + # K8s Cluster section + if not p.has_checkpoint("k8s-tf"): + click.echo("Provisioning K8s cluster...") + + # run hosting provider tf to create K8s cluster + hp_tf_env_vars = { + **{}, # add vars here + **tf_env_vars} + # set envs as required by tf + for k, vault_i in hp_tf_env_vars.items(): + os.environ[k] = vault_i + + tf_wrapper = TfWrapper(tf_folder / "hosting_provider") + tf_wrapper.init() + tf_wrapper.apply() + hp_out = tf_wrapper.output() + + # store out params + # network + p.parameters[""] = hp_out["network_id"] + # roles + p.parameters[""] = hp_out["iam_argoworkflow_role"] + p.parameters[""] = hp_out["iam_argoworkflow_role"] # TODO: use own role + p.parameters[""] = hp_out["atlantis_role"] + p.parameters[""] = hp_out["cert_manager_role"] + p.parameters[""] = hp_out["harbor_role"] + p.parameters[""] = hp_out["external_dns_role"] + p.parameters[""] = hp_out["vault_role"] + # cluster + p.parameters[""] = hp_out["cluster_endpoint"] + p.parameters[""] = hp_out["cluster_oidc_provider"] # do we need it? + + # unset envs as no longer needed + for k in hp_tf_env_vars.keys(): + os.environ.pop(k) + + p.set_checkpoint("k8s-tf") + p.save_checkpoint() + + click.echo("Provisioning K8s cluster. Done!") else: - click.echo("Downloading and installing kubectl") - dm.install_kubectl() - click.echo("kubectl is installed") + click.echo("Skipped VCS provisioning.") + + if not p.has_checkpoint("gitops-vcs"): + tm.parametrise_registry(p.parameters) + tm.parametrise_root(p.parameters) + + click.echo("Pushing GitOps code...") + + tm.upload(p.parameters[""], + p.internals["DEFAULT_SSH_PRIVATE_KEY_PATH"], + p.internals["GIT_USER_NAME"], + p.internals["GIT_USER_EMAIL"]) + + click.echo("Pushing GitOps code. Done!") + + p.set_checkpoint("gitops-vcs") + p.save_checkpoint() + else: + click.echo("Skipped GitOps repo initialization.") + + # user could get kubeconfig by running command + # `aws eks update-kubeconfig --region region-code --name my-cluster --kubeconfig my-config-path` + # CLI could not follow this approach as aws client could be not configured properly when keys are used + # CLI is creating this file programmatically return True diff --git a/cli/common/const/const.py b/cli/common/const/const.py index 0d9b04be..0325a24c 100644 --- a/cli/common/const/const.py +++ b/cli/common/const/const.py @@ -1,6 +1,10 @@ DEFAULT_ENUM_VALUE = 'unknown' -GITOPS_REPOSITORY_URL = 'https://github.com/CloudGeometry/cg-devx-core.git' +GITOPS_REPOSITORY_URL = 'https://github.com/CloudGeometry/cgdevx-core.git' GITOPS_REPOSITORY_BRANCH = 'main' STATE_INPUT_PARAMS = 'input' +STATE_INTERNAL_PARAMS = 'internal' +STATE_PARAMS = 'params' STATE_CHECKPOINTS = 'checkpoints' LOCAL_FOLDER = '.cgdevx' +FALLBACK_AUTHOR_NAME = 'cgdevx-bot' +FALLBACK_AUTHOR_EMAIL = 'cg-devx-automation@cloudgeometry.io' diff --git a/cli/common/enums/state.py b/cli/common/enums/state.py deleted file mode 100644 index 6ac6e394..00000000 --- a/cli/common/enums/state.py +++ /dev/null @@ -1,37 +0,0 @@ -"""State enums.""" -import enum - -UNKNOWN_STATE = 'unknown' - - -class State(str, enum.Enum): - """List of possible states.""" - - running = 'running' - stopping = 'stopping' - stopped = 'stopped' - terminating = 'terminating' - terminated = 'terminated' - default = 'provisioning' - - @classmethod - def has_value(cls, value) -> bool: - """ - Check if value defined. - - :param value: Value - :return: True or False - """ - return value in cls._value2member_map_ - - @classmethod - def can_ignore(cls, value): - """ - Check if the state passed from the request is 'unknown' and event can be ignored. - - :param value: str - :return: return True if the state is unknown, otherwise return False. - """ - if value == UNKNOWN_STATE: - return True - return False diff --git a/cli/common/state_store.py b/cli/common/state_store.py index bae830f7..07a7ecd2 100644 --- a/cli/common/state_store.py +++ b/cli/common/state_store.py @@ -5,7 +5,8 @@ import yaml -from cli.common.const.const import STATE_INPUT_PARAMS, LOCAL_FOLDER, STATE_CHECKPOINTS +from cli.common.const.const import STATE_INPUT_PARAMS, LOCAL_FOLDER, STATE_CHECKPOINTS, STATE_INTERNAL_PARAMS, \ + STATE_PARAMS from cli.common.const.parameter_names import CLOUD_PROVIDER, GIT_PROVIDER, DNS_REGISTRAR from cli.common.enums.cloud_providers import CloudProviders from cli.common.enums.dns_registrars import DnsRegistrars @@ -16,8 +17,21 @@ class StateStore: __store: dict = {} def __init__(self, input_params={}): - self.__store[STATE_INPUT_PARAMS] = input_params self.__store[STATE_CHECKPOINTS] = [] + self.__store[STATE_PARAMS] = {} + self.__store[STATE_INTERNAL_PARAMS] = {} + self.__store[STATE_INPUT_PARAMS] = {} + + file_path = Path().home() / LOCAL_FOLDER / "state.yaml" + if os.path.exists(file_path): + with open(file_path, "r+") as infile: + config = yaml.safe_load(infile) + self.__store[STATE_CHECKPOINTS] = config[STATE_CHECKPOINTS] + self.__store[STATE_PARAMS] = config[STATE_PARAMS] + self.__store[STATE_INTERNAL_PARAMS] = config[STATE_INTERNAL_PARAMS] + self.__store[STATE_INPUT_PARAMS] = config[STATE_INPUT_PARAMS] + + self.__store[STATE_INPUT_PARAMS].update(input_params) @property def cloud_provider(self) -> CloudProviders: @@ -42,24 +56,44 @@ def get_input_param(self, key): else: return None - def __getitem__(self, key: str) -> Any: - return self.__store[STATE_INPUT_PARAMS].__getitem__(key) - - def __setitem__(self, key: str, value) -> None: - return self.__store[STATE_INPUT_PARAMS].__setitem__(key, [value]) - @classmethod def update_input_params(self, input_params: dict): self.__store[STATE_INPUT_PARAMS].update(input_params) + @property + def input_param(self): + return self.__store[STATE_INPUT_PARAMS] + @classmethod def validate_input_params(self, validator): return validator(self) + # def __getitem__(self, key: str) -> Any: + # return self.__store[STATE_INPUT_PARAMS].__getitem__(key) + # + # def __setitem__(self, key: str, value) -> None: + # return self.__store[STATE_INPUT_PARAMS].__setitem__(key, [value]) + + @property + def parameters(self): + return self.__store[STATE_PARAMS] + + @property + def internals(self): + return self.__store[STATE_INTERNAL_PARAMS] + + @classmethod + def set_parameter(self, key, value): + self.__store[STATE_INTERNAL_PARAMS][key] = value + @classmethod def set_checkpoint(self, name: str): self.__store[STATE_CHECKPOINTS].append(name) + @classmethod + def has_checkpoint(self, name: str): + return name in self.__store[STATE_CHECKPOINTS] + @classmethod def save_checkpoint(self): file_path = Path().home() / LOCAL_FOLDER / "state.yaml" diff --git a/cli/requirements.txt b/cli/requirements.txt index ee3f8008..6490771a 100644 --- a/cli/requirements.txt +++ b/cli/requirements.txt @@ -1,5 +1,5 @@ click==8.1.7 -cloup==3.0.0 +cloup==3.0.2 configparser==6.0.0 pyyaml==6.0.1 flake8==6.1.0 @@ -7,12 +7,13 @@ flake8-class-attributes-order==0.1.3 flake8-docstrings==1.7.0 pydocstyle==6.3.0 kubernetes==27.2.0 -boto3==1.28.29 +boto3==1.28.44 azure-identity==1.14.0 azure-storage-blob==12.17.0 azure-mgmt-resource==23.0.1 gcloud==0.18.3 -GitPython==3.1.32 +GitPython==3.1.35 dnspython==2.4.2 -httpx==0.24.1 -python-terraform==0.10.1 \ No newline at end of file +httpx==0.25.0 +python-terraform==0.10.1 +ghrepo==0.7.0 \ No newline at end of file diff --git a/cli/services/cloud/aws/aws_manager.py b/cli/services/cloud/aws/aws_manager.py index 1902315a..f6c64722 100644 --- a/cli/services/cloud/aws/aws_manager.py +++ b/cli/services/cloud/aws/aws_manager.py @@ -1,3 +1,5 @@ +import textwrap + from cli.common.utils.os_utils import detect_command_presence from cli.services.cloud.aws.aws_sdk import AwsSdk from cli.services.cloud.aws.iam_permissions import vpc_permissions, eks_permissions, s3_permissions, \ @@ -13,16 +15,48 @@ class AWSManager(CloudProviderManager): def __init__(self, region, profile, key, secret): self.__aws_sdk = AwsSdk(region, profile, key, secret) + @property + def region(self): + return self.__aws_sdk.region + def detect_cli_presence(self) -> bool: """Check whether `name` is on PATH and marked as executable.""" return detect_command_presence(CLI) - def create_iac_state_storage(self, bucket: str, region: str = None): + def create_iac_state_storage(self, bucket: str): """ Creates cloud native terraform remote state storage - :return: Resource identifier + :return: Resource identifier, Region """ - return self.__aws_sdk.create_bucket(bucket, region) + return self.__aws_sdk.create_bucket(bucket) + + def create_iac_backend_snippet(self, location: str, region: str, service="default"): + if region is None: + region = self.region + # TODO: consider replacing with file template + return textwrap.dedent('''\ + backend "s3" {{ + bucket = "{bucket}" + key = "terraform/{service}/terraform.tfstate" + region = "{region}" + encrypt = true + }}'''.format(bucket=location, region=region, service=service)) + + def create_hosting_provider_snippet(self): + # TODO: consider replacing with file template + return textwrap.dedent('''\ + provider "aws" { + default_tags { + tags = { + ClusterName = local.name + ProvisionedBy = local.ProvisionedBy + } + } + }''') + + def create_k8s_rol_binding_snippet(self): + # TODO: consider replacing with file template + return "serviceAccountName" def evaluate_permissions(self) -> bool: """ diff --git a/cli/services/cloud/aws/aws_sdk.py b/cli/services/cloud/aws/aws_sdk.py index ca03d17d..dfc1fd52 100644 --- a/cli/services/cloud/aws/aws_sdk.py +++ b/cli/services/cloud/aws/aws_sdk.py @@ -13,6 +13,10 @@ def __init__(self, region, profile, key, secret): self._session_manager = AwsSessionManager() self._session_manager.create_session(region, profile, key, secret) + @property + def region(self): + return self._session_manager.session.region_name + def current_user_arn(self): """Autodetect current user ARN. Method doesn't work with STS/assumed roles @@ -84,7 +88,7 @@ def create_bucket(self, bucket_name, region=None): # Create bucket try: if region is None: - region = self._session_manager.session.region_name + region = self.region s3_client = self._session_manager.session.client('s3', region_name=region) location = {'LocationConstraint': region} @@ -93,7 +97,7 @@ def create_bucket(self, bucket_name, region=None): except ClientError as e: logging.error(e) return False - return bucket["Location"] + return bucket_name, region def get_name_severs(self, domain_name: str) -> [str]: r53_client = self._session_manager.session.client('route53') diff --git a/cli/services/cloud/cloud_provider_manager.py b/cli/services/cloud/cloud_provider_manager.py index 1fb7d1b6..a58a81db 100644 --- a/cli/services/cloud/cloud_provider_manager.py +++ b/cli/services/cloud/cloud_provider_manager.py @@ -15,9 +15,30 @@ def evaluate_permissions(self) -> bool: """ pass - def create_iac_state_storage(self, bucket: str, region: str = None): + def create_iac_state_storage(self, bucket: str): """ Creates cloud native terraform remote state storage :return: Resource identifier """ pass + + def create_iac_backend_snippet(self, location: str, region: str = None): + """ + Creates terraform backend snippet + :return: Code snippet + """ + pass + + def create_hosting_provider_snippet(self, location: str, region: str = None): + """ + Creates terraform hosting provider snippet + :return: Code snippet + """ + pass + + def create_k8s_rol_binding_snippet(self): + """ + Creates service account - IAM role binding snippet + :return: Snippet + """ + pass diff --git a/cli/services/dependency_manager.py b/cli/services/dependency_manager.py index 394d55d2..d22ae924 100644 --- a/cli/services/dependency_manager.py +++ b/cli/services/dependency_manager.py @@ -13,6 +13,7 @@ from cli.common.const.const import LOCAL_FOLDER from cli.common.utils.generators import random_string_generator +from cli.services.tf_wrapper import TfWrapper class DependencyManager: @@ -84,18 +85,15 @@ def _validate_checksum(self, file: str, checksum: str): def check_tf(self): tf_executable = Path().home() / LOCAL_FOLDER / "tools" / "terraform" if os.path.exists(tf_executable): - t = Terraform(terraform_bin_path=str(tf_executable)) - return_code, stdout, stderr = t.version("-json") - version = json.loads(stdout) - if return_code == 0 and version["terraform_version"] == self.tf_version: + tf = TfWrapper() + if tf.version() == self.tf_version: return True - else: - return False - else: - return False + + return False def check_kubectl(self): kctl_executable = Path().home() / LOCAL_FOLDER / "tools" / "kubectl" + # TODO: extract and check kubectl version if os.path.exists(kctl_executable): return True else: diff --git a/cli/services/kctl_wrapper.py b/cli/services/kctl_wrapper.py new file mode 100644 index 00000000..cb9b02a1 --- /dev/null +++ b/cli/services/kctl_wrapper.py @@ -0,0 +1,48 @@ +import subprocess +from pathlib import Path + +import yaml + +from cli.common.const.const import LOCAL_FOLDER + + +class KctlWrapper: + @staticmethod + def __kubectl_command_builder(basic_command: str, resource=None, name=None, container_name=None, + namespace=None, flags=None, with_definition=False): + + kctl_executable = str(Path().home() / LOCAL_FOLDER / "tools" / "kubectl") + command = [kctl_executable, basic_command] + if resource: + command.append(resource) + if name: + command.append(name) + if container_name: + command.extend(['-c', container_name]) + if namespace: + command.extend(['--namespace', namespace]) + if flags: + command.extend(flags) + if with_definition: + command.extend(['-f', '-']) + return command + + @staticmethod + def __run_command(command, *args, **kwargs): + proc = subprocess.Popen(command, stdin=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = proc.communicate(yaml.dump(kwargs).encode()) + proc.stdin.close() + if proc.wait() != 0: + raise Exception(stderr) + + def run(self, command: str, *args, **kwargs): + command = self.__kubectl_command_builder(command, *args, **kwargs) + self.__run_command(command, *args, **kwargs) + + def create(self, definition, namespace=None): + command = self.__kubectl_command_builder('create', namespace=namespace, with_definition=True) + self.__run_command(command, definition) + + def apply(self, definition, namespace=None): + command = self.__kubectl_command_builder('apply', namespace=namespace, flags=['--record'], with_definition=True) + self.__run_command(command, definition) diff --git a/cli/services/keys/key_manager.py b/cli/services/keys/key_manager.py index f667e3d7..a94d7bc9 100644 --- a/cli/services/keys/key_manager.py +++ b/cli/services/keys/key_manager.py @@ -1,8 +1,11 @@ +import os +import stat from pathlib import Path from cryptography.hazmat.backends import default_backend as crypto_default_backend from cryptography.hazmat.primitives import serialization as crypto_serialization -from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric import rsa, ed25519 +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cli.common.const.const import LOCAL_FOLDER @@ -11,7 +14,7 @@ class KeyManager: """Cryptographic key management wrapper to standardise key management.""" @staticmethod - def create_keys(key_name: str = "cgdevx-rsa"): + def create_rsa_keys(key_name: str = "k8s_cgdevx_rsa"): """ Create keypair :return: @@ -23,22 +26,38 @@ def create_keys(key_name: str = "cgdevx-rsa"): key_size=2048 ) + return KeyManager._process_key(key, key_name) + + @staticmethod + def create_ed_keys(key_name: str = "cgdevx_ed"): + """ + Create keypair + :return: + """ + + key = Ed25519PrivateKey.generate() + + return KeyManager._process_key(key, key_name) + + @staticmethod + def _process_key(key, key_name): private_key = key.private_bytes( encoding=crypto_serialization.Encoding.PEM, - format=crypto_serialization.PrivateFormat.PKCS8, + format=crypto_serialization.PrivateFormat.OpenSSH, encryption_algorithm=crypto_serialization.NoEncryption() ) - public_key = key.public_key().public_bytes( encoding=crypto_serialization.Encoding.OpenSSH, format=crypto_serialization.PublicFormat.OpenSSH ) - - with open(Path().home() / LOCAL_FOLDER / f'{key_name}.pem', "w") as private_key_file: + private_key_path = Path().home() / LOCAL_FOLDER / f'{key_name}' + public_key_path = Path().home() / LOCAL_FOLDER / f'{key_name}.pub' + with open(private_key_path, "w") as private_key_file: private_key_file.write(private_key.decode()) - pk = public_key.decode() - with open(Path().home() / LOCAL_FOLDER / f'{key_name}.pub', "w") as public_key_file: + with open(public_key_path, "w") as public_key_file: public_key_file.write(pk) - return pk + os.chmod(private_key_path, stat.S_IRUSR | stat.S_IWUSR) + + return pk, str(public_key_path), str(private_key_path) diff --git a/cli/services/tf_wrapper.py b/cli/services/tf_wrapper.py new file mode 100644 index 00000000..c3666341 --- /dev/null +++ b/cli/services/tf_wrapper.py @@ -0,0 +1,63 @@ +import json +import os +from pathlib import Path + +from python_terraform import Terraform, IsFlagged, IsNotFlagged + +from cli.common.const.const import LOCAL_FOLDER + + +class TfWrapper: + + def __init__(self, working_dir: str = None): + tf_executable = Path().home() / LOCAL_FOLDER / "tools" / "terraform" + if os.path.exists(tf_executable): + self._tf = Terraform(terraform_bin_path=str(tf_executable), + working_dir=working_dir, + is_env_vars_included=True) + else: + raise Exception("No tf executable found") + + def change_working_dir(self, working_dir: str): + self._tf.working_dir = working_dir + + def version(self): + return_code, stdout, stderr = self._tf.version("-json") + output = json.loads(stdout) + if return_code != 0: + raise Exception("tf executable failure", return_code, stderr) + return output["terraform_version"] + + def init(self): + return_code, stdout, stderr = self._tf.init() + + if return_code != 0: + raise Exception("tf executable failure", return_code, stderr) + + return True + + def apply(self, variables: dict = None): + self._tf.variables = variables + + # TODO: set capture_output=False and rewire tf logs + return_code, stdout, stderr = self._tf.apply(skip_plan=True, input=False) + + if return_code != 0: + raise Exception("tf executable failure", return_code, stderr) + + return True + + def output(self): + output = {} + tf_output = self._tf.output() + for k, v in tf_output.items(): + output[k] = v["value"] + return output + + def destroy(self): + return_code, stdout, stderr = self._tf.destroy(force=IsNotFlagged, auto_approve=IsFlagged) + + if return_code != 0: + raise Exception("tf executable failure", return_code, stderr) + + return True diff --git a/cli/services/vcs/git_provider_manager.py b/cli/services/vcs/git_provider_manager.py index 89a06f7e..2e4face7 100644 --- a/cli/services/vcs/git_provider_manager.py +++ b/cli/services/vcs/git_provider_manager.py @@ -12,5 +12,19 @@ def evaluate_permissions(self): """ Check if provided credentials have required permissions :return: True or False - """ + """ + pass + + def create_tf_module_snippet(self): + """ + Create tf git provider snippet + :return: Snippet + """ + pass + + def get_current_user_info(self): + """ + Get authenticated user info + :return: Login, Name, Email + """ pass diff --git a/cli/services/vcs/github/github_manager.py b/cli/services/vcs/github/github_manager.py index 0300092d..16414554 100644 --- a/cli/services/vcs/github/github_manager.py +++ b/cli/services/vcs/github/github_manager.py @@ -1,7 +1,9 @@ +import json from urllib.error import HTTPError import requests +from cli.common.const.const import FALLBACK_AUTHOR_NAME, FALLBACK_AUTHOR_EMAIL from cli.services.vcs.git_provider_manager import GitProviderManager @@ -19,6 +21,8 @@ def check_repository_existence(self, name: str = "GitOps"): """ headers = { 'Authorization': f'token {self.__token}', + 'Accept': 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', } try: response = requests.get(f'https://api.github.com/repos/{self.__org_name}/{name}', headers=headers) @@ -36,6 +40,8 @@ def evaluate_permissions(self): """ headers = { 'Authorization': f'token {self.__token}', + 'Accept': 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', } try: response = requests.head('https://api.github.com', headers=headers) @@ -53,12 +59,32 @@ def evaluate_permissions(self): except HTTPError as e: return False - def _clone(self): - pass - # https://gitpython.readthedocs.io/en/stable/tutorial.html#tutorial-label - # try: - # git.Repo.clone_from(f'https://null:null@github.com/{user.git_user}/{user.git_repos}.git', - # f'temp/{user.name}/') - # except git.exc.GitError: - # print(f'ERROR! {user.name}: {user.git_user}/{user.git_repos} does not exist') - # repo = git.Repo.clone_from(self._small_repo_url(), os.path.join(rw_dir, "repo"), branch="master") + def get_current_user_info(self): + """ + Get authenticated user info + :return: Login, Name, Email + """ + headers = { + 'Authorization': f'token {self.__token}', + 'Accept': 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', + } + try: + response = requests.get('https://api.github.com/user', headers=headers) + res = json.loads(response.text) + + if res["name"] is None: + name = FALLBACK_AUTHOR_NAME + else: + name = res["name"] + + if res["email"] is None: + email = FALLBACK_AUTHOR_EMAIL + else: + email = res["email"] + return res["login"], name, email + except HTTPError as e: + raise e + + def create_tf_module_snippet(self): + return 'provider "github" {}' diff --git a/cli/services/vcs/template_manager.py b/cli/services/vcs/template_manager.py index 25e066aa..3344e833 100644 --- a/cli/services/vcs/template_manager.py +++ b/cli/services/vcs/template_manager.py @@ -1,9 +1,157 @@ -class TemplateManager: +import os +import shutil +from pathlib import Path +from urllib.error import HTTPError + +import requests +from ghrepo import GHRepo +from git import Repo, RemoteProgress, GitError, Actor + +from cli.common.const.const import LOCAL_FOLDER, GITOPS_REPOSITORY_URL, GITOPS_REPOSITORY_BRANCH +import logging + +logging.basicConfig(level=logging.INFO) + + +class GitOpsTemplateManager: """CG DevX Git repo templates manager.""" - def check_repository_existence(self, url: str, branch: str = "main"): + def __init__(self, gitops_template_url: str, gitops_template_branch: str, token=None): + if gitops_template_url is None: + self.__url = GITOPS_REPOSITORY_URL + else: + self.__url = gitops_template_url + + if gitops_template_branch is None: + self.__branch = GITOPS_REPOSITORY_BRANCH + else: + self.__branch = gitops_template_branch + + # TODO: DEBUG, remove + self.__token = token + + def check_repository_existence(self): """ Check if repository exists :return: True or False """ + repo = GHRepo.parse(self.__url) + headers = {} + if self.__token is not None: + headers['Authorization'] = f'token {self.__token}' + + try: + response = requests.get(f'https://api.github.com/repos/{repo.owner}/{repo.name}/branches/{self.__branch}', + headers=headers) + if response.status_code == requests.codes["not_found"]: + return False + elif response.ok: + return True + except HTTPError as e: + raise e + + def clone(self): + gitops_folder = Path().home() / LOCAL_FOLDER / "gitops" + if os.path.exists(gitops_folder): + shutil.rmtree(gitops_folder) + os.makedirs(gitops_folder) + try: + repo = Repo.clone_from(self.__url, gitops_folder, progress=ProgressPrinter(), branch=self.__branch) + except GitError as e: + raise e + + def upload(self, path: str, key_path: str, git_user_name: str, git_user_email: str): + gitops_folder = Path().home() / LOCAL_FOLDER / "gitops" + if not os.path.exists(gitops_folder): + raise Exception("GitOps repo does not exist") + + try: + ssh_cmd = f'ssh -o StrictHostKeyChecking=no -i {key_path}' + + repo = Repo.init(gitops_folder) + + with repo.git.custom_environment(GIT_SSH_COMMAND=ssh_cmd): + if not any(repo.remotes): + origin = repo.create_remote(name='origin', url=path) + + repo.git.add(all=True) + author = Actor(name=git_user_name, email=git_user_email) + repo.index.commit("initial", author=author, committer=author) + + repo.remotes.origin.push(repo.active_branch.name) + + except GitError as e: + raise e + + def restructure_template(self): + gitops_folder = Path().home() / LOCAL_FOLDER / "gitops" + # remove git reference + shutil.rmtree(gitops_folder / ".git", ignore_errors=True) + # remove cli + shutil.rmtree(gitops_folder / "cli", ignore_errors=True) + # restructure gitops + shutil.move(gitops_folder / "platform" / "terraform", gitops_folder / "terraform") + shutil.move(gitops_folder / "platform" / "gitops-pipelines", gitops_folder / "gitops-pipelines") + for src_file in Path(gitops_folder / "platform").glob('*.*'): + # workaround for mac, should not happen + if src_file.name.endswith(".DS_Store"): + continue + shutil.move(src_file, gitops_folder) + shutil.rmtree(gitops_folder / "platform") + + # drop all non template readme files + for root, dirs, files in os.walk(gitops_folder): + for name in files: + if name.endswith(".md") and not name.startswith("tpl_"): + os.remove(os.path.join(root, name)) + + # rename readme file templates + for root, dirs, files in os.walk(gitops_folder): + for name in files: + if name.startswith("tpl_") and name.endswith(".md"): + s = os.path.join(root, name) + os.rename(s, s.replace("tpl_", "")) + + def parametrise_tf(self, params: dict): + tf_folder = Path().home() / LOCAL_FOLDER / "gitops" / "terraform" + + self.__file_replace(params, tf_folder) + + def parametrise_registry(self, params: dict): + pipelines_folder = Path().home() / LOCAL_FOLDER / "gitops" / "gitops-pipelines" + + self.__file_replace(params, pipelines_folder) + + def parametrise_root(self, params: dict): + for src_file in Path(Path().home() / LOCAL_FOLDER / "gitops").glob('*.md'): + with open(src_file, "r") as file: + data = file.read() + for k, v in params.items(): + data = data.replace(k, v) + with open(src_file, "w") as file: + file.write(data) + + @staticmethod + def __file_replace(params, folder): + try: + for root, dirs, files in os.walk(folder): + for name in files: + file_path = os.path.join(root, name) + with open(file_path, "r") as file: + data = file.read() + for k, v in params.items(): + data = data.replace(k, v) + with open(file_path, "w") as file: + file.write(data) + except Exception as e: + raise e + # tf_executable = Path().home() / LOCAL_FOLDER / "tools" / "terraform" + # if os.path.exists(tf_executable): + # t = Terraform(terraform_bin_path=str(tf_executable)) + # t.replace("hello world", "/w.*d/", "everybody") + + +class ProgressPrinter(RemoteProgress): + def update(self, op_code, cur_count, max_count=None, message=""): + # TODO: forward to CLI (Click) progress bar pass diff --git a/cli/tests/parameters.yaml b/cli/tests/parameters.yaml index 2c0f0b95..0662bc36 100644 --- a/cli/tests/parameters.yaml +++ b/cli/tests/parameters.yaml @@ -1,12 +1,12 @@ email: cg-devx-demo@cloudgeometry.io cloud-provider: aws cloud-profile: cg-develop -cloud-region: eu-central-1 -cluster-name: cg-devx-demo +cloud-region: eu-west-1 +cluster-name: cg-devx-demo-cli dns-registrar: route53 domain-name: cgdevx-demo.demoapps.click git-provider: github -git-org: cloudgeometry -git-access-token: gh_xxx -gitops-repo-name: cg-devx-gitops +git-org: CGDevX-Demo +git-access-token: ghp_xxx +gitops-repo-name: cg-devx-gitops-test setup-demo-workload: False \ No newline at end of file diff --git a/platform/README.md b/platform/README.md deleted file mode 100644 index 9fc399b5..00000000 --- a/platform/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# platform - -The `gitops` repository has 2 main sections - -- `/gitops_pipelines`: delivery pipeline configurations -- `/terraform`: infrastructure as code & configuration as code for all the cloud services, git provider, secrets and user management - -## CG DevX services - -The CG DevX services: - -| Application | Namespace | Description | URL (where applicable) | -|--------------------------| ---------------- |---------------------------------------------|-----------------------------| -| | N/A | | | -| Vault | vault | Secrets Management | | -| Argo CD | argocd | GitOps Continuous Delivery | | -| Argo Workflows | argo | Application Continuous Integration | | -| Atlantis | atlantis | Terraform Workflow Automation | | -| Harbor | harbor | Image & Helm Chart Registry | | -| Grafana | monitoring | Observability | | -| SonarQube | sonarqube | Code Quality | | - ---- - -## gitops registry - -The argocd configurations in this repo can be found in the [registry directory](./registry). The applications that we build and release on the cgdevx platform will also be registered here in the development, staging, and production folders. - -The `main` branch's registry directory represents the gitops desired state for all apps registered with kubernetes. Argo CD will automatically apply your desired state to kubernetes through. You can see the Sync status of all of your apps in [argo cd](https://argocd.cgdevx-demo.demoapps.click). - -## terraform infrastructure as code - -The terraform in this repository can be found in the [terraform directory](./terraform). It has entry points for management of cloud resources, vault configurations, git provider configurations, and user management. - -All of our terraform is automated with a tool called atlantis that integrates with your git pull requests. To see the terraform entry points and under what circumstance they are triggered, see [atlantis.yaml](./atlantis.yaml). - -Any change to a `*.tf` file, even a whitespace change, will trigger its corresponding Atlantis workflow once a pull request is submitted. Within a minute it will post the plan to the pull request with instruction on how to apply the plan if approved. \ No newline at end of file diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/clusterrolebinding.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/0-clusterrolebinding.yaml similarity index 100% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/clusterrolebinding.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/0-clusterrolebinding.yaml diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/registry.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/0-registry.yaml similarity index 88% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/registry.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/0-registry.yaml index 9b3c657a..08388a9e 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/registry.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/0-registry.yaml @@ -9,7 +9,7 @@ spec: project: core source: repoURL: - path: registry/ + path: gitops-pipelines/delivery/clusters/cc-cluster/core-services targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/cert-manager.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/10-cert-manager.yaml similarity index 80% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/cert-manager.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/10-cert-manager.yaml index d50c4ab5..1ce66c0a 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/cert-manager.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/10-cert-manager.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/cert-manager + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/cert-manager targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/external-dns.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/10-external-dns.yaml similarity index 80% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/external-dns.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/10-external-dns.yaml index a0bd1d97..303322c1 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/external-dns.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/10-external-dns.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/external-dns + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/external-dns targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/argocd.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/100-argocd.yaml similarity index 83% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/argocd.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/100-argocd.yaml index ad39d17f..107260ae 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/argocd.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/100-argocd.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/argocd + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/argocd targetRevision: HEAD destination: server: 'https://kubernetes.default.svc' diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/monitoring.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/105-monitoring.yaml similarity index 82% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/monitoring.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/105-monitoring.yaml index 39ec5d1a..bb37cd4a 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/monitoring.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/105-monitoring.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/monitoring + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/monitoring targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/kubecost.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/130-kubecost.yaml similarity index 82% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/kubecost.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/130-kubecost.yaml index bc16db56..07771e9a 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/kubecost.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/130-kubecost.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kubecost + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kubecost targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/trivy.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/130-trivy.yaml similarity index 81% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/trivy.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/130-trivy.yaml index 2f6de477..a9894d0a 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/trivy.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/130-trivy.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/devsecops/trivy + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/devsecops/trivy targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/tracee.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/140-tracee.yaml similarity index 80% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/tracee.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/140-tracee.yaml index fd7cfd53..fc451684 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/tracee.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/140-tracee.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/devsecops/tracee + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/devsecops/tracee targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/kyverno.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/160-kyverno.yaml similarity index 80% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/kyverno.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/160-kyverno.yaml index 03aff4e1..11540e7d 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/kyverno.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/160-kyverno.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/devsecops/kyverno + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/devsecops/kyverno targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/cert-issuers.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/20-cert-issuers.yaml similarity index 79% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/cert-issuers.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/20-cert-issuers.yaml index e54375a4..3fc8bdc2 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/cert-issuers.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/20-cert-issuers.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/cert-issuers + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/cert-issuers targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/vault.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/20-vault.yaml similarity index 80% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/vault.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/20-vault.yaml index 02021079..cdb47a2a 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/vault.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/20-vault.yaml @@ -9,7 +9,7 @@ spec: project: core source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/components/vault + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/vault targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/external-secrets-operator.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/30-external-secrets-operator.yaml similarity index 79% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/external-secrets-operator.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/30-external-secrets-operator.yaml index 85e106c0..98ce3f18 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/external-secrets-operator.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/30-external-secrets-operator.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/external-secrets-operator + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/external-secrets-operator targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/cluster-secret-store.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/40-cluster-secret-store.yaml similarity index 82% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/cluster-secret-store.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/40-cluster-secret-store.yaml index 8d062d8c..f91825bd 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/cluster-secret-store.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/40-cluster-secret-store.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/cluster-secret-store + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/cluster-secret-store targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/actions-runner-controller.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-actions-runner-controller.yaml similarity index 84% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/actions-runner-controller.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-actions-runner-controller.yaml index bce1ffed..bc4b35ce 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/actions-runner-controller.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-actions-runner-controller.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/actions-runner-controller + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/actions-runner-controller targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/argo-workflows.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-argo-workflows.yaml similarity index 84% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/argo-workflows.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-argo-workflows.yaml index 72a16874..7eb348b8 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/argo-workflows.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-argo-workflows.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/argo-workflows + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/argo-workflows targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/atlantis.yml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-atlantis.yml similarity index 82% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/atlantis.yml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-atlantis.yml index 36411e80..ef77d945 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/atlantis.yml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-atlantis.yml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/atlantis + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/atlantis targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/reloader.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-reloader.yaml similarity index 81% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/reloader.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-reloader.yaml index 39f09d5a..84e967b7 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/reloader.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/50-reloader.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/reloader + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/kube-system/reloader targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/github-runner.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/60-github-runner.yaml similarity index 86% rename from platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/github-runner.yaml rename to platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/60-github-runner.yaml index a4f2e067..1bb976fa 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/github-runner.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/60-github-runner.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/github-runner + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/github-runner targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/components/argo-workflows/application.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/components/argo-workflows/application.yaml index f2942dec..5f894109 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/components/argo-workflows/application.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/components/argo-workflows/application.yaml @@ -26,7 +26,7 @@ spec: create: true name: argo-server annotations: - ' + : '' extraArgs: - --secure - --auth-mode=client diff --git a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/components/argo-workflows/argo-workflows-cwfts.yaml b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/components/argo-workflows/argo-workflows-cwfts.yaml index 6ef85a7a..a3db24b5 100644 --- a/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/components/argo-workflows/argo-workflows-cwfts.yaml +++ b/platform/gitops-pipelines/delivery/clusters/cc-cluster/core-services/components/argo-workflows/argo-workflows-cwfts.yaml @@ -9,7 +9,7 @@ spec: project: default source: repoURL: - path: platform/gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/argo-workflows/cluster-workflow-templates + path: gitops_pipelines/delivery/clusters/cc_cluster/core_services/components/argo-workflows/cluster-workflow-templates targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/delivery/clusters/worker-cluster/core-services/registry.yaml b/platform/gitops-pipelines/delivery/clusters/worker-cluster/core-services/0-registry.yaml similarity index 88% rename from platform/gitops-pipelines/delivery/clusters/worker-cluster/core-services/registry.yaml rename to platform/gitops-pipelines/delivery/clusters/worker-cluster/core-services/0-registry.yaml index 1da32702..34fec175 100644 --- a/platform/gitops-pipelines/delivery/clusters/worker-cluster/core-services/registry.yaml +++ b/platform/gitops-pipelines/delivery/clusters/worker-cluster/core-services/0-registry.yaml @@ -9,7 +9,7 @@ spec: project: source: repoURL: - path: registry/ + path: gitops-pipelines/delivery/clusters/worker-cluster/core-services targetRevision: HEAD destination: server: https://kubernetes.default.svc diff --git a/platform/gitops-pipelines/README.md b/platform/gitops-pipelines/tpl_README.md similarity index 100% rename from platform/gitops-pipelines/README.md rename to platform/gitops-pipelines/tpl_README.md diff --git a/platform/terraform/hosting_provider/main.tf b/platform/terraform/hosting_provider/main.tf index 8c55fbcc..9215b6c7 100644 --- a/platform/terraform/hosting_provider/main.tf +++ b/platform/terraform/hosting_provider/main.tf @@ -1,30 +1,23 @@ terraform { - -#Placeholder for remote backend configuration + # Remote backend configuration + # } locals { - name = "" - ProvisionedBy = "cgdevx" + name = "" + ProvisionedBy = "cgdevx" + region = "" + email = [""] } -# configure cloud provider through env variables -# AWS_REGION and AWS_PROFILE for local run and through assuming IAM role in CI runner -# so, for local run required: -# export AWS_REGION="" -# export AWS_PROFILE="" -provider "aws" { - default_tags { - tags = { - ClusterName = local.name - ProvisionedBy = local.ProvisionedBy - } - } -} +# Provider configuration +# module "hosting-provider" { - source = "../modules/cloud_" - cluster_name = local.name + source = "../modules/cloud_" + cluster_name = local.name + region = local.region + alert_emails = local.email } diff --git a/platform/terraform/hosting_provider/output.tf b/platform/terraform/hosting_provider/output.tf new file mode 100644 index 00000000..9a17495c --- /dev/null +++ b/platform/terraform/hosting_provider/output.tf @@ -0,0 +1,32 @@ +# network +output "network_id" { + value = module.hosting-provider.network_id +} + +# IAM roles +output "iam_argoworkflow_role" { + value = module.hosting-provider.iam_argoworkflow_role +} +output "atlantis_role" { + value = module.hosting-provider.atlantis_irsa_role +} +output "cert_manager_role" { + value = module.hosting-provider.cert_manager_irsa_role +} +output "harbor_role" { + value = module.hosting-provider.harbor_irsa_role +} +output "external_dns_role" { + value = module.hosting-provider.external_dns_irsa_role +} +output "vault_role" { + value = module.hosting-provider.vault_irsa_role +} + +# cluster +output "cluster_endpoint" { + value = module.hosting-provider.cluster_endpoint +} +output "cluster_oidc_provider" { + value = module.hosting-provider.oidc_provider +} \ No newline at end of file diff --git a/platform/terraform/modules/cloud_aws/TERRAFORM-README.md b/platform/terraform/modules/cloud_aws/TERRAFORM-README.md index 4659b166..69a2dbb0 100644 --- a/platform/terraform/modules/cloud_aws/TERRAFORM-README.md +++ b/platform/terraform/modules/cloud_aws/TERRAFORM-README.md @@ -15,32 +15,32 @@ ## Modules -| Name | Source | Version | -|------|--------|---------| -| [atlantis\_irsa\_role](#module\_atlantis\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | +| Name | Source | Version | +|--------------------------------------------------------------------------------------------------------------|--------|---------| +| [atlantis\_irsa\_role](#module\_atlantis\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | | [cert\_manager\_irsa\_role](#module\_cert\_manager\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | -| [ebs\_csi\_irsa\_role](#module\_ebs\_csi\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | -| [ebs\_kms\_key](#module\_ebs\_kms\_key) | terraform-aws-modules/kms/aws | ~> 1.5 | -| [efs\_csi\_irsa\_role](#module\_efs\_csi\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | -| [eks](#module\_eks) | terraform-aws-modules/eks/aws | ~>19.16.0 | +| [ebs\_csi\_irsa\_role](#module\_ebs\_csi\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | +| [ebs\_kms\_key](#module\_ebs\_kms\_key) | terraform-aws-modules/kms/aws | ~> 1.5 | +| [efs\_csi\_irsa\_role](#module\_efs\_csi\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | +| [eks](#module\_eks) | terraform-aws-modules/eks/aws | ~>19.16.0 | | [external\_dns\_irsa\_role](#module\_external\_dns\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | -| [iam\_argoworkflow\_role](#module\_iam\_argoworkflow\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | -| [image\_registry\_irsa\_role](#module\_image\_registry\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | -| [vault\_irsa\_role](#module\_vault\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | -| [vpc\_cni\_irsa](#module\_vpc\_cni\_irsa) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | +| [iam\_argoworkflow\_role](#module\_iam\_argoworkflow\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | +| [image\_registry\_irsa\_role](#module\_image\_registry\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | +| [vault\_irsa\_role](#module\_vault\_irsa\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | +| [vpc\_cni\_irsa](#module\_vpc\_cni\_irsa) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | ## Resources -| Name | Type | -|------|------| -| [aws_iam_policy.argoworkflow](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.atlantis_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.image_registry_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.vault_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_ami.eks_default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | +| Name | Type | +|---------------------------------------------------------------------------------------------------------------------------------------|------| +| [aws_iam_policy.argoworkflow](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.atlantis_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.harbor_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.vault_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_ami.eks_default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | | [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | ## Inputs @@ -90,7 +90,7 @@ | [iam\_argoworkflow\_role](#output\_iam\_argoworkflow\_role) | ArgoCD Workflow IAM role ARN | | [igw\_arn](#output\_igw\_arn) | IGW ARN Generated by VPC module | | [igw\_id](#output\_igw\_id) | IGW ID Generated by VPC module | -| [image\_registry\_irsa\_role](#output\_image\_registry\_irsa\_role) | Image registry (Harbor) Role ARN | +| [image\_registry\_irsa\_role](#output\_image\_registry\_irsa\_role) | Image registry (Harbor) Role ARN | | [kms\_key\_arn](#output\_kms\_key\_arn) | The Amazon Resource Name (ARN) of the key | | [kms\_key\_id](#output\_kms\_key\_id) | The globally unique identifier for the key | | [kms\_key\_policy](#output\_kms\_key\_policy) | The IAM resource policy set on the key | diff --git a/platform/terraform/modules/cloud_aws/iam.tf b/platform/terraform/modules/cloud_aws/iam.tf index dbda6a77..10c86329 100644 --- a/platform/terraform/modules/cloud_aws/iam.tf +++ b/platform/terraform/modules/cloud_aws/iam.tf @@ -115,7 +115,7 @@ module "cert_manager_irsa_role" { # container registry -module "image_registry_irsa_role" { +module "harbor_irsa_role" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" role_name = "${local.name}-image-registry-role" oidc_providers = { @@ -126,7 +126,7 @@ module "image_registry_irsa_role" { } } role_policy_arns = { - policy = aws_iam_policy.image_registry_policy.arn + policy = aws_iam_policy.harbor_policy.arn } } diff --git a/platform/terraform/modules/cloud_aws/main.tf b/platform/terraform/modules/cloud_aws/main.tf index bbd7b2cb..9607f56f 100644 --- a/platform/terraform/modules/cloud_aws/main.tf +++ b/platform/terraform/modules/cloud_aws/main.tf @@ -3,7 +3,7 @@ data "aws_availability_zones" "available" {} locals { name = var.cluster_name cluster_version = var.cluster_version - region = var.aws_region + region = var.region aws_account = data.aws_caller_identity.current.account_id vpc_cidr = var.cluster_network_cidr azs = slice(data.aws_availability_zones.available.names, 0, min(var.az_count, length(data.aws_availability_zones.available.names))) diff --git a/platform/terraform/modules/cloud_aws/outputs.tf b/platform/terraform/modules/cloud_aws/outputs.tf index 4a5f2ff9..72bef718 100644 --- a/platform/terraform/modules/cloud_aws/outputs.tf +++ b/platform/terraform/modules/cloud_aws/outputs.tf @@ -47,9 +47,9 @@ output "cert_manager_irsa_role" { description = "Cert_Manager IAM Role ARN" value = module.cert_manager_irsa_role.iam_role_arn } -output "image_registry_irsa_role" { +output "harbor_irsa_role" { description = "Image registry (Harbor) Role ARN" - value = module.image_registry_irsa_role.iam_role_arn + value = module.harbor_irsa_role.iam_role_arn } output "external_dns_irsa_role" { description = "External DNS IAM Role ARN" diff --git a/platform/terraform/modules/cloud_aws/policy.tf b/platform/terraform/modules/cloud_aws/policy.tf index 142d65c0..aaeb5621 100644 --- a/platform/terraform/modules/cloud_aws/policy.tf +++ b/platform/terraform/modules/cloud_aws/policy.tf @@ -17,8 +17,8 @@ resource "aws_iam_policy" "argoworkflow" { tags = local.tags } -resource "aws_iam_policy" "image_registry_policy" { - name = "${local.name}-image-registry-policy" +resource "aws_iam_policy" "harbor_policy" { + name = "${local.name}-harbor-policy" description = "Image registry (Harbor) policy for image replication" policy = jsonencode({ @@ -29,7 +29,7 @@ resource "aws_iam_policy" "image_registry_policy" { "ecr:ReplicateImage" ], "Effect" : "Allow", - "Resource" : "arn:aws:ecr:${var.aws_region}:${local.aws_account}:*" + "Resource" : "arn:aws:ecr:${var.region}:${local.aws_account}:*" } ] }) @@ -44,7 +44,7 @@ resource "aws_iam_policy" "atlantis_policy" { { "Action" : "eks:*", "Effect" : "Allow", - "Resource" : "arn:aws:eks:${var.aws_region}:${local.aws_account}:*" + "Resource" : "arn:aws:eks:${var.region}:${local.aws_account}:*" } ] }) @@ -60,7 +60,7 @@ resource "aws_iam_policy" "vault_policy" { { "Action" : "secretsmanager:*", "Effect" : "Allow", - "Resource" : "arn:aws:secretsmanager:${var.aws_region}:${local.aws_account}:*" + "Resource" : "arn:aws:secretsmanager:${var.region}:${local.aws_account}:*" } ] }) diff --git a/platform/terraform/modules/cloud_aws/variables.tf b/platform/terraform/modules/cloud_aws/variables.tf index 488ba72e..5ad5968a 100644 --- a/platform/terraform/modules/cloud_aws/variables.tf +++ b/platform/terraform/modules/cloud_aws/variables.tf @@ -1,5 +1,5 @@ #Do we really want the region setting here? -variable "aws_region" { +variable "region" { type = string default = "eu-west-1" } @@ -22,8 +22,8 @@ variable "cluster_name" { type = string default = "gxc" validation { - condition = (length(var.cluster_name) <= 12) && (length(var.cluster_name) >= 2) - error_message = "Must be between 2 and 12 symbols long" + condition = (length(var.cluster_name) <= 16) && (length(var.cluster_name) >= 2) + error_message = "Must be between 2 and 16 symbols long" } } variable "cluster_version" { @@ -42,21 +42,28 @@ variable "node_groups" { type = list(object({ name = optional(string, "") instance_types = optional(list(string), ["t3.medium", "t3.small"]) - capacity_type = optional(string, "on-demand") - min_size = optional(number, 2) + capacity_type = optional(string, "on_demand") + min_size = optional(number, 3) max_size = optional(number, 5) desired_size = optional(number, 3) } ) ) - # default = [{}] + default = [ + { + name = "default" + instance_types = ["t3.medium", "t3.small"] + capacity_type = "on_demand" + min_size = 3 + max_size = 5 + desired_size = 3 + } + ] } variable "cluster_node_labels" { type = map(any) - default = { - "node.kubernetes.io/provisioned_by" = "cgdevx" - } + default = {} } variable "alert_emails" { diff --git a/platform/terraform/modules/vcs_github/variable.tf b/platform/terraform/modules/vcs_github/variable.tf index 8948bf26..f6059b39 100644 --- a/platform/terraform/modules/vcs_github/variable.tf +++ b/platform/terraform/modules/vcs_github/variable.tf @@ -18,4 +18,7 @@ variable "vcs_bot_ssh_public_key" { default = "" } - +variable "demo_workload_enabled" { + type = bool + default = false +} diff --git a/platform/terraform/modules/vcs_github/workload_repos.tf b/platform/terraform/modules/vcs_github/workload_repos.tf index 8fcfb52c..f3c3b616 100644 --- a/platform/terraform/modules/vcs_github/workload_repos.tf +++ b/platform/terraform/modules/vcs_github/workload_repos.tf @@ -1,21 +1,24 @@ -module "workload_template_repo" { +module "workload_demo_template_repo" { source = "./repository" + count = var.demo_workload_enabled == true ? 1 : 0 - repo_name = "workload-template" + repo_name = "workload-demo-template" is_template = true } -module "workload1_repo" { +module "workload_demo_repo" { source = "./repository" + count = var.demo_workload_enabled == true ? 1 : 0 - repo_name = "workload1" + repo_name = "workload-demo" } -module "workload1_iac_repo" { +module "workload_demo_iac_repo" { source = "./repository" + count = var.demo_workload_enabled == true ? 1 : 0 - repo_name = "workload1-iac" + repo_name = "workload-demo-iac" atlantis_enabled = true atlantis_url = var.atlantis_url atlantis_repo_webhook_secret = var.atlantis_repo_webhook_secret diff --git a/platform/terraform/README.md b/platform/terraform/tpl_README.md similarity index 100% rename from platform/terraform/README.md rename to platform/terraform/tpl_README.md diff --git a/platform/terraform/vcs/main.tf b/platform/terraform/vcs/main.tf index a6f897b0..3511239d 100644 --- a/platform/terraform/vcs/main.tf +++ b/platform/terraform/vcs/main.tf @@ -1,50 +1,22 @@ terraform { - - ## - # backend "s3" { - # bucket = "" - # key = "terraform/github/terraform.tfstate" - - # region = "" - # encrypt = true - # } - + # Remote backend configuration + # } -# Configure the GitHub Provider -provider "github" {} - - -# Variables for templating -# - `` - git-provider -# - `` - git-org -# - `` - git-access-token -# - `` - gitops-repo-name -# - `` Note!: URL does not contain protocol suffix -# - `` +# Configure Git Provider +# +locals { + gitops_repo_name = "" + atlantis_url = "https:///events" +} module "vcs" { - source = "../modules/vcs_github" + source = "../modules/vcs_" - gitops_repo_name = "gitops-repository" #set here - atlantis_url = "https://atlantis.cgdevx-demo.demoapps.click/events" # set "https:///events" + gitops_repo_name = local.gitops_repo_name + atlantis_url = local.atlantis_url atlantis_repo_webhook_secret = var.atlantis_repo_webhook_secret vcs_bot_ssh_public_key = var.vcs_bot_ssh_public_key - + demo_workload_enabled = var.demo_workload_enabled } - -# module "vcs" { -# source = "../modules/vcs_" - -# gitops_repo_name = "" -# atlantis_url = "https:///events" -# # secrets variables need to define through environment variables: -# # export TF_VAR_atlantis_repo_webhook_secret="" -# # export TF_VAR_vcs_bot_ssh_public_key=" | +| Argo CD | argocd | GitOps Continuous Delivery | https:// | +| Argo Workflows | argo | Application Continuous Integration | https:// | +| Atlantis | atlantis | Terraform Workflow Automation | https:// | +| Harbor | harbor | Image & Helm Chart Registry | https:// | +| Grafana | monitoring | Observability | https:// | +| SonarQube | sonarqube | Code Quality | https:// | + +--- + +## gitops registry + +The argocd configurations in this repo can be found in the [registry directory](./registry). The applications that we +build and release on the cgdevx platform will also be registered here in the development, staging, and production +folders. + +The `main` branch's registry directory represents the gitops desired state for all apps registered with kubernetes. Argo +CD will automatically apply your desired state to kubernetes through. You can see the Sync status of all of your apps +in [argo cd](https://argocd.cgdevx-demo.demoapps.click). + +## terraform infrastructure as code + +The terraform in this repository can be found in the [terraform directory](./terraform). It has entry points for +management of cloud resources, vault configurations, git provider configurations, and user management. + +All of our terraform is automated with a tool called atlantis that integrates with your git pull requests. To see the +terraform entry points and under what circumstance they are triggered, see [atlantis.yaml](./atlantis.yaml). + +Any change to a `*.tf` file, even a whitespace change, will trigger its corresponding Atlantis workflow once a pull +request is submitted. Within a minute it will post the plan to the pull request with instruction on how to apply the +plan if approved. \ No newline at end of file diff --git a/templating.md b/templating.md index dc5e7cc8..8da2db7e 100644 --- a/templating.md +++ b/templating.md @@ -32,10 +32,8 @@ Templating variables generated during setup process ### Internal values -- `` - Target git repository HTTP URL - `` - Target git repository Git URL - `` - Git repository URL used for referencing repos including wildcard -- `` - Binary artifat store - `` - K8s Control Center (AKA primary) cluster FQDN - `` @@ -48,14 +46,12 @@ IAM roles for core components created during setup process - `` - `` - `` -- `` -- `` - `` - `` ### Ingress -Ingress URLs for core components. Note!: URL does not contain protocol suffix +Ingress URLs for core components. Note!: URL does not contain protocol prefix - `` - `` @@ -78,7 +74,7 @@ bot user's keypair ### OIDC -OIDC provider configuration Note!: URL does not contain protocol suffix +OIDC provider configuration Note!: URL does not contain protocol prefix - `` - `` @@ -88,6 +84,14 @@ OIDC provider configuration Note!: URL does not contain protocol suffix ### K8s +- `` +- `` - `` - `` +### Network + +- `` +- `` +- `` +- ``