diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9d15f5e --- /dev/null +++ b/.env.example @@ -0,0 +1,49 @@ +KUBE_NAMESPACE= + +MASTER_NODE_LABEL= +WORKER_NODE_LABEL= + +# Certificates Secrets +KUBE_CERT_SERVER= +KUBE_DOMAIN_ORGANIZATION= +KUBE_DOMAIN_EMAIL= +KUBE_DOMAIN= +KUBE_API_DOMAIN= + +# Backend Secrets +KUBE_BACKEND_TOKEN_SECRET= +KUBE_BACKEND_ACCESS_TOKEN_DURATION= +KUBE_BACKEND_REFRESH_TOKEN_DURATION= +KUBE_BACKEND_DEFAULT_USER_FULL_NAME= +KUBE_BACKEND_DEFAULT_USER_EMAIL= +KUBE_BACKEND_DEFAULT_USER_USERNAME= +KUBE_BACKEND_DEFAULT_USER_PASSWORD= +KUBE_BACKEND_GOOGLE_CLIENT_ID= +KUBE_BACKEND_GOOGLE_CLIENT_SECRET= +KUBE_BACKEND_GOOGLE_REDIRECT_URL= +KUBE_BACKEND_GITHUB_CLIENT_ID= +KUBE_BACKEND_GITHUB_CLIENT_SECRET= +KUBE_BACKEND_GITHUB_REDIRECT_URL= + +# Frontend Secrets +KUBE_FRONTEND_URL= +KUBE_FRONTEND_BACKEND_URL= +KUBE_FRONTEND_OAUTH_URL= +KUBE_FRONTEND_GITHUB_USER= + +# Database Secrets (Postgres) +KUBE_DATABASE_NAME= +KUBE_DATABASE_USER= +KUBE_DATABASE_PASSWORD= + +# Redis Secrets +KUBE_REDIS_PASSWORD= + +# Storage Secrets (AWS S3) +KUBE_STORAGE_TYPE= +KUBE_STORAGE_AWS_ACCESS_KEY_ID= +KUBE_STORAGE_AWS_SECRET_ACCESS_KEY= +KUBE_STORAGE_AWS_REGION= +KUBE_STORAGE_AWS_BUCKET= +KUBE_STORAGE_VIRUS_CHECKER_TYPE= +KUBE_STORAGE_VIRUS_CHECKER_API_KEY= diff --git a/.github/workflows/deploy-cluster.yml b/.github/workflows/deploy-cluster.yml new file mode 100644 index 0000000..e295175 --- /dev/null +++ b/.github/workflows/deploy-cluster.yml @@ -0,0 +1,30 @@ +name: remote ssh command + +on: + repository_dispatch: + types: [deploy-prod] + push: + branches: + - main + - staging + +jobs: + deploy: + name: Deploy to Cluster + environment: ${{ github.ref_name == 'main' && 'prod' || 'staging' }} + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - uses: actions/checkout@v4 + - name: Writing Env File + run: | + echo "${{ secrets.ENV_FILE }}" | base64 -d > .env + - name: Set Kubernetes Context + uses: azure/k8s-set-context@v1 + with: + method: kubeconfig + kubeconfig: ${{ secrets.KUBE_CONFIG }} + - name: Execute deploy + run: | + ./deploy.sh -f .env \ No newline at end of file diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml deleted file mode 100644 index d1a7e2a..0000000 --- a/.github/workflows/deploy-prod.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: remote ssh command - -on: - repository_dispatch: - types: [deploy-prod] - push: - branches: - - main - -jobs: - build: - name: Build - environment: prod - runs-on: ubuntu-latest - timeout-minutes: 30 - - steps: - - uses: actions/checkout@v4 - - name: Set up Python 3.8 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - - name: Create Config Json File - run: | - echo "${{ secrets.CONFIG_JSON }}" | base64 -d > config.json - - - name: Inserts Prod Enviromental Variables - run: | - python -m pip install --upgrade pip pipenv - pipenv install - pipenv run python setup.py -e prod -f config.json - - - name: copy file via ssh - uses: appleboy/scp-action@master - with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USER }} - port: ${{ secrets.SSH_PORT }} - key: ${{ secrets.SSH_KEY }} - source: "." - target: "infra-hideyoshi.com" - - - name: executing remote ssh commands - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USER }} - port: ${{ secrets.SSH_PORT }} - key: ${{ secrets.SSH_KEY }} - command_timeout: 30m - script: | - sudo apt update && sudo apt install -y jq - curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash - source ~/.profile - cd infra-hideyoshi.com - ./deploy.sh --prod - ./refresh.sh \ No newline at end of file diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml deleted file mode 100644 index 520ab1e..0000000 --- a/.github/workflows/deploy-staging.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: remote ssh command - -on: - push: - branches: - - staging - -jobs: - build: - name: Build - environment: staging - runs-on: ubuntu-latest - timeout-minutes: 30 - - steps: - - uses: actions/checkout@v4 - - name: Set up Python 3.8 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - - name: Create Config Json File - run: | - echo "${{ secrets.CONFIG_JSON }}" | base64 -d > config.json - - - name: Inserts Prod Enviromental Variables - run: | - python -m pip install --upgrade pip pipenv - pipenv install - pipenv run python setup.py -e staging -f config.json - - - name: copy file via ssh - uses: appleboy/scp-action@master - with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USER }} - port: ${{ secrets.SSH_PORT }} - key: ${{ secrets.SSH_KEY }} - source: "." - target: "infra-hideyoshi.com" - - - name: executing remote ssh commands - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USER }} - port: ${{ secrets.SSH_PORT }} - key: ${{ secrets.SSH_KEY }} - command_timeout: 30m - script: | - sudo apt update && sudo apt install -y jq - curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash - source ~/.profile - cd infra-hideyoshi.com - ./deploy.sh --staging - ./refresh.sh diff --git a/.github/workflows/refresh-deployment.yml b/.github/workflows/refresh-deployment.yml index 36adfc7..3df41d6 100644 --- a/.github/workflows/refresh-deployment.yml +++ b/.github/workflows/refresh-deployment.yml @@ -5,40 +5,32 @@ on: types: [refresh-deployments] jobs: - refresh-deployments: - name: Refresh deployments + refresh_deployment: + name: Refresh Kubernetes Deployments environment: prod runs-on: ubuntu-latest + timeout-minutes: 30 + env: + deployments: ${{ github.event.client_payload.deployments }} + steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - name: Writing Env File + run: | + echo "${{ secrets.ENV_FILE }}" | base64 -d > .env + + - name: Set Kubernetes Context + uses: azure/k8s-set-context@v1 + with: + method: kubeconfig + kubeconfig: ${{ secrets.KUBE_CONFIG }} - - name: Executing Remote Commands - Refresh All Deployments - env: - deployments: ${{ github.event.client_payload.deployments }} + - name: Refresh Deployments if: ${{ env.deployments == '' }} - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USER }} - port: ${{ secrets.SSH_PORT }} - key: ${{ secrets.SSH_KEY }} - script: | - [[ -d infra-hideyoshi.com ]] && \ - cd infra-hideyoshi.com && \ - ./refresh.sh + run: | + ./deploy.sh -f .env -r - - name: Executing Remote Commands - Refresh Specific Deployments - env: - deployments: ${{ github.event.client_payload.deployments }} + - name: Refresh Specific Deployments if: ${{ env.deployments != '' }} - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.SSH_HOST }} - username: ${{ secrets.SSH_USER }} - port: ${{ secrets.SSH_PORT }} - key: ${{ secrets.SSH_KEY }} - script: | - [[ -d infra-hideyoshi.com ]] && \ - cd infra-hideyoshi.com && \ - ./refresh.sh ${{ env.deployments }} \ No newline at end of file + run: | + ./deploy.sh -f .env -r ${{ env.deployments }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index d48d751..1ea70ac 100644 --- a/.gitignore +++ b/.gitignore @@ -2,12 +2,15 @@ .vscode/ .env* +!.env.example .secret* **/*.json !**/*.example.json *.patch +*KUBECONFIG* + **/cert-manager-certificate.yaml **/nginx-ingress-api.yaml **/nginx-ingress-root.yaml diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 70e71ab..0000000 --- a/Pipfile +++ /dev/null @@ -1,13 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -python-dotenv = "*" -envsubst = "*" - -[dev-packages] - -[requires] -python_version = "3" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 6f0e098..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,37 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "9127f5121153ee714035c045f3621c810565d451e8dd461576f24e165e55f73d" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "envsubst": { - "hashes": [ - "sha256:7188f9f2a046e45b63098ce5a7fd84126cbe9e5b73b8ff78eaf6e32122c0caaf", - "sha256:d8e402984a84dda4ea7a8a1f7afe1c41e54a1257cfb74c80cb8f991053d97b9b" - ], - "index": "pypi", - "version": "==0.1.5" - }, - "python-dotenv": { - "hashes": [ - "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba", - "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a" - ], - "index": "pypi", - "version": "==1.0.0" - } - }, - "develop": {} -} diff --git a/deploy.sh b/deploy.sh index 336a9f3..c802636 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,21 +1,82 @@ -#!/bin/bash +#!/bin/sh -function check_for_dependencies() { - if ! command -v kubectl &>/dev/null; then + +validate_dependencies() { + if ! command -v kubectl &> /dev/null; then echo "kubectl could not be found" exit 1 fi - if ! command -v jq &>/dev/null; then - echo "jq could not be found" - exit 1 - fi - if ! command -v helm &>/dev/null; then + + if ! command -v helm &> /dev/null; then echo "helm could not be found" exit 1 fi + + if ! command -v envsubst &> /dev/null; then + echo "envsubst could not be found" + exit 1 + fi + + if [[ $environment == "local" ]]; then + if ! command -v minikube &> /dev/null; then + echo "minikube could not be found" + exit 1 + fi + fi + + echo "Dependencies validated" } -function configure_nginx_ingress() { + +read_env_file() { + if [ -f $1 ]; then + set -a && source $1 && set +a; + fi +} + + +build_secret_envs() { + for i in $(env | grep -E '^KUBE_[a-zA-Z_][a-zA-Z0-9_]*=' | cut -d= -f1); do + eval "export ${i}_B64=$(echo -n ${!i} | base64 -w0)" + done +} + + +apply_template() { + echo -e "\n\n----------------------------------------------------\n" + echo -e "Applying: $1\n" + echo -e "----------------------------------------------------\n\n\n" + + envsubst < $1 | kubectl apply -f - +} + + +apply_deployment() { + deployment_name=$1 + deployment_files=$2 + + for file in $(find $2 -type f); do + apply_template $file + done + + kubectl wait --for=condition=available \ + --timeout=600s \ + deployment.apps/${deployment_name} \ + -n ${KUBE_NAMESPACE} +} + + +configure_nginx_minikube() { + if [[ $setup_minikube == "true" ]]; then + minikube start --driver kvm2 --cpus 2 --memory 4Gib + fi + + minikube addons enable ingress-dns + minikube addons enable ingress +} + + +configure_nginx_ingress() { helm upgrade --install ingress-nginx ingress-nginx \ --repo https://kubernetes.github.io/ingress-nginx \ --namespace ingress-nginx --create-namespace @@ -26,7 +87,8 @@ function configure_nginx_ingress() { --timeout=120s } -function configure_cert_manager() { + +configure_cert_manager() { helm repo add jetstack https://charts.jetstack.io --force-update helm repo update helm install cert-manager jetstack/cert-manager \ @@ -34,121 +96,148 @@ function configure_cert_manager() { --create-namespace \ --version v1.14.2 \ --set installCRDs=true \ - --timeout=600s + --timeout=600s || echo "Cert Manager already installed" } -function configure_postgres() { + +configure_postgres() { helm repo add cnpg https://cloudnative-pg.github.io/charts helm upgrade --install cnpg \ - --namespace portfolio \ + --namespace ${KUBE_NAMESPACE} \ --create-namespace \ cnpg/cloudnative-pg kubectl wait --for=condition=available \ --timeout=600s \ deployment.apps/cnpg-cloudnative-pg \ - -n portfolio + -n ${KUBE_NAMESPACE} - kubectl apply -f ./deployment/postgres/cn-cluster.yaml + apply_template "./template/postgres/cn-cluster.template.yaml" kubectl wait --for=condition=Ready \ --timeout=600s \ cluster/postgres-cn-cluster \ - -n portfolio + -n ${KUBE_NAMESPACE} } -function application_deploy() { - kubectl create secret generic backend-secret -n portfolio \ - --from-env-file <(jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" ./deployment/secrets/backendSecret.json) - - kubectl create secret generic frontend-secret -n portfolio \ - --from-env-file <(jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" ./deployment/secrets/frontendSecret.json) - - kubectl create secret generic redis-secret -n portfolio \ - --from-env-file <(jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" ./deployment/secrets/redisSecret.json) - - kubectl create secret generic storage-secret -n portfolio \ - --from-env-file <(jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" ./deployment/secrets/storageSecret.json) - - kubectl apply -f ./deployment/redis - kubectl wait --for=condition=available \ - --timeout=600s \ - deployment.apps/redis-deployment \ - -n portfolio - - kubectl apply -f ./deployment/frontend - kubectl wait --for=condition=available \ - --timeout=600s \ - deployment.apps/frontend-deployment \ - -n portfolio - - kubectl apply -f ./deployment/storage - kubectl wait --for=condition=available \ - --timeout=600s \ - deployment.apps/storage-deployment \ - -n portfolio - - kubectl apply -f ./deployment/backend - kubectl wait --for=condition=available \ - --timeout=600s \ - deployment.apps/backend-deployment \ - -n portfolio - - kubectl apply -f \ - ./deployment/nginx-ingress - -} - -function main() { - - check_for_dependencies - - if [[ $1 == "--local" || $1 == "-l" ]]; then - - function kubectl { - minikube kubectl -- $@ - } - - minikube start --driver kvm2 --cpus 2 --memory 4Gib - minikube addons enable ingress-dns - minikube addons enable ingress +configure_ingress() { + apply_template "./template/nginx-ingress/nginx-ingress-root.template.yaml" + if [[ $environment == "local" ]]; then + apply_template "./template/cert-manager/cert-manager-issuer-dev.yaml" else + apply_template "./template/cert-manager/cert-manager-issuer.yaml" + fi + apply_template "./template/cert-manager/cert-manager-certificate.template.yaml" +} + + +deploy_kubernetes() { + if [[ $environment == "local" ]]; then + configure_nginx_minikube + else configure_nginx_ingress - fi configure_cert_manager - kubectl apply -f ./deployment/portfolio-namespace.yaml + KUBE_FILES=( + "./template/portfolio-namespace.template.yaml" + "./template/portfolio-secret.template.yml" + ) + + for file in ${KUBE_FILES[@]}; do + apply_template $file + done configure_postgres - application_deploy + apply_deployment "redis-deployment" "./template/redis" - if [[ $1 == "--local" || $1 == "-l" ]]; then + apply_deployment "storage-deployment" "./template/storage" - kubectl apply -f \ - ./deployment/cert-manager/cert-manager-issuer-dev.yaml + apply_deployment "backend-deployment" "./template/backend" - kubectl apply -f \ - ./deployment/cert-manager/cert-manager-certificate.yaml + apply_deployment "frontend-deployment" "./template/frontend" - echo "http://$(/usr/bin/minikube ip)" - - else - - kubectl apply -f \ - ./deployment/cert-manager/cert-manager-issuer.yaml - - kubectl apply -f \ - ./deployment/cert-manager/cert-manager-certificate.yaml + configure_ingress + if [[ $environment == "local" ]]; then + echo "Minikube IP: http://$(minikube ip)" fi - - exit 0 - } -main $1 + +main() { + build_secret_envs + + deploy_kubernetes $@ +} + + +refresh() { + deployments=$1 + if [[ -z $1 ]]; then + deployments=( + "redis-deployment" + "storage-deployment" + "backend-deployment" + "frontend-deployment" + ) + fi + for deployment in ${deployments[@]}; do + kubectl rollout restart deployment/${deployment} -n ${KUBE_NAMESPACE} + done +} + + +environment="remote" +setup_minikube="false" +execution_mode="deploy" + +while getopts ":f:e:mrh" opt; do + case ${opt} in + f ) + echo "Reading env file: ${OPTARG}" + read_env_file ${OPTARG} + ;; + e ) + [[ ${OPTARG} == "local" ]] && environment="local" + echo "Environment: ${OPTARG}" + ;; + m ) + setup_minikube="true" + echo "Setting up minikube" + ;; + h ) + echo "Usage: deploy.sh [-f ] [-e ] [-m ]" + exit 0 + ;; + r ) + echo "Executing Refresh" + execution_mode="refresh" + + eval nextopt=\${$OPTIND} + if [[ -n $nextopt && $nextopt != -* ]]; then + OPTIND=$((OPTIND + 1)) + refresh_deployments=($nextopt) + fi + ;; + *) + echo "Invalid option: $OPTARG" + exit 1 + ;; + esac +done + +validate_dependencies + +if [[ $execution_mode == "deploy" ]]; then + main +elif [[ $execution_mode == "refresh" ]]; then + [[ -z $refresh_deployments ]] && refresh || refresh $refresh_deployments +else + echo "Invalid execution mode: $execution_mode" + exit 1 +fi \ No newline at end of file diff --git a/env.example.json b/env.example.json deleted file mode 100644 index 1533946..0000000 --- a/env.example.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "backendSecret": { - "tokenSecret": "", - "accessTokenDuration": "", - "refreshTokenDuration": "", - "defaultUserFullName": "", - "defaultUserEmail": "", - "defaultUserUsername": "", - "defaultUserPassword": "", - "googleClientId": "", - "googleClientSecret": "", - "googleRedirectUrl": "", - "githubClientId": "", - "githubClientSecret": "", - "githubRedirectUrl": "" - }, - "frontendSecret": { - "frontendPath": "", - "backendUrl": "", - "backendOAuthUrl": "" - }, - "postgresSecret": { - "postgresDatabase": "", - "postgresUser": "", - "postgresPassword": "" - }, - "redisSecret": { - "redisPassword": "" - }, - "storageSecret": { - "storageType": "", - "awsAccessKeyId": "", - "awsSecretAccessKey": "", - "awsRegion": "", - "awsBucket": "", - "virusCheckerType": "", - "virusCheckerApiKey": "" - } -} \ No newline at end of file diff --git a/refresh.sh b/refresh.sh deleted file mode 100755 index 7e905c2..0000000 --- a/refresh.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - - -function refresh_kubernetes_secrets() { - kubectl delete secret backend-secret -n portfolio - kubectl delete secret frontend-secret -n portfolio - kubectl delete secret postgres-secret -n portfolio - kubectl delete secret redis-secret -n portfolio - kubectl delete secret storage-secret -n portfolio - - kubectl create secret generic backend-secret -n portfolio --from-env-file <(jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" ./deployment/secrets/backendSecret.json) - kubectl create secret generic frontend-secret -n portfolio --from-env-file <(jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" ./deployment/secrets/frontendSecret.json) - kubectl create secret generic postgres-secret -n portfolio --from-env-file <(jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" ./deployment/secrets/postgresSecret.json) - kubectl create secret generic redis-secret -n portfolio --from-env-file <(jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" ./deployment/secrets/redisSecret.json) - kubectl create secret generic storage-secret -n portfolio --from-env-file <(jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" ./deployment/secrets/storageSecret.json) -} - -function refresh_kubernetes_deployments() { - NAMESPACES=( - portfolio - ) - DEPLOYMENTS=("$@") - - for i in "${NAMESPACES[@]}"; do - for x in "${DEPLOYMENTS[@]}"; do - PODS=$(kubectl -n $i get pods --no-headers | awk '{print $1}' | grep $x | tr '\n' ' ') - kubectl -n $i delete pods $PODS - done - done -} - - -if [ $# -eq 0 ]; then - DEPLOYMENTS=( - "frontend-deployment" - "backend-deployment" - "storage-deployment" - "storage-processor-deployment" - ) -else - DEPLOYMENTS=("$@") -fi - -refresh_kubernetes_secrets - -refresh_kubernetes_deployments "${NAMESPACES[@]}" "${DEPLOYMENTS[@]}" - diff --git a/setup.py b/setup.py deleted file mode 100644 index 1769c86..0000000 --- a/setup.py +++ /dev/null @@ -1,208 +0,0 @@ -from base64 import b64decode, b64encode -from dotenv import load_dotenv -from envsubst import envsubst -from pathlib import Path, PosixPath -import argparse -import warnings -import json -import os - - -def write_template(template: str, output: str): - os.makedirs(os.path.dirname(output), exist_ok=True) - with open(template, 'r') as template,\ - open(output, 'w') as output: - output.write(envsubst(template.read())) - - -def configure_env_variables(environment: str): - if not environment in ("prod", "staging", "local"): - raise ValueError("Invalid Environment Selected") - - match environment: - case "local": - DOMAIN = "local.hideyoshi.com.br" - API_DOMAIN = "api.local.hideyoshi.com.br" - MASTER_NODE_LABEL = "minikube.k8s.io/name: minikube" - WORKER_NODE_LABEL = "minikube.k8s.io/name: minikube" - - case "staging": - DOMAIN = "staging.hideyoshi.com.br" - API_DOMAIN = "api.staging.hideyoshi.com.br" - MASTER_NODE_LABEL = "node_type: master" - WORKER_NODE_LABEL = "node_type: worker" - - case _: - DOMAIN = "hideyoshi.com.br" - API_DOMAIN = "api.hideyoshi.com.br" - MASTER_NODE_LABEL = "node_type: master" - WORKER_NODE_LABEL = "node_type: worker" - - os.environ["DOMAIN"] = DOMAIN - os.environ["API_DOMAIN"] = API_DOMAIN - os.environ["MASTER_NODE_LABEL"] = MASTER_NODE_LABEL - os.environ["WORKER_NODE_LABEL"] = WORKER_NODE_LABEL - - -def configure_templates(environment: str): - MAPPINS = [ - {"template": "template/cert-manager/cert-manager-certificate.template.yaml", "output": "deployment/cert-manager/cert-manager-certificate.yaml"}, - {"template": "template/nginx-ingress/nginx-ingress-root.template.yaml", "output": "deployment/nginx-ingress/nginx-ingress-root.yaml"}, - {"template": "template/postgres/cn-cluster.template.yaml", "output": "deployment/postgres/cn-cluster.yaml"}, - {"template": "template/frontend/frontend.template.yaml", "output": "deployment/frontend/frontend.yaml"}, - {"template": "template/backend/backend.template.yaml", "output": "deployment/backend/backend.yaml"}, - {"template": "template/storage/storage-processor.template.yaml", "output": "deployment/storage/storage-processor.yaml"}, - {"template": "template/storage/storage.template.yaml", "output": "deployment/storage/storage.yaml"}, - ] - - for mapping in MAPPINS: - write_template(mapping["template"], mapping["output"]) - - -def validate_backend_secret(secret: str): - required_keys = [ - 'tokenSecret', - 'accessTokenDuration', - 'refreshTokenDuration', - 'defaultUserFullName', - 'defaultUserEmail', - 'defaultUserUsername', - 'defaultUserPassword', - 'googleClientId', - 'googleClientSecret', - 'googleRedirectUrl', - 'githubClientId', - 'githubClientSecret', - 'githubRedirectUrl' - ] - - for key in required_keys: - if key not in secret: - raise ValueError(f"Key {key} not found in backendSecret") - - -def validate_frontend_secret(secret: str): - required_keys = [ - 'frontendPath', - 'backendUrl', - 'backendOAuthUrl', - 'githubUser' - ] - - for key in required_keys: - if key not in secret: - raise ValueError(f"Key {key} not found in frontendSecret") - - -def validate_postgres_secret(secret: str): - required_keys = [ - 'postgresUser', - 'postgresPassword', - 'postgresDatabase' - ] - - for key in required_keys: - if key not in secret: - raise ValueError(f"Key {key} not found in postgresSecret") - - - -def validate_redis_secret(secret: str): - required_keys = [ - 'redisPassword', - ] - - for key in required_keys: - if key not in secret: - raise ValueError(f"Key {key} not found in redisSecret") - - -def validate_storage_secret(secret: str): - required_keys = [ - 'storageType', - 'awsAccessKeyId', - 'awsSecretAccessKey', - 'awsRegion', - 'awsBucket', - 'virusCheckerType', - 'virusCheckerApiKey', - ] - - for key in required_keys: - if key not in secret: - raise ValueError(f"Key {key} not found in storageSecret") - - -def validate_env(env: dict): - required_secrets = [ - 'backendSecret', - 'frontendSecret', - 'postgresSecret', - 'redisSecret', - 'storageSecret', - ] - - for secret in required_secrets: - if secret not in env: - raise ValueError(f"Secret {secret} not found in env.json") - - if secret == 'backendSecret': - validate_backend_secret(env[secret]) - - if secret == 'frontendSecret': - validate_frontend_secret(env[secret]) - - if secret == 'postgresSecret': - validate_postgres_secret(env[secret]) - - if secret == 'redisSecret': - validate_redis_secret(env[secret]) - - if secret == 'storageSecret': - validate_storage_secret(env[secret]) - -def write_secrets_to_file(env: dict): - for key, secret in env.items(): - secrets_dir = Path("deployment", "secrets") - if not secrets_dir.exists(): - secrets_dir.mkdir() - - with open(secrets_dir.joinpath(f"{key}.json"), "w") as f: - json.dump(secret, f, indent=4) - - -def read_env_json(file: str) -> dict: - with open(file, "r") as f: - return json.load(f) - - -def main(file, environment): - env = read_env_json(file) - - validate_env(env) - - write_secrets_to_file(env) - - configure_env_variables(environment) - - configure_templates(environment) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(prog="Setup") - parser.add_argument( - "-f", "--file", - dest="file", - default=".env", - help="Secret file [default = .secret]" - ) - parser.add_argument( - "-e", "--environment", - dest="environment", - default="prod", - help="Selected Deployment Environment [default = prod, options = [prod, staging, dev]]" - ) - - args = parser.parse_args() - - main(**vars(args)) diff --git a/deployment/backend/backend-config.yaml b/template/backend/backend-config.yaml similarity index 63% rename from deployment/backend/backend-config.yaml rename to template/backend/backend-config.yaml index f8b54f6..126b9b6 100644 --- a/deployment/backend/backend-config.yaml +++ b/template/backend/backend-config.yaml @@ -1,9 +1,9 @@ apiVersion: v1 kind: ConfigMap metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: backend-config data: backend_url: backend-service backend_port: "8070" - backend_db_name: portfolio \ No newline at end of file + backend_db_name: ${KUBE_DATABASE_NAME} \ No newline at end of file diff --git a/template/backend/backend.template.yaml b/template/backend/backend.template.yaml index 1bb8548..d38c2ad 100644 --- a/template/backend/backend.template.yaml +++ b/template/backend/backend.template.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: backend-deployment spec: replicas: 1 @@ -15,6 +15,18 @@ spec: spec: nodeSelector: ${WORKER_NODE_LABEL} + initContainers: + - name: wait-storage-init + image: busybox:latest + args: + - /bin/sh + - -c + - > + set -x; + while [ $(curl -sw '%{http_code}' "storage-service:8070/health" -o /dev/null) -ne 200 ]; do + sleep 15; + done + containers: - name: backend image: yoshiunfriendly/backend-hideyoshi.com @@ -32,7 +44,7 @@ spec: httpGet: path: /health port: 8070 - initialDelaySeconds: 240 + initialDelaySeconds: 60 livenessProbe: httpGet: path: /health @@ -43,49 +55,49 @@ spec: valueFrom: secretKeyRef: name: frontend-secret - key: frontendPath + key: frontendUrl - name: TOKEN_SECRET valueFrom: secretKeyRef: name: backend-secret - key: tokenSecret + key: backendTokenSecret - name: ACCESS_TOKEN_DURATION valueFrom: secretKeyRef: name: backend-secret - key: accessTokenDuration + key: backendAccessTokenDuration - name: REFRESH_TOKEN_DURATION valueFrom: secretKeyRef: name: backend-secret - key: refreshTokenDuration + key: backendRefreshTokenDuration - name: DEFAULT_USER_FULLNAME valueFrom: secretKeyRef: name: backend-secret - key: defaultUserFullName + key: backendDefaultUserFullName - name: DEFAULT_USER_EMAIL valueFrom: secretKeyRef: name: backend-secret - key: defaultUserEmail + key: backendDefaultUserEmail - name: DEFAULT_USER_USERNAME valueFrom: secretKeyRef: name: backend-secret - key: defaultUserUsername + key: backendDefaultUserUsername - name: DEFAULT_USER_PASSWORD valueFrom: secretKeyRef: name: backend-secret - key: defaultUserPassword + key: backendDefaultUserPassword - name: PORT valueFrom: @@ -97,37 +109,37 @@ spec: valueFrom: secretKeyRef: name: backend-secret - key: googleClientId + key: backendGoogleClientId - name: GOOGLE_CLIENT_SECRET valueFrom: secretKeyRef: name: backend-secret - key: googleClientSecret + key: backendGoogleClientSecret - name: GOOGLE_REDIRECT_URL valueFrom: secretKeyRef: name: backend-secret - key: googleRedirectUrl + key: backendGoogleRedirectUrl - name: GITHUB_CLIENT_ID valueFrom: secretKeyRef: name: backend-secret - key: githubClientId + key: backendGithubClientId - name: GITHUB_CLIENT_SECRET valueFrom: secretKeyRef: name: backend-secret - key: githubClientSecret + key: backendGithubClientSecret - name: GITHUB_REDIRECT_URL valueFrom: secretKeyRef: name: backend-secret - key: githubRedirectUrl + key: backendGithubRedirectUrl - name: POSTGRES_URL valueFrom: @@ -193,7 +205,7 @@ spec: apiVersion: v1 kind: Service metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: backend-service spec: selector: diff --git a/template/cert-manager/cert-manager-certificate.template.yaml b/template/cert-manager/cert-manager-certificate.template.yaml index 09d5a1b..7a25aa8 100644 --- a/template/cert-manager/cert-manager-certificate.template.yaml +++ b/template/cert-manager/cert-manager-certificate.template.yaml @@ -2,15 +2,15 @@ apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: letsencrypt-cluster-certificate - namespace: portfolio + namespace: ${KUBE_NAMESPACE} spec: dnsNames: - - ${DOMAIN} - - ${API_DOMAIN} + - ${KUBE_DOMAIN} + - ${KUBE_API_DOMAIN} secretName: letsencrypt-cluster-certificate-tls issuerRef: name: cluster-certificate-issuer kind: ClusterIssuer subject: organizations: - - Hideyoshi \ No newline at end of file + - ${KUBE_DOMAIN_ORGANIZATION} \ No newline at end of file diff --git a/deployment/cert-manager/cert-manager-issuer-dev.yaml b/template/cert-manager/cert-manager-issuer-dev.yaml similarity index 100% rename from deployment/cert-manager/cert-manager-issuer-dev.yaml rename to template/cert-manager/cert-manager-issuer-dev.yaml diff --git a/deployment/cert-manager/cert-manager-issuer.yaml b/template/cert-manager/cert-manager-issuer.yaml similarity index 73% rename from deployment/cert-manager/cert-manager-issuer.yaml rename to template/cert-manager/cert-manager-issuer.yaml index 6936556..de0159f 100644 --- a/deployment/cert-manager/cert-manager-issuer.yaml +++ b/template/cert-manager/cert-manager-issuer.yaml @@ -4,8 +4,8 @@ metadata: name: cluster-certificate-issuer spec: acme: - server: https://acme-v02.api.letsencrypt.org/directory - email: vitor.h.n.batista@gmail.com + server: ${KUBE_CERT_SERVER} + email: ${KUBE_DOMAIN_EMAIL} privateKeySecretRef: name: cluster-certificate-issuer solvers: diff --git a/deployment/frontend/frontend-config.yaml b/template/frontend/frontend-config.yaml similarity index 76% rename from deployment/frontend/frontend-config.yaml rename to template/frontend/frontend-config.yaml index c8bf709..7ca5d0b 100644 --- a/deployment/frontend/frontend-config.yaml +++ b/template/frontend/frontend-config.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: ConfigMap metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: frontend-config data: frontend_url: frontend-service \ No newline at end of file diff --git a/template/frontend/frontend.template.yaml b/template/frontend/frontend.template.yaml index 614619c..b71ccbd 100644 --- a/template/frontend/frontend.template.yaml +++ b/template/frontend/frontend.template.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: frontend-deployment labels: app: frontend @@ -17,6 +17,17 @@ spec: spec: nodeSelector: ${WORKER_NODE_LABEL} + initContainers: + - name: wait-backend-init + image: busybox:latest + args: + - /bin/sh + - -c + - > + set -x; + while [ $(curl -sw '%{http_code}' "backend-service:8000/health" -o /dev/null) -ne 200 ]; do + sleep 15; + done containers: - name: frontend image: yoshiunfriendly/frontend-hideyoshi.com:latest @@ -47,23 +58,23 @@ spec: valueFrom: secretKeyRef: name: frontend-secret - key: backendUrl + key: frontendBackendUrl - name: BACKEND_OAUTH_URL valueFrom: secretKeyRef: name: frontend-secret - key: backendOAuthUrl + key: frontendOAuthUrl - name: GITHUB_USER valueFrom: secretKeyRef: name: frontend-secret - key: githubUser + key: frontendGithubUser --- apiVersion: v1 kind: Service metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: frontend-service spec: selector: diff --git a/template/nginx-ingress/nginx-ingress-root.template.yaml b/template/nginx-ingress/nginx-ingress-root.template.yaml index e752f56..64bd92d 100644 --- a/template/nginx-ingress/nginx-ingress-root.template.yaml +++ b/template/nginx-ingress/nginx-ingress-root.template.yaml @@ -1,7 +1,7 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: nginx-ingress annotations: kubernetes.io/ingress.class: nginx @@ -10,11 +10,11 @@ metadata: spec: tls: - hosts: - - ${DOMAIN} - - ${API_DOMAIN} + - ${KUBE_DOMAIN} + - ${KUBE_API_DOMAIN} secretName: letsencrypt-cluster-certificate-tls rules: - - host: ${DOMAIN} + - host: ${KUBE_DOMAIN} http: paths: - path: / @@ -24,7 +24,7 @@ spec: name: frontend-service port: number: 5000 - - host: ${API_DOMAIN} + - host: ${KUBE_API_DOMAIN} http: paths: - path: / diff --git a/deployment/portfolio-namespace.yaml b/template/portfolio-namespace.template.yaml similarity index 59% rename from deployment/portfolio-namespace.yaml rename to template/portfolio-namespace.template.yaml index 5b9aa57..f068a10 100644 --- a/deployment/portfolio-namespace.yaml +++ b/template/portfolio-namespace.template.yaml @@ -1,4 +1,4 @@ apiVersion: v1 kind: Namespace metadata: - name: portfolio \ No newline at end of file + name: ${KUBE_NAMESPACE} \ No newline at end of file diff --git a/template/portfolio-secret.template.yml b/template/portfolio-secret.template.yml new file mode 100644 index 0000000..c27d3fd --- /dev/null +++ b/template/portfolio-secret.template.yml @@ -0,0 +1,77 @@ +apiVersion: v1 +kind: Secret +metadata: + name: backend-secret + namespace: ${KUBE_NAMESPACE} +type: Opaque +data: + backendTokenSecret: ${KUBE_BACKEND_TOKEN_SECRET_B64} + backendAccessTokenDuration: ${KUBE_BACKEND_ACCESS_TOKEN_DURATION_B64} + backendRefreshTokenDuration: ${KUBE_BACKEND_REFRESH_TOKEN_DURATION_B64} + backendDefaultUserFullName: ${KUBE_BACKEND_DEFAULT_USER_FULL_NAME_B64} + backendDefaultUserEmail: ${KUBE_BACKEND_DEFAULT_USER_EMAIL_B64} + backendDefaultUserUsername: ${KUBE_BACKEND_DEFAULT_USER_USERNAME_B64} + backendDefaultUserPassword: ${KUBE_BACKEND_DEFAULT_USER_PASSWORD_B64} + backendGoogleClientId: ${KUBE_BACKEND_GOOGLE_CLIENT_ID_B64} + backendGoogleClientSecret: ${KUBE_BACKEND_GOOGLE_CLIENT_SECRET_B64} + backendGoogleRedirectUrl: ${KUBE_BACKEND_GOOGLE_REDIRECT_URL_B64} + backendGithubClientId: ${KUBE_BACKEND_GITHUB_CLIENT_ID_B64} + backendGithubClientSecret: ${KUBE_BACKEND_GITHUB_CLIENT_SECRET_B64} + backendGithubRedirectUrl: ${KUBE_BACKEND_GITHUB_REDIRECT_URL_B64} + +--- + +apiVersion: v1 +kind: Secret +metadata: + name: frontend-secret + namespace: ${KUBE_NAMESPACE} +type: Opaque +data: + frontendUrl: ${KUBE_FRONTEND_URL_B64} + frontendBackendUrl: ${KUBE_FRONTEND_BACKEND_URL_B64} + frontendOAuthUrl: ${KUBE_FRONTEND_OAUTH_URL_B64} + frontendGithubUser: ${KUBE_FRONTEND_GITHUB_USER_B64} + +--- + +apiVersion: v1 +kind: Secret +metadata: + name: database-secret + namespace: ${KUBE_NAMESPACE} +type: Opaque +data: + databaseName: ${KUBE_DATABASE_NAME_B64} + databaseUser: ${KUBE_DATABASE_USER_B64} + databasePassword: ${KUBE_DATABASE_PASSWORD_B64} + + +--- + +apiVersion: v1 +kind: Secret +metadata: + name: redis-secret + namespace: ${KUBE_NAMESPACE} +type: Opaque +data: + redisPassword: ${KUBE_REDIS_PASSWORD_B64} + +--- + +apiVersion: v1 +kind: Secret +metadata: + name: storage-secret + namespace: ${KUBE_NAMESPACE} +type: Opaque +data: + storageType: ${KUBE_STORAGE_TYPE_B64} + storageAwsAccessKeyId: ${KUBE_STORAGE_AWS_ACCESS_KEY_ID_B64} + storageAwsSecretAccessKey: ${KUBE_STORAGE_AWS_SECRET_ACCESS_KEY_B64} + storageAwsRegion: ${KUBE_STORAGE_AWS_REGION_B64} + storageAwsBucket: ${KUBE_STORAGE_AWS_BUCKET_B64} + storageVirusCheckerType: ${KUBE_STORAGE_VIRUS_CHECKER_TYPE_B64} + storageVirusCheckerApiKey: ${KUBE_STORAGE_VIRUS_CHECKER_API_KEY_B64} + \ No newline at end of file diff --git a/template/postgres/cn-cluster.template.yaml b/template/postgres/cn-cluster.template.yaml index 0ce2949..f53bffb 100644 --- a/template/postgres/cn-cluster.template.yaml +++ b/template/postgres/cn-cluster.template.yaml @@ -2,7 +2,7 @@ apiVersion: postgresql.cnpg.io/v1 kind: Cluster metadata: name: postgres-cn-cluster - namespace: portfolio + namespace: ${KUBE_NAMESPACE} spec: instances: 1 primaryUpdateStrategy: unsupervised diff --git a/deployment/redis/redis-config.yaml b/template/redis/redis-config.yaml similarity index 77% rename from deployment/redis/redis-config.yaml rename to template/redis/redis-config.yaml index a63391a..cd08cc9 100644 --- a/deployment/redis/redis-config.yaml +++ b/template/redis/redis-config.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: ConfigMap metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: redis-config data: redis-url: redis-service diff --git a/deployment/redis/redis.yaml b/template/redis/redis.yaml similarity index 90% rename from deployment/redis/redis.yaml rename to template/redis/redis.yaml index 52908d9..93a1f40 100644 --- a/deployment/redis/redis.yaml +++ b/template/redis/redis.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: redis-deployment spec: replicas: 1 @@ -15,7 +15,7 @@ spec: spec: containers: - name: redis - image: bitnami/redis + image: bitnami/redis:6.2.16 imagePullPolicy: "IfNotPresent" resources: requests: @@ -37,7 +37,7 @@ spec: apiVersion: v1 kind: Service metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: redis-service spec: selector: diff --git a/deployment/storage/storage-config.yaml b/template/storage/storage-config.yaml similarity index 79% rename from deployment/storage/storage-config.yaml rename to template/storage/storage-config.yaml index 229d64e..e3b0228 100644 --- a/deployment/storage/storage-config.yaml +++ b/template/storage/storage-config.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: ConfigMap metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: storage-config data: storage_url: storage-service diff --git a/template/storage/storage-processor.template.yaml b/template/storage/storage-processor.template.yaml index 9677924..becbd58 100644 --- a/template/storage/storage-processor.template.yaml +++ b/template/storage/storage-processor.template.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: storage-processor-deployment spec: replicas: 1 @@ -44,13 +44,13 @@ spec: valueFrom: secretKeyRef: name: frontend-secret - key: backendUrl + key: frontendBackendUrl - name: EXPIRES_IN valueFrom: secretKeyRef: name: backend-secret - key: accessTokenDuration + key: backendAccessTokenDuration - name: SERVER_PORT valueFrom: @@ -86,34 +86,34 @@ spec: valueFrom: secretKeyRef: name: storage-secret - key: awsAccessKeyId + key: storageAwsAccessKeyId - name: AWS_SECRET_ACCESS_KEY valueFrom: secretKeyRef: name: storage-secret - key: awsSecretAccessKey + key: storageAwsSecretAccessKey - name: AWS_REGION_NAME valueFrom: secretKeyRef: name: storage-secret - key: awsRegion + key: storageAwsRegion - name: AWS_BUCKET_NAME valueFrom: secretKeyRef: name: storage-secret - key: awsBucket + key: storageAwsBucket - name: VIRUS_CHECKER_TYPE valueFrom: secretKeyRef: name: storage-secret - key: virusCheckerType + key: storageVirusCheckerType - name: VIRUS_CHECKER_API_KEY valueFrom: secretKeyRef: name: storage-secret - key: virusCheckerApiKey + key: storageVirusCheckerApiKey diff --git a/template/storage/storage.template.yaml b/template/storage/storage.template.yaml index 9e10e0e..a408161 100644 --- a/template/storage/storage.template.yaml +++ b/template/storage/storage.template.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: storage-deployment spec: replicas: 1 @@ -43,13 +43,13 @@ spec: valueFrom: secretKeyRef: name: frontend-secret - key: backendUrl + key: frontendBackendUrl - name: EXPIRES_IN valueFrom: secretKeyRef: name: backend-secret - key: accessTokenDuration + key: backendAccessTokenDuration - name: SERVER_PORT valueFrom: @@ -85,43 +85,43 @@ spec: valueFrom: secretKeyRef: name: storage-secret - key: awsAccessKeyId + key: storageAwsAccessKeyId - name: AWS_SECRET_ACCESS_KEY valueFrom: secretKeyRef: name: storage-secret - key: awsSecretAccessKey + key: storageAwsSecretAccessKey - name: AWS_REGION_NAME valueFrom: secretKeyRef: name: storage-secret - key: awsRegion + key: storageAwsRegion - name: AWS_BUCKET_NAME valueFrom: secretKeyRef: name: storage-secret - key: awsBucket + key: storageAwsBucket - name: VIRUS_CHECKER_TYPE valueFrom: secretKeyRef: name: storage-secret - key: virusCheckerType + key: storageVirusCheckerType - name: VIRUS_CHECKER_API_KEY valueFrom: secretKeyRef: name: storage-secret - key: virusCheckerApiKey + key: storageVirusCheckerApiKey --- apiVersion: v1 kind: Service metadata: - namespace: portfolio + namespace: ${KUBE_NAMESPACE} name: storage-service spec: selector: