From e70706ab795486023b8905640e16a01519676845 Mon Sep 17 00:00:00 2001 From: satishbpatil Date: Mon, 15 Jul 2024 11:54:02 -0500 Subject: [PATCH] Codecommit (#12) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * replace git with codecommit Co-authored-by: Sébastien Allamand --------- Signed-off-by: Sébastien Allamand Co-authored-by: EC2 Default User Co-authored-by: Sébastien Allamand --- assets/scripts/destroy-git.sh | 10 + assets/scripts/destroy.sh | 10 +- .../webstore/assets/base/configMap.yaml | 0 .../webstore/assets/base/deployment.yaml | 0 .../webstore/assets/base/kustomization.yaml | 0 .../webstore/assets/base/service.yaml | 0 .../webstore/assets/base/serviceAccount.yaml | 0 .../webstore/assets/hub/kustomization.yaml | 0 .../webstore/assets/prod/kustomization.yaml | 0 .../assets/staging/kustomization.yaml | 0 .../webstore/carts/base/deployment.yaml | 0 .../carts/base/infra-mng/configMap.yaml | 0 .../carts/base/infra-mng/ddb-table.yaml | 0 .../carts/base/infra-mng/kustomization.yaml | 0 .../webstore/carts/base/infra/configMap.yaml | 0 .../carts/base/infra/deployment-db.yaml | 0 .../carts/base/infra/kustomization.yaml | 0 .../webstore/carts/base/infra/service-db.yaml | 0 .../carts/base/kustomization-mng.yaml | 0 .../webstore/carts/base/kustomization.yaml | 0 .../webstore/carts/base/service.yaml | 0 .../webstore/carts/base/serviceAccount.yaml | 0 .../webstore/carts/hub/kustomization.yaml | 0 .../carts/prod/kustomization-mng.yaml | 0 .../webstore/carts/prod/kustomization.yaml | 0 .../carts/staging/kustomization-mng.yaml | 0 .../webstore/carts/staging/kustomization.yaml | 0 .../webstore/carts/staging/try-namespace.yaml | 0 .../webstore/catalog/base/configMap.yaml | 0 .../webstore/catalog/base/deployment.yaml | 0 .../webstore/catalog/base/kustomization.yaml | 0 .../webstore/catalog/base/secrets.yaml | 0 .../webstore/catalog/base/service-mysql.yaml | 0 .../webstore/catalog/base/service.yaml | 0 .../webstore/catalog/base/serviceAccount.yaml | 0 .../catalog/base/statefulset-mysql.yaml | 0 .../webstore/catalog/hub/kustomization.yaml | 0 .../webstore/catalog/prod/kustomization.yaml | 0 .../catalog/staging/kustomization.yaml | 0 .../webstore/checkout/base/configMap.yaml | 0 .../checkout/base/deployment-redis.yaml | 0 .../webstore/checkout/base/deployment.yaml | 0 .../webstore/checkout/base/kustomization.yaml | 0 .../webstore/checkout/base/service-redis.yaml | 0 .../webstore/checkout/base/service.yaml | 0 .../checkout/base/serviceAccount.yaml | 0 .../webstore/checkout/hub/kustomization.yaml | 0 .../webstore/checkout/prod/kustomization.yaml | 0 .../checkout/staging/kustomization.yaml | 0 .../webstore/orders/base/configMap.yaml | 0 .../orders/base/deployment-mysql.yaml | 0 .../webstore/orders/base/deployment.yaml | 0 .../webstore/orders/base/kustomization.yaml | 0 .../webstore/orders/base/secrets.yaml | 0 .../webstore/orders/base/service-mysql.yaml | 0 .../webstore/orders/base/service.yaml | 0 .../webstore/orders/base/serviceAccount.yaml | 0 .../webstore/orders/hub/kustomization.yaml | 0 .../webstore/orders/prod/kustomization.yaml | 0 .../orders/staging/kustomization.yaml | 0 .../webstore/ui/base/configMap.yaml | 0 .../webstore/ui/base/deployment.yaml | 0 .../webstore/ui/base/hpa.yaml | 0 .../webstore/ui/base/kustomization.yaml | 0 .../webstore/ui/base/nlb.yaml | 0 .../webstore/ui/base/service.yaml | 0 .../webstore/ui/base/serviceAccount.yaml | 0 .../webstore/ui/hub/kustomization.yaml | 0 .../webstore/ui/prod/hpa.yaml | 0 .../webstore/ui/prod/kustomization.yaml | 0 .../webstore/ui/staging/kustomization.yaml | 0 .../index.en.md => 010-prerequisites.md} | 8 +- content/010_prerequisites/010-fork-github.md | 44 -- .../030_base/030_create-vpc/010-create-vpc.md | 2 - .../030_base/035_gitrepo/010-create-repo.md | 467 ++++++++++++++++++ content/030_base/035_gitrepo/index.en.md | 19 + .../060_addons/030-update-metadata.md | 211 ++------ .../030_base/060_addons/033-argocd-repos.md | 62 +++ .../060_addons/040-addons-applicationset.md | 49 +- .../060_addons/050-loadbalancer-addon.md | 20 +- .../010-argocd-selfmanaged.md | 4 +- .../075_namespace/010-enable-namespace.md | 109 ++-- .../078_workload/010-deploy-workloads.md | 67 ++- content/030_base/index.en.md | 2 +- .../010-provision-spoke.md | 32 +- .../010-configure-hub-cluster.md | 8 +- .../020-configure-spoke-staging.md | 1 - .../030-test-hub-spoke-connectivity.md | 18 +- .../040_project/010-create-project.md | 46 +- .../010_hub_and_spoke/050-enable-namespace.md | 16 +- content/090_cleanup/index.en.md | 2 +- static/images/argocd-repositories.png | Bin 0 -> 40439 bytes static/images/argocd_k8s_repos.png | Bin 0 -> 9323 bytes static/images/asset-github-folders.png | Bin 0 -> 15703 bytes static/images/clone_starterfiles.png | Bin 0 -> 16841 bytes static/images/cluster-addons.png | Bin 28883 -> 33015 bytes static/images/codecommit_platform.png | Bin 0 -> 28646 bytes static/images/codecommit_repos.png | Bin 0 -> 36441 bytes static/images/local_platform.png | Bin 0 -> 13663 bytes static/images/namespace-begin.png | Bin 0 -> 13551 bytes .../namespace-create-webstore-namespace.png | Bin 0 -> 61448 bytes static/images/namespace-design.png | Bin 0 -> 32634 bytes .../namespace-namespace-applicationset.png | Bin 0 -> 9916 bytes ...espace-process-webstore-applicationset.png | Bin 0 -> 21465 bytes .../namespace-webstore-applicationset.png | Bin 0 -> 20167 bytes .../namespace-webstore-defalut-values.png | Bin 0 -> 20767 bytes static/images/platform-github-folders.png | Bin 85781 -> 73212 bytes static/images/project-applicationset.png | Bin 0 -> 25839 bytes static/images/project-values.png | Bin 0 -> 16064 bytes .../images/workload-appofapps-iteration.png | Bin 0 -> 27365 bytes static/images/workload-appofapps-monitor.png | Bin 0 -> 17725 bytes static/images/workload-appofapps.png | Bin 0 -> 12870 bytes static/images/workload-github-folders.png | Bin 0 -> 24129 bytes .../images/workload-webstore-deployment.png | Bin 0 -> 39447 bytes static/images/workload-webstore-folders.png | Bin 0 -> 42624 bytes static/images/workload-webstore.png | Bin 0 -> 22980 bytes ...espace-process-webstore-applicationset.png | Bin 0 -> 21465 bytes 117 files changed, 810 insertions(+), 397 deletions(-) create mode 100644 assets/scripts/destroy-git.sh rename assets/{developer => workload}/webstore/assets/base/configMap.yaml (100%) rename assets/{developer => workload}/webstore/assets/base/deployment.yaml (100%) rename assets/{developer => workload}/webstore/assets/base/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/assets/base/service.yaml (100%) rename assets/{developer => workload}/webstore/assets/base/serviceAccount.yaml (100%) rename assets/{developer => workload}/webstore/assets/hub/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/assets/prod/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/assets/staging/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/deployment.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/infra-mng/configMap.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/infra-mng/ddb-table.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/infra-mng/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/infra/configMap.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/infra/deployment-db.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/infra/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/infra/service-db.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/kustomization-mng.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/service.yaml (100%) rename assets/{developer => workload}/webstore/carts/base/serviceAccount.yaml (100%) rename assets/{developer => workload}/webstore/carts/hub/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/carts/prod/kustomization-mng.yaml (100%) rename assets/{developer => workload}/webstore/carts/prod/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/carts/staging/kustomization-mng.yaml (100%) rename assets/{developer => workload}/webstore/carts/staging/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/carts/staging/try-namespace.yaml (100%) rename assets/{developer => workload}/webstore/catalog/base/configMap.yaml (100%) rename assets/{developer => workload}/webstore/catalog/base/deployment.yaml (100%) rename assets/{developer => workload}/webstore/catalog/base/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/catalog/base/secrets.yaml (100%) rename assets/{developer => workload}/webstore/catalog/base/service-mysql.yaml (100%) rename assets/{developer => workload}/webstore/catalog/base/service.yaml (100%) rename assets/{developer => workload}/webstore/catalog/base/serviceAccount.yaml (100%) rename assets/{developer => workload}/webstore/catalog/base/statefulset-mysql.yaml (100%) rename assets/{developer => workload}/webstore/catalog/hub/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/catalog/prod/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/catalog/staging/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/checkout/base/configMap.yaml (100%) rename assets/{developer => workload}/webstore/checkout/base/deployment-redis.yaml (100%) rename assets/{developer => workload}/webstore/checkout/base/deployment.yaml (100%) rename assets/{developer => workload}/webstore/checkout/base/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/checkout/base/service-redis.yaml (100%) rename assets/{developer => workload}/webstore/checkout/base/service.yaml (100%) rename assets/{developer => workload}/webstore/checkout/base/serviceAccount.yaml (100%) rename assets/{developer => workload}/webstore/checkout/hub/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/checkout/prod/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/checkout/staging/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/orders/base/configMap.yaml (100%) rename assets/{developer => workload}/webstore/orders/base/deployment-mysql.yaml (100%) rename assets/{developer => workload}/webstore/orders/base/deployment.yaml (100%) rename assets/{developer => workload}/webstore/orders/base/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/orders/base/secrets.yaml (100%) rename assets/{developer => workload}/webstore/orders/base/service-mysql.yaml (100%) rename assets/{developer => workload}/webstore/orders/base/service.yaml (100%) rename assets/{developer => workload}/webstore/orders/base/serviceAccount.yaml (100%) rename assets/{developer => workload}/webstore/orders/hub/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/orders/prod/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/orders/staging/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/ui/base/configMap.yaml (100%) rename assets/{developer => workload}/webstore/ui/base/deployment.yaml (100%) rename assets/{developer => workload}/webstore/ui/base/hpa.yaml (100%) rename assets/{developer => workload}/webstore/ui/base/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/ui/base/nlb.yaml (100%) rename assets/{developer => workload}/webstore/ui/base/service.yaml (100%) rename assets/{developer => workload}/webstore/ui/base/serviceAccount.yaml (100%) rename assets/{developer => workload}/webstore/ui/hub/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/ui/prod/hpa.yaml (100%) rename assets/{developer => workload}/webstore/ui/prod/kustomization.yaml (100%) rename assets/{developer => workload}/webstore/ui/staging/kustomization.yaml (100%) rename content/{010_prerequisites/index.en.md => 010-prerequisites.md} (58%) delete mode 100644 content/010_prerequisites/010-fork-github.md create mode 100644 content/030_base/035_gitrepo/010-create-repo.md create mode 100644 content/030_base/035_gitrepo/index.en.md create mode 100644 content/030_base/060_addons/033-argocd-repos.md create mode 100644 static/images/argocd-repositories.png create mode 100644 static/images/argocd_k8s_repos.png create mode 100644 static/images/asset-github-folders.png create mode 100644 static/images/clone_starterfiles.png create mode 100644 static/images/codecommit_platform.png create mode 100644 static/images/codecommit_repos.png create mode 100644 static/images/local_platform.png create mode 100644 static/images/namespace-begin.png create mode 100644 static/images/namespace-create-webstore-namespace.png create mode 100644 static/images/namespace-design.png create mode 100644 static/images/namespace-namespace-applicationset.png create mode 100644 static/images/namespace-process-webstore-applicationset.png create mode 100644 static/images/namespace-webstore-applicationset.png create mode 100644 static/images/namespace-webstore-defalut-values.png create mode 100644 static/images/project-applicationset.png create mode 100644 static/images/project-values.png create mode 100644 static/images/workload-appofapps-iteration.png create mode 100644 static/images/workload-appofapps-monitor.png create mode 100644 static/images/workload-appofapps.png create mode 100644 static/images/workload-github-folders.png create mode 100644 static/images/workload-webstore-deployment.png create mode 100644 static/images/workload-webstore-folders.png create mode 100644 static/images/workload-webstore.png create mode 100644 static/namespace-process-webstore-applicationset.png diff --git a/assets/scripts/destroy-git.sh b/assets/scripts/destroy-git.sh new file mode 100644 index 0000000..7df8c38 --- /dev/null +++ b/assets/scripts/destroy-git.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -uo pipefail + +[[ -n "${DEBUG:-}" ]] && set -x + +cd ~/environment/codecommit/ + + +terraform destroy -auto-approve \ No newline at end of file diff --git a/assets/scripts/destroy.sh b/assets/scripts/destroy.sh index 0f69e74..9c0b7ab 100755 --- a/assets/scripts/destroy.sh +++ b/assets/scripts/destroy.sh @@ -2,15 +2,15 @@ set -uo pipefail -~/environment/wgit/assets/scripts/destroy-applications.sh +$SOURCE_DIR/assets/scripts/destroy-applications.sh -~/environment/wgit/assets/scripts/destroy-spoke.sh staging +$SOURCE_DIR/assets/scripts/destroy-spoke.sh staging -~/environment/wgit/assets/scripts/destroy-hub.sh +$SOURCE_DIR/assets/scripts/destroy-hub.sh +$SOURCE_DIR/assets/scripts/destroy-git.sh -~/environment/wgit/assets/scripts/destroy-vpc.sh - +$SOURCE_DIR/assets/scripts/destroy-vpc.sh diff --git a/assets/developer/webstore/assets/base/configMap.yaml b/assets/workload/webstore/assets/base/configMap.yaml similarity index 100% rename from assets/developer/webstore/assets/base/configMap.yaml rename to assets/workload/webstore/assets/base/configMap.yaml diff --git a/assets/developer/webstore/assets/base/deployment.yaml b/assets/workload/webstore/assets/base/deployment.yaml similarity index 100% rename from assets/developer/webstore/assets/base/deployment.yaml rename to assets/workload/webstore/assets/base/deployment.yaml diff --git a/assets/developer/webstore/assets/base/kustomization.yaml b/assets/workload/webstore/assets/base/kustomization.yaml similarity index 100% rename from assets/developer/webstore/assets/base/kustomization.yaml rename to assets/workload/webstore/assets/base/kustomization.yaml diff --git a/assets/developer/webstore/assets/base/service.yaml b/assets/workload/webstore/assets/base/service.yaml similarity index 100% rename from assets/developer/webstore/assets/base/service.yaml rename to assets/workload/webstore/assets/base/service.yaml diff --git a/assets/developer/webstore/assets/base/serviceAccount.yaml b/assets/workload/webstore/assets/base/serviceAccount.yaml similarity index 100% rename from assets/developer/webstore/assets/base/serviceAccount.yaml rename to assets/workload/webstore/assets/base/serviceAccount.yaml diff --git a/assets/developer/webstore/assets/hub/kustomization.yaml b/assets/workload/webstore/assets/hub/kustomization.yaml similarity index 100% rename from assets/developer/webstore/assets/hub/kustomization.yaml rename to assets/workload/webstore/assets/hub/kustomization.yaml diff --git a/assets/developer/webstore/assets/prod/kustomization.yaml b/assets/workload/webstore/assets/prod/kustomization.yaml similarity index 100% rename from assets/developer/webstore/assets/prod/kustomization.yaml rename to assets/workload/webstore/assets/prod/kustomization.yaml diff --git a/assets/developer/webstore/assets/staging/kustomization.yaml b/assets/workload/webstore/assets/staging/kustomization.yaml similarity index 100% rename from assets/developer/webstore/assets/staging/kustomization.yaml rename to assets/workload/webstore/assets/staging/kustomization.yaml diff --git a/assets/developer/webstore/carts/base/deployment.yaml b/assets/workload/webstore/carts/base/deployment.yaml similarity index 100% rename from assets/developer/webstore/carts/base/deployment.yaml rename to assets/workload/webstore/carts/base/deployment.yaml diff --git a/assets/developer/webstore/carts/base/infra-mng/configMap.yaml b/assets/workload/webstore/carts/base/infra-mng/configMap.yaml similarity index 100% rename from assets/developer/webstore/carts/base/infra-mng/configMap.yaml rename to assets/workload/webstore/carts/base/infra-mng/configMap.yaml diff --git a/assets/developer/webstore/carts/base/infra-mng/ddb-table.yaml b/assets/workload/webstore/carts/base/infra-mng/ddb-table.yaml similarity index 100% rename from assets/developer/webstore/carts/base/infra-mng/ddb-table.yaml rename to assets/workload/webstore/carts/base/infra-mng/ddb-table.yaml diff --git a/assets/developer/webstore/carts/base/infra-mng/kustomization.yaml b/assets/workload/webstore/carts/base/infra-mng/kustomization.yaml similarity index 100% rename from assets/developer/webstore/carts/base/infra-mng/kustomization.yaml rename to assets/workload/webstore/carts/base/infra-mng/kustomization.yaml diff --git a/assets/developer/webstore/carts/base/infra/configMap.yaml b/assets/workload/webstore/carts/base/infra/configMap.yaml similarity index 100% rename from assets/developer/webstore/carts/base/infra/configMap.yaml rename to assets/workload/webstore/carts/base/infra/configMap.yaml diff --git a/assets/developer/webstore/carts/base/infra/deployment-db.yaml b/assets/workload/webstore/carts/base/infra/deployment-db.yaml similarity index 100% rename from assets/developer/webstore/carts/base/infra/deployment-db.yaml rename to assets/workload/webstore/carts/base/infra/deployment-db.yaml diff --git a/assets/developer/webstore/carts/base/infra/kustomization.yaml b/assets/workload/webstore/carts/base/infra/kustomization.yaml similarity index 100% rename from assets/developer/webstore/carts/base/infra/kustomization.yaml rename to assets/workload/webstore/carts/base/infra/kustomization.yaml diff --git a/assets/developer/webstore/carts/base/infra/service-db.yaml b/assets/workload/webstore/carts/base/infra/service-db.yaml similarity index 100% rename from assets/developer/webstore/carts/base/infra/service-db.yaml rename to assets/workload/webstore/carts/base/infra/service-db.yaml diff --git a/assets/developer/webstore/carts/base/kustomization-mng.yaml b/assets/workload/webstore/carts/base/kustomization-mng.yaml similarity index 100% rename from assets/developer/webstore/carts/base/kustomization-mng.yaml rename to assets/workload/webstore/carts/base/kustomization-mng.yaml diff --git a/assets/developer/webstore/carts/base/kustomization.yaml b/assets/workload/webstore/carts/base/kustomization.yaml similarity index 100% rename from assets/developer/webstore/carts/base/kustomization.yaml rename to assets/workload/webstore/carts/base/kustomization.yaml diff --git a/assets/developer/webstore/carts/base/service.yaml b/assets/workload/webstore/carts/base/service.yaml similarity index 100% rename from assets/developer/webstore/carts/base/service.yaml rename to assets/workload/webstore/carts/base/service.yaml diff --git a/assets/developer/webstore/carts/base/serviceAccount.yaml b/assets/workload/webstore/carts/base/serviceAccount.yaml similarity index 100% rename from assets/developer/webstore/carts/base/serviceAccount.yaml rename to assets/workload/webstore/carts/base/serviceAccount.yaml diff --git a/assets/developer/webstore/carts/hub/kustomization.yaml b/assets/workload/webstore/carts/hub/kustomization.yaml similarity index 100% rename from assets/developer/webstore/carts/hub/kustomization.yaml rename to assets/workload/webstore/carts/hub/kustomization.yaml diff --git a/assets/developer/webstore/carts/prod/kustomization-mng.yaml b/assets/workload/webstore/carts/prod/kustomization-mng.yaml similarity index 100% rename from assets/developer/webstore/carts/prod/kustomization-mng.yaml rename to assets/workload/webstore/carts/prod/kustomization-mng.yaml diff --git a/assets/developer/webstore/carts/prod/kustomization.yaml b/assets/workload/webstore/carts/prod/kustomization.yaml similarity index 100% rename from assets/developer/webstore/carts/prod/kustomization.yaml rename to assets/workload/webstore/carts/prod/kustomization.yaml diff --git a/assets/developer/webstore/carts/staging/kustomization-mng.yaml b/assets/workload/webstore/carts/staging/kustomization-mng.yaml similarity index 100% rename from assets/developer/webstore/carts/staging/kustomization-mng.yaml rename to assets/workload/webstore/carts/staging/kustomization-mng.yaml diff --git a/assets/developer/webstore/carts/staging/kustomization.yaml b/assets/workload/webstore/carts/staging/kustomization.yaml similarity index 100% rename from assets/developer/webstore/carts/staging/kustomization.yaml rename to assets/workload/webstore/carts/staging/kustomization.yaml diff --git a/assets/developer/webstore/carts/staging/try-namespace.yaml b/assets/workload/webstore/carts/staging/try-namespace.yaml similarity index 100% rename from assets/developer/webstore/carts/staging/try-namespace.yaml rename to assets/workload/webstore/carts/staging/try-namespace.yaml diff --git a/assets/developer/webstore/catalog/base/configMap.yaml b/assets/workload/webstore/catalog/base/configMap.yaml similarity index 100% rename from assets/developer/webstore/catalog/base/configMap.yaml rename to assets/workload/webstore/catalog/base/configMap.yaml diff --git a/assets/developer/webstore/catalog/base/deployment.yaml b/assets/workload/webstore/catalog/base/deployment.yaml similarity index 100% rename from assets/developer/webstore/catalog/base/deployment.yaml rename to assets/workload/webstore/catalog/base/deployment.yaml diff --git a/assets/developer/webstore/catalog/base/kustomization.yaml b/assets/workload/webstore/catalog/base/kustomization.yaml similarity index 100% rename from assets/developer/webstore/catalog/base/kustomization.yaml rename to assets/workload/webstore/catalog/base/kustomization.yaml diff --git a/assets/developer/webstore/catalog/base/secrets.yaml b/assets/workload/webstore/catalog/base/secrets.yaml similarity index 100% rename from assets/developer/webstore/catalog/base/secrets.yaml rename to assets/workload/webstore/catalog/base/secrets.yaml diff --git a/assets/developer/webstore/catalog/base/service-mysql.yaml b/assets/workload/webstore/catalog/base/service-mysql.yaml similarity index 100% rename from assets/developer/webstore/catalog/base/service-mysql.yaml rename to assets/workload/webstore/catalog/base/service-mysql.yaml diff --git a/assets/developer/webstore/catalog/base/service.yaml b/assets/workload/webstore/catalog/base/service.yaml similarity index 100% rename from assets/developer/webstore/catalog/base/service.yaml rename to assets/workload/webstore/catalog/base/service.yaml diff --git a/assets/developer/webstore/catalog/base/serviceAccount.yaml b/assets/workload/webstore/catalog/base/serviceAccount.yaml similarity index 100% rename from assets/developer/webstore/catalog/base/serviceAccount.yaml rename to assets/workload/webstore/catalog/base/serviceAccount.yaml diff --git a/assets/developer/webstore/catalog/base/statefulset-mysql.yaml b/assets/workload/webstore/catalog/base/statefulset-mysql.yaml similarity index 100% rename from assets/developer/webstore/catalog/base/statefulset-mysql.yaml rename to assets/workload/webstore/catalog/base/statefulset-mysql.yaml diff --git a/assets/developer/webstore/catalog/hub/kustomization.yaml b/assets/workload/webstore/catalog/hub/kustomization.yaml similarity index 100% rename from assets/developer/webstore/catalog/hub/kustomization.yaml rename to assets/workload/webstore/catalog/hub/kustomization.yaml diff --git a/assets/developer/webstore/catalog/prod/kustomization.yaml b/assets/workload/webstore/catalog/prod/kustomization.yaml similarity index 100% rename from assets/developer/webstore/catalog/prod/kustomization.yaml rename to assets/workload/webstore/catalog/prod/kustomization.yaml diff --git a/assets/developer/webstore/catalog/staging/kustomization.yaml b/assets/workload/webstore/catalog/staging/kustomization.yaml similarity index 100% rename from assets/developer/webstore/catalog/staging/kustomization.yaml rename to assets/workload/webstore/catalog/staging/kustomization.yaml diff --git a/assets/developer/webstore/checkout/base/configMap.yaml b/assets/workload/webstore/checkout/base/configMap.yaml similarity index 100% rename from assets/developer/webstore/checkout/base/configMap.yaml rename to assets/workload/webstore/checkout/base/configMap.yaml diff --git a/assets/developer/webstore/checkout/base/deployment-redis.yaml b/assets/workload/webstore/checkout/base/deployment-redis.yaml similarity index 100% rename from assets/developer/webstore/checkout/base/deployment-redis.yaml rename to assets/workload/webstore/checkout/base/deployment-redis.yaml diff --git a/assets/developer/webstore/checkout/base/deployment.yaml b/assets/workload/webstore/checkout/base/deployment.yaml similarity index 100% rename from assets/developer/webstore/checkout/base/deployment.yaml rename to assets/workload/webstore/checkout/base/deployment.yaml diff --git a/assets/developer/webstore/checkout/base/kustomization.yaml b/assets/workload/webstore/checkout/base/kustomization.yaml similarity index 100% rename from assets/developer/webstore/checkout/base/kustomization.yaml rename to assets/workload/webstore/checkout/base/kustomization.yaml diff --git a/assets/developer/webstore/checkout/base/service-redis.yaml b/assets/workload/webstore/checkout/base/service-redis.yaml similarity index 100% rename from assets/developer/webstore/checkout/base/service-redis.yaml rename to assets/workload/webstore/checkout/base/service-redis.yaml diff --git a/assets/developer/webstore/checkout/base/service.yaml b/assets/workload/webstore/checkout/base/service.yaml similarity index 100% rename from assets/developer/webstore/checkout/base/service.yaml rename to assets/workload/webstore/checkout/base/service.yaml diff --git a/assets/developer/webstore/checkout/base/serviceAccount.yaml b/assets/workload/webstore/checkout/base/serviceAccount.yaml similarity index 100% rename from assets/developer/webstore/checkout/base/serviceAccount.yaml rename to assets/workload/webstore/checkout/base/serviceAccount.yaml diff --git a/assets/developer/webstore/checkout/hub/kustomization.yaml b/assets/workload/webstore/checkout/hub/kustomization.yaml similarity index 100% rename from assets/developer/webstore/checkout/hub/kustomization.yaml rename to assets/workload/webstore/checkout/hub/kustomization.yaml diff --git a/assets/developer/webstore/checkout/prod/kustomization.yaml b/assets/workload/webstore/checkout/prod/kustomization.yaml similarity index 100% rename from assets/developer/webstore/checkout/prod/kustomization.yaml rename to assets/workload/webstore/checkout/prod/kustomization.yaml diff --git a/assets/developer/webstore/checkout/staging/kustomization.yaml b/assets/workload/webstore/checkout/staging/kustomization.yaml similarity index 100% rename from assets/developer/webstore/checkout/staging/kustomization.yaml rename to assets/workload/webstore/checkout/staging/kustomization.yaml diff --git a/assets/developer/webstore/orders/base/configMap.yaml b/assets/workload/webstore/orders/base/configMap.yaml similarity index 100% rename from assets/developer/webstore/orders/base/configMap.yaml rename to assets/workload/webstore/orders/base/configMap.yaml diff --git a/assets/developer/webstore/orders/base/deployment-mysql.yaml b/assets/workload/webstore/orders/base/deployment-mysql.yaml similarity index 100% rename from assets/developer/webstore/orders/base/deployment-mysql.yaml rename to assets/workload/webstore/orders/base/deployment-mysql.yaml diff --git a/assets/developer/webstore/orders/base/deployment.yaml b/assets/workload/webstore/orders/base/deployment.yaml similarity index 100% rename from assets/developer/webstore/orders/base/deployment.yaml rename to assets/workload/webstore/orders/base/deployment.yaml diff --git a/assets/developer/webstore/orders/base/kustomization.yaml b/assets/workload/webstore/orders/base/kustomization.yaml similarity index 100% rename from assets/developer/webstore/orders/base/kustomization.yaml rename to assets/workload/webstore/orders/base/kustomization.yaml diff --git a/assets/developer/webstore/orders/base/secrets.yaml b/assets/workload/webstore/orders/base/secrets.yaml similarity index 100% rename from assets/developer/webstore/orders/base/secrets.yaml rename to assets/workload/webstore/orders/base/secrets.yaml diff --git a/assets/developer/webstore/orders/base/service-mysql.yaml b/assets/workload/webstore/orders/base/service-mysql.yaml similarity index 100% rename from assets/developer/webstore/orders/base/service-mysql.yaml rename to assets/workload/webstore/orders/base/service-mysql.yaml diff --git a/assets/developer/webstore/orders/base/service.yaml b/assets/workload/webstore/orders/base/service.yaml similarity index 100% rename from assets/developer/webstore/orders/base/service.yaml rename to assets/workload/webstore/orders/base/service.yaml diff --git a/assets/developer/webstore/orders/base/serviceAccount.yaml b/assets/workload/webstore/orders/base/serviceAccount.yaml similarity index 100% rename from assets/developer/webstore/orders/base/serviceAccount.yaml rename to assets/workload/webstore/orders/base/serviceAccount.yaml diff --git a/assets/developer/webstore/orders/hub/kustomization.yaml b/assets/workload/webstore/orders/hub/kustomization.yaml similarity index 100% rename from assets/developer/webstore/orders/hub/kustomization.yaml rename to assets/workload/webstore/orders/hub/kustomization.yaml diff --git a/assets/developer/webstore/orders/prod/kustomization.yaml b/assets/workload/webstore/orders/prod/kustomization.yaml similarity index 100% rename from assets/developer/webstore/orders/prod/kustomization.yaml rename to assets/workload/webstore/orders/prod/kustomization.yaml diff --git a/assets/developer/webstore/orders/staging/kustomization.yaml b/assets/workload/webstore/orders/staging/kustomization.yaml similarity index 100% rename from assets/developer/webstore/orders/staging/kustomization.yaml rename to assets/workload/webstore/orders/staging/kustomization.yaml diff --git a/assets/developer/webstore/ui/base/configMap.yaml b/assets/workload/webstore/ui/base/configMap.yaml similarity index 100% rename from assets/developer/webstore/ui/base/configMap.yaml rename to assets/workload/webstore/ui/base/configMap.yaml diff --git a/assets/developer/webstore/ui/base/deployment.yaml b/assets/workload/webstore/ui/base/deployment.yaml similarity index 100% rename from assets/developer/webstore/ui/base/deployment.yaml rename to assets/workload/webstore/ui/base/deployment.yaml diff --git a/assets/developer/webstore/ui/base/hpa.yaml b/assets/workload/webstore/ui/base/hpa.yaml similarity index 100% rename from assets/developer/webstore/ui/base/hpa.yaml rename to assets/workload/webstore/ui/base/hpa.yaml diff --git a/assets/developer/webstore/ui/base/kustomization.yaml b/assets/workload/webstore/ui/base/kustomization.yaml similarity index 100% rename from assets/developer/webstore/ui/base/kustomization.yaml rename to assets/workload/webstore/ui/base/kustomization.yaml diff --git a/assets/developer/webstore/ui/base/nlb.yaml b/assets/workload/webstore/ui/base/nlb.yaml similarity index 100% rename from assets/developer/webstore/ui/base/nlb.yaml rename to assets/workload/webstore/ui/base/nlb.yaml diff --git a/assets/developer/webstore/ui/base/service.yaml b/assets/workload/webstore/ui/base/service.yaml similarity index 100% rename from assets/developer/webstore/ui/base/service.yaml rename to assets/workload/webstore/ui/base/service.yaml diff --git a/assets/developer/webstore/ui/base/serviceAccount.yaml b/assets/workload/webstore/ui/base/serviceAccount.yaml similarity index 100% rename from assets/developer/webstore/ui/base/serviceAccount.yaml rename to assets/workload/webstore/ui/base/serviceAccount.yaml diff --git a/assets/developer/webstore/ui/hub/kustomization.yaml b/assets/workload/webstore/ui/hub/kustomization.yaml similarity index 100% rename from assets/developer/webstore/ui/hub/kustomization.yaml rename to assets/workload/webstore/ui/hub/kustomization.yaml diff --git a/assets/developer/webstore/ui/prod/hpa.yaml b/assets/workload/webstore/ui/prod/hpa.yaml similarity index 100% rename from assets/developer/webstore/ui/prod/hpa.yaml rename to assets/workload/webstore/ui/prod/hpa.yaml diff --git a/assets/developer/webstore/ui/prod/kustomization.yaml b/assets/workload/webstore/ui/prod/kustomization.yaml similarity index 100% rename from assets/developer/webstore/ui/prod/kustomization.yaml rename to assets/workload/webstore/ui/prod/kustomization.yaml diff --git a/assets/developer/webstore/ui/staging/kustomization.yaml b/assets/workload/webstore/ui/staging/kustomization.yaml similarity index 100% rename from assets/developer/webstore/ui/staging/kustomization.yaml rename to assets/workload/webstore/ui/staging/kustomization.yaml diff --git a/content/010_prerequisites/index.en.md b/content/010-prerequisites.md similarity index 58% rename from content/010_prerequisites/index.en.md rename to content/010-prerequisites.md index 7efb29b..6d65808 100644 --- a/content/010_prerequisites/index.en.md +++ b/content/010-prerequisites.md @@ -7,8 +7,6 @@ weight: 10 1. Basic knowledge of [Terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started), Terraform [workspaces](https://developer.hashicorp.com/terraform/language/state/workspaces) 2. Basic knowledge of [Argo CD](https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/) Application, ApplicationSet, Projects, App of App pattern and Generator concepts - Cluster, Git, and Matrix, -3. GitHub account -4. Familiarity with GitHub cli commands clone, commit, pull, push -5. GitHub [access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token) -6. Basic knowledge of [GitOps Bridge](https://github.com/gitops-bridge-dev/kubecon-2023-na-argocon/blob/main/terraform/eks-argocd/README.md) -7. Basic knowledge of [Helm](https://helm.sh/docs/) +3. Familiarity with Git cli commands clone, commit, pull, push +4. Basic knowledge of [GitOps Bridge](https://github.com/gitops-bridge-dev/kubecon-2023-na-argocon/blob/main/terraform/eks-argocd/README.md) +5. Basic knowledge of [Helm](https://helm.sh/docs/) diff --git a/content/010_prerequisites/010-fork-github.md b/content/010_prerequisites/010-fork-github.md deleted file mode 100644 index a0ca85c..0000000 --- a/content/010_prerequisites/010-fork-github.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: 'Fork workshop repo' -weight: 10 ---- - -### 1. Github account - -You need to have a GitHub account. - -If you don't have an account - -1. Navigate to https://github.com/ -2. Click Sign up -3. Follow the prompts to create an account - -### 2. Fork the workshop repository -Forking a repository makes a copy of the workshop files into your GitHub account. You will update your repository throughout the workshop. - -1. Navigate to https://github.com/aws-samples/eks-blueprints-for-terraform-workshop -2. Click on the arrow near the **Fork** button and choose "**Create a new fork**" - - ![GitHub Fork](/static/images/github-fork.png) -3. Under "Owner," select the dropdown menu and click your github for the forked repository -4. Keep the repository name as eks-blueprints-for-terraform-workshop -5. Click **Create fork** - -### 3. Generate GitHub access token -Throughout the workshop you will be updating the forked repository. You will use token to authenticate to GitHub. - -1. Navigate to https://github.com -2. In the upper-right corner of any page, click your profile photo, then click Settings -![GitHub Fork](/static/images/github-setting.png) -3. On the left sidebar, at the bottom, click **Developer settings** -4. In the left sidebar, under **Personal access tokens**, click **Fine-grained tokens** -5. Click **Generate new token** button. Use the default value for any field that is not explicitly specified in the steps below. -6. Under **Token name**, enter a name for the token -7. Under Expiration, select an expiration for the token -8. Under **Repository access**, click either **All repositoties** or **Only select repositories** to restrict the token on and select the forked **eks-blueprints-for-terraform-workshop** repository -9. Under Permissions, select **Repository Permissions** -10. Select *Contents* dropdown and select "Read and Write" - - ![GitHub permissions](/static/images/github-permission.png) -11. Click **Generate Token**. Store this token safely. You will need this token throughout the workshop. If you loose it, generate a new token. - diff --git a/content/030_base/030_create-vpc/010-create-vpc.md b/content/030_base/030_create-vpc/010-create-vpc.md index 7bc8f59..44e81cf 100644 --- a/content/030_base/030_create-vpc/010-create-vpc.md +++ b/content/030_base/030_create-vpc/010-create-vpc.md @@ -160,6 +160,4 @@ terraform apply -auto-approve Once completed, you can see the VPC in the [console](https://console.aws.amazon.com/vpc/home?#vpcs:tag:Name=eks-blueprint) -Next, you will create an EKS cluster. - ::alert[This workshop uses local Terraform state. To learn about a proper setup, take a look at https://www.terraform.io/language/state]{header="Terraform State Management"} diff --git a/content/030_base/035_gitrepo/010-create-repo.md b/content/030_base/035_gitrepo/010-create-repo.md new file mode 100644 index 0000000..87850eb --- /dev/null +++ b/content/030_base/035_gitrepo/010-create-repo.md @@ -0,0 +1,467 @@ +--- +title: 'Create CodeCommit Repository' +weight: 10 +--- + +### 1. Create Terraform providers + +Define Terraform and providers versions + +```bash +mkdir -p ~/environment/codecommit +cd ~/environment/codecommit +cat > ~/environment/codecommit/versions.tf << 'EOF' +terraform { + required_version = ">= 1.4.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0.0" + } + random = { + version = ">= 3" + } + } +} +EOF +``` + +### 2. Define variables + +Define values for CodeCommit repository names and repo path within the repositories. + +```bash +cat > ~/environment/codecommit/variables.tf << 'EOF' + + +variable "ssh_key_basepath" { + description = "path to .ssh directory" + type = string + default = "~/.ssh" +} + +variable "gitops_addons_basepath" { + description = "Git repository base path for addons" + default = "addons/" +} +variable "gitops_addons_path" { + description = "Git repository path for addons" + default = "applicationset/" +} +variable "gitops_addons_revision" { + description = "Git repository revision/branch/ref for addons" + default = "HEAD" +} +variable "gitops_addons_repo_name" { + description = "Git repository name for addons" + default = "gitops-platform" +} + +variable "gitops_platform_basepath" { + description = "Git repository base path for platform" + default = "" +} +variable "gitops_platform_path" { + description = "Git repository path for workload" + default = "bootstrap" +} +variable "gitops_platform_revision" { + description = "Git repository revision/branch/ref for workload" + default = "HEAD" +} +variable "gitops_platform_repo_name" { + description = "Git repository name for platform" + default = "gitops-platform" +} + +variable "gitops_workload_basepath" { + description = "Git repository base path for workload" + default = "" +} +variable "gitops_workload_path" { + description = "Git repository path for workload" + default = "" +} +variable "gitops_workload_revision" { + description = "Git repository revision/branch/ref for workload" + default = "HEAD" +} +variable "gitops_workload_repo_name" { + description = "Git repository name for workload" + default = "gitops-workload" +} + +EOF +``` + +### 3. Create repositories + +Create repositories, IAM user and configure the user to access repositories. + +```bash +cat > ~/environment/codecommit/main.tf <<'EOF' + + +data "aws_region" "current" {} + +locals { + + context_prefix = "terraform-workshop" + + gitops_workload_repo_name = var.gitops_workload_repo_name + gitops_workload_org = "ssh://${aws_iam_user_ssh_key.gitops.id}@git-codecommit.${data.aws_region.current.id}.amazonaws.com" + gitops_workload_repo = "v1/repos/${local.gitops_workload_repo_name}" + + gitops_platform_repo_name = var.gitops_platform_repo_name + gitops_platform_org = "ssh://${aws_iam_user_ssh_key.gitops.id}@git-codecommit.${data.aws_region.current.id}.amazonaws.com" + gitops_platform_repo = "v1/repos/${local.gitops_platform_repo_name}" + + gitops_addons_repo_name = var.gitops_addons_repo_name + gitops_addons_org = "ssh://${aws_iam_user_ssh_key.gitops.id}@git-codecommit.${data.aws_region.current.id}.amazonaws.com" + gitops_addons_repo = "v1/repos/${local.gitops_addons_repo_name}" + + ssh_key_basepath = var.ssh_key_basepath + git_private_ssh_key = "${local.ssh_key_basepath}/gitops_ssh.pem" + git_private_ssh_key_config = "${local.ssh_key_basepath}/config" + ssh_host = "git-codecommit.*.amazonaws.com" + ssh_config = <<-EOF + # AWS Workshop https://github.com/aws-samples/argocd-on-amazon-eks-workshop.git + Host ${local.ssh_host} + User ${aws_iam_user_ssh_key.gitops.id} + IdentityFile ${local.git_private_ssh_key} + EOF + +} + +resource "aws_codecommit_repository" "workloads" { + repository_name = local.gitops_workload_repo_name + description = "CodeCommit repository for ArgoCD workloads" +} + +resource "aws_codecommit_repository" "platform" { + repository_name = local.gitops_platform_repo_name + description = "CodeCommit repository for ArgoCD platform" +} + + +resource "aws_iam_user" "gitops" { + name = "${local.context_prefix}-gitops" + path = "/" +} + +resource "aws_iam_user_ssh_key" "gitops" { + username = aws_iam_user.gitops.name + encoding = "SSH" + public_key = tls_private_key.gitops.public_key_openssh +} + +resource "tls_private_key" "gitops" { + algorithm = "RSA" + rsa_bits = 4096 +} + +resource "random_string" "secret_suffix" { + length = 5 # Length of the random string + special = false # Set to true if you want to include special characters + upper = true # Set to true if you want uppercase letters in the string + lower = true # Set to true if you want lowercase letters in the string + number = true # Set to true if you want numbers in the string +} +resource "aws_secretsmanager_secret" "codecommit_key" { + name = "codecommit-key-${random_string.secret_suffix.result}" +} + +resource "aws_secretsmanager_secret_version" "private_key_secret_version" { + secret_id = aws_secretsmanager_secret.codecommit_key.id + secret_string = tls_private_key.gitops.private_key_pem +} + +resource "local_file" "ssh_private_key" { + content = tls_private_key.gitops.private_key_pem + filename = pathexpand(local.git_private_ssh_key) + file_permission = "0600" +} + +resource "local_file" "ssh_config" { + count = local.ssh_key_basepath == "/home/ec2-user/.ssh" ? 1 : 0 + content = local.ssh_config + filename = pathexpand(local.git_private_ssh_key_config) + file_permission = "0600" + + # Ensure that the local_file resource is created/updated after the local-exec provisioner + depends_on = [null_resource.append_string_block] +} + +resource "null_resource" "append_string_block" { + count = local.ssh_key_basepath == "/home/ec2-user/.ssh" ? 0 : 1 + triggers = { + always_run = "${timestamp()}" + file = pathexpand(local.git_private_ssh_key_config) + } + + provisioner "local-exec" { + when = create + command = <<-EOL + start_marker="### START BLOCK AWS Workshop ###" + end_marker="### END BLOCK AWS Workshop ###" + block="$start_marker\n${replace(local.ssh_config, "\n", "\n")}\n$end_marker" + file="${self.triggers.file}" + + if ! grep -q "$start_marker" "$file"; then + echo "$block" >> "$file" + fi + EOL + } + + provisioner "local-exec" { + when = destroy + command = <<-EOL + start_marker="### START BLOCK AWS Workshop ###" + end_marker="### END BLOCK AWS Workshop ###" + file="${self.triggers.file}" + + if grep -q "$start_marker" "$file"; then + sed -i "/$start_marker/,/$end_marker/d" "$file" + fi + EOL + + } +} + + +data "aws_iam_policy_document" "gitops_access" { + statement { + sid = "" + actions = [ + "codecommit:GitPull", + "codecommit:GitPush" + ] + effect = "Allow" + resources = [ + aws_codecommit_repository.workloads.arn, + aws_codecommit_repository.platform.arn, + ] + } +} + +resource "aws_iam_policy" "gitops_access" { + name = "${local.context_prefix}-gitops" + path = "/" + policy = data.aws_iam_policy_document.gitops_access.json +} + +resource "aws_iam_user_policy_attachment" "gitops_access" { + user = aws_iam_user.gitops.name + policy_arn = aws_iam_policy.gitops_access.arn +} + + +EOF +``` + +### 4. Create outputs + +The outputs are referenced in upcoming chapters. + +```bash +cat > ~/environment/codecommit/outputs.tf <<'EOF' +output "configure_argocd" { + value = "argocd repo add ${local.gitops_workload_org}/${local.gitops_workload_repo} --ssh-private-key-path $${HOME}/.ssh/gitops_ssh.pem --insecure-ignore-host-key --upsert --name git-repo" +} +output "git_clone" { + value = "git clone ${local.gitops_workload_org}/${local.gitops_workload_repo}" +} +output "ssh_config" { + value = local.ssh_config +} +output "ssh_host" { + value = local.ssh_host +} + +output "git_private_ssh_key" { + value = local.git_private_ssh_key +} + +output "gitops_addons_url" { + value = "${local.gitops_addons_org}/${local.gitops_addons_repo}" +} +output "gitops_addons_org" { + description = "Git repository org/user contains for addons" + value = local.gitops_addons_org +} +output "gitops_addons_repo" { + description = "Git repository contains for addons" + value = local.gitops_addons_repo +} +output "gitops_addons_basepath" { + description = "Git repository base path for addons" + value = var.gitops_addons_basepath +} +output "gitops_addons_path" { + description = "Git repository path for addons" + value = var.gitops_addons_path +} +output "gitops_addons_revision" { + description = "Git repository revision/branch/ref for addons" + value = var.gitops_addons_revision +} + +output "gitops_platform_url" { + value = "${local.gitops_platform_org}/${local.gitops_platform_repo}" +} +output "gitops_platform_org" { + description = "Git repository org/user contains for platform" + value = local.gitops_platform_org +} +output "gitops_platform_repo" { + description = "Git repository contains for platform" + value = local.gitops_workload_repo +} +output "gitops_platform_basepath" { + description = "Git repository base path for platform" + value = var.gitops_platform_basepath +} +output "gitops_platform_path" { + description = "Git repository path for platform" + value = var.gitops_platform_path +} +output "gitops_platform_revision" { + description = "Git repository revision/branch/ref for platform" + value = var.gitops_platform_revision +} + +output "gitops_workload_url" { + value = "${local.gitops_workload_org}/${local.gitops_workload_repo}" +} +output "gitops_workload_org" { + description = "Git repository org/user contains for workload" + value = local.gitops_workload_org +} +output "gitops_workload_repo" { + description = "Git repository contains for workload" + value = local.gitops_workload_repo +} +output "gitops_workload_basepath" { + description = "Git repository base path for workload" + value = var.gitops_workload_basepath +} +output "gitops_workload_path" { + description = "Git repository path for workload" + value = var.gitops_workload_path +} +output "gitops_workload_revision" { + description = "Git repository revision/branch/ref for workload" + value = var.gitops_workload_revision +} +output "codecommit_key_id" { + description = "Secret name that holds the SSH key for accessing CodeCommit" + value = aws_secretsmanager_secret.codecommit_key.id +} +output "codecommit_key_name" { + description = "Secret name that holds the SSH key for accessing CodeCommit" + value = aws_secretsmanager_secret.codecommit_key.name +} + +EOF +``` +### 5. Provision CodeCommit repositories + +```bash +cd ~/environment/codecommit +terraform init +terraform apply --auto-approve +``` + +## Populate git repositories + +The CodeCommit repositories will be populated with starter files first. These starter files will provide a foundation for the workshop. In the following workshop chapters, we will build on top of these starter files. +### 1. Set environment variables + +```bash +export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account) +export AWS_DEFAULT_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region') +export WORKING_DIR="$HOME/environment" +export SOURCE_DIR="$WORKING_DIR/source/assets" +export SCRIPT_DIR="$SOURCE_DIR/scripts" +export GITOPS_DIR="$WORKING_DIR/gitops-repos" + + +echo "export ACCOUNT_ID=${ACCOUNT_ID}" | tee -a ~/.bash_profile +echo "export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}" | tee -a ~/.bash_profile +echo "export WORKING_DIR=${WORKING_DIR}" | tee -a ~/.bash_profile +echo "export SOURCE_DIR=${SOURCE_DIR}" | tee -a ~/.bash_profile +echo "export SCRIPT_DIR=${SCRIPT_DIR}" | tee -a ~/.bash_profile +echo "export GITOPS_DIR=${GITOPS_DIR}" | tee -a ~/.bash_profile +source ~/.bash_profile + +``` + + + +### 2. Clone starter files + +Clone webstore workload , starter files for platform and cleanup scripts. + +![Clone Repository](/static/images/clone_starterfiles.png) + +```bash +cd $WORKING_DIR +git clone --depth 1 --no-checkout https://github.com/aws-samples/eks-blueprints-for-terraform-workshop source +cd source +git sparse-checkout set assets +git checkout +cd $WORKING_DIR + +``` +::::expand{header="What is in my cloned repo?"} +This repository contains resources for managing Kubernetes clusters in the **assets** directory. It includes Kubernetes YAML files for deploying workloads, ApplicationSets, and configuration values for addons, namespaces, and projects. + +Asset Folder:![Asset Folders](/static/images/asset-github-folders.png) + +Platform Folder:![Platform Folders](/static/images/platform-github-folders.png) + +Webstore Workload Folder:![Workload Folders](/static/images/workload-github-folders.png) +:::: + +### 3. Populate codecommit gitops-platform repository + +Copy platform starter files to "gitops-repos" folder from the cloned repository. + +![Local Platform](/static/images/local_platform.png) + +Push "gitops-repos" platform folder to codecommit "gitops-platform" repository + +![CodeCommit Platform](/static/images/codecommit_platform.png) + + +```bash +mkdir -p ${GITOPS_DIR} +gitops_platform_url=ssh://git-codecommit.${AWS_DEFAULT_REGION}.amazonaws.com/v1/repos/gitops-platform +# populate platform repository +ssh-keyscan -H git-codecommit.${AWS_REGION}.amazonaws.com &> ~/.ssh/known_hosts +git clone ${gitops_platform_url} ${GITOPS_DIR}/platform +cp -r $SOURCE_DIR/platform/* ${GITOPS_DIR}/platform +cd ${GITOPS_DIR}/platform +git -C ${GITOPS_DIR}/platform add . || true +git -C ${GITOPS_DIR}/platform commit -m "initial commit" || true +git -C ${GITOPS_DIR}/platform push || true +``` + + + +### 4. Populate codecommit gitops-workload repository + + +```bash +cd ~/environment +gitops_workload_url=ssh://git-codecommit.${AWS_DEFAULT_REGION}.amazonaws.com/v1/repos/gitops-workload +# populate workload repository +git clone ${gitops_workload_url} ${GITOPS_DIR}/workload +cp -r $SOURCE_DIR/workload/* ${GITOPS_DIR}/workload +cd ${GITOPS_DIR}/workload +git -C ${GITOPS_DIR}/workload add . || true +git -C ${GITOPS_DIR}/workload commit -m "initial commit" || true +git -C ${GITOPS_DIR}/workload push || true + +``` \ No newline at end of file diff --git a/content/030_base/035_gitrepo/index.en.md b/content/030_base/035_gitrepo/index.en.md new file mode 100644 index 0000000..64cf798 --- /dev/null +++ b/content/030_base/035_gitrepo/index.en.md @@ -0,0 +1,19 @@ +--- +title: 'Setup Git Repository' +weight: 35 +--- + +In this workshop you are going to create two codecommit repositories: + +![CodeCommit Repository](/static/images/codecommit_repos.png) + +1. "gitops-workload" for developers to store Kubernetes manifests for webstore microservices workload + +2. "gitops-platform" for platform engineers to store infrastructure artifacts like addons, application deployment, etc. + +The separation of the workload and platform repositories between developers and platform engineers illustrates a separation of roles and responsibilities. Using CodeCommit provides a managed git service on AWS. Creating the IAM user allows controlled access to the repositories. + +This workshop creates "terraform-workshop-gitops" IAM user to [access](https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-ssh-unixes.html#setting-up-ssh-unixes-keys) both repositories. + +* Creates policy to access two repositories +* Configures SSH access to the repositories with public and private key diff --git a/content/030_base/060_addons/030-update-metadata.md b/content/030_base/060_addons/030-update-metadata.md index ba19c76..881f405 100644 --- a/content/030_base/060_addons/030-update-metadata.md +++ b/content/030_base/060_addons/030-update-metadata.md @@ -13,132 +13,60 @@ In the Argo CD user interface, go to the hub cluster. The hub-cluster currently > Labels can be used to find collections of objects that satisfy generator conditions. Annotations provide additional information. -### 1. Add variables -You can have separate git repository for addons, platform and workloads. In this workshop they all exist in the same repository. -Define Git repository variables for addons, platform, and workloads. These repository variables will be referenced in upcoming chapters when generating Applications. +### 1. Codecommit Remote State +The hub cluster references codecommit module outputs. ```json -cat <<'EOF' >> ~/environment/hub/variables.tf -variable "gitops_addons_url" { - type = string - description = "Git repository addons url" - default = "https://github.com/aws-samples/eks-blueprints-for-terraform-workshop.git" -} -variable "gitops_platform_url" { - type = string - description = "Git repository platform url" - default = "https://github.com/aws-samples/eks-blueprints-for-terraform-workshop.git" -} -variable "gitops_workload_url" { - type = string - description = "Git repository platform url" - default = "https://github.com/aws-samples/eks-blueprints-for-terraform-workshop.git" -} -variable "gitops_addons_basepath" { - type = string - description = "Git repository base path for addons" - default = "assets/platform/addons/" -} -variable "gitops_addons_path" { - type = string - description = "Git repository path for addons" - default = "applicationset/" -} -variable "gitops_addons_revision" { - type = string - description = "Git repository revision/branch/ref for addons" - default = "HEAD" -} -variable "gitops_platform_basepath" { - type = string - description = "Git repository base path for platform" - default = "assets/platform/" -} -variable "gitops_platform_path" { - type = string - description = "Git repository path for platform" - default = "bootstrap" -} -variable "gitops_platform_revision" { - type = string - description = "Git repository revision/branch/ref for platform" - default = "HEAD" -} -variable "gitops_workload_basepath" { - type = string - description = "Git repository base path for platform" - default = "assets/developer/" -} -variable "gitops_workload_path" { - type = string - description = "Git repository path for workload" - default = "gitops/apps" -} -variable "gitops_workload_revision" { - type = string - description = "Git repository revision/branch/ref for platform" - default = "HEAD" +cat <<'EOF' >> ~/environment/hub/remote_state.tf + +data "terraform_remote_state" "git" { + backend = "local" + + config = { + path = "${path.module}/../codecommit/terraform.tfstate" + } } EOF ``` -### Set git Values - -Copy the provided code snippet, replace the placeholder value "<>" with your actual GitHub User Name, used to fork the repository. We use the full HTTPS clone URL **Then you can proceed.**. ->If you have clone the repo in another organisation than you're GitHub User, you can also update the GITHUB_LOGIN with your org name. - -```bash -export GITHUB_USER="<>" -export GITHUB_LOGIN=$GITHUB_USER -export GITHUB_TOKEN="<> -``` +### 2. Reference Codecommit outputs values ```json -cat <> ~/environment/terraform.tfvars -gitops_addons_url = "https://github.com/${GITHUB_LOGIN}/eks-blueprints-for-terraform-workshop.git" -gitops_platform_url = "https://github.com/${GITHUB_LOGIN}/eks-blueprints-for-terraform-workshop.git" -gitops_workload_url = "https://github.com/${GITHUB_LOGIN}/eks-blueprints-for-terraform-workshop.git" - -addons = { - enable_aws_load_balancer_controller = false - enable_aws_argocd = false +cat <<'EOF' >> ~/environment/hub/main.tf +locals{ + + gitops_addons_url = data.terraform_remote_state.git.outputs.gitops_addons_url + gitops_addons_basepath = data.terraform_remote_state.git.outputs.gitops_addons_basepath + gitops_addons_path = data.terraform_remote_state.git.outputs.gitops_addons_path + gitops_addons_revision = data.terraform_remote_state.git.outputs.gitops_addons_revision + + gitops_platform_url = data.terraform_remote_state.git.outputs.gitops_platform_url + gitops_platform_basepath = data.terraform_remote_state.git.outputs.gitops_platform_basepath + gitops_platform_path = data.terraform_remote_state.git.outputs.gitops_platform_path + gitops_platform_revision = data.terraform_remote_state.git.outputs.gitops_platform_revision + + gitops_workload_url = data.terraform_remote_state.git.outputs.gitops_workload_url + gitops_workload_basepath = data.terraform_remote_state.git.outputs.gitops_workload_basepath + gitops_workload_path = data.terraform_remote_state.git.outputs.gitops_workload_path + gitops_workload_revision = data.terraform_remote_state.git.outputs.gitops_workload_revision + } EOF ``` -::alert[Check the file is correctly filled]{header="Important" type="warning"} -```bash -c9 open ~/environment/terraform.tfvars -``` - -Example: -``` -eks_admin_role_name = "WSParticipantRole" -gitops_addons_url = "https://github.com/seb-workshop/eks-blueprints-for-terraform-workshop.git" -gitops_platform_url = "https://github.com/seb-workshop/eks-blueprints-for-terraform-workshop.git" -gitops_workload_url = "https://github.com/seb-workshop/eks-blueprints-for-terraform-workshop.git" - -addons = { - enable_aws_load_balancer_controller = false - enable_aws_argocd = false -} -``` -::alert[For simplicity in this workshop, we use the same Git repository for add-ons, platform, and workloads. However, the project is structured to allow you to easily use separate Git repositories for each functionality, depending on your needs.]{header="Important" type="warning"} +### 2. Define addons variables -### 2. Define local variables +Define enable-* addons boolean variables. These provide a simple way to control whether addons are installed or removed. Define addons variable as a list of key/value pairs of addon(enable-*) values. Define addons_metadata variable as a list of key/value pairs of mainly codecommit values. -Define some local variables, that include: -- *'addons'* local, which represents the Labels that will be sent to the Cluster Secret -- *'addons_metadata'* local, which represents the annotations that will be sent to the Cluster Secfret Some values are commented and will be used later in the workshop. -:::code{showCopyAction=true showLineNumbers=false language=json highlightLines='48,73'} +:::code{showCopyAction=true showLineNumbers=false language=json highlightLines='48,58'} cat <<'EOF' >> ~/environment/hub/main.tf locals{ @@ -196,21 +124,6 @@ locals{ ) - gitops_addons_url = var.gitops_addons_url - gitops_addons_basepath = var.gitops_addons_basepath - gitops_addons_path = var.gitops_addons_path - gitops_addons_revision = var.gitops_addons_revision - - gitops_platform_url = var.gitops_platform_url - gitops_platform_basepath = var.gitops_platform_basepath - gitops_platform_path = var.gitops_platform_path - gitops_platform_revision = var.gitops_platform_revision - - gitops_workload_url = var.gitops_workload_url - gitops_workload_basepath = var.gitops_workload_basepath - gitops_workload_path = var.gitops_workload_path - gitops_workload_revision = var.gitops_workload_revision - addons_metadata = merge( #enableaddonmetadata module.eks_blueprints_addons.gitops_metadata, { @@ -248,63 +161,6 @@ locals{ EOF ::: -### 3. Define outputs - -The purpose of these outputs is to provide data for upcoming spoke modules (in advanced sections). - -```bash -cat <<'EOF' >> ~/environment/hub/outputs.tf - -output "gitops_addons_url" { - value = local.gitops_addons_url -} - -output "gitops_addons_path" { - value = local.gitops_addons_path -} - -output "gitops_addons_revision" { - value = local.gitops_addons_revision -} - -output "gitops_addons_basepath" { - value = local.gitops_addons_basepath -} - -output "gitops_platform_url" { - value = local.gitops_platform_url -} - -output "gitops_platform_path" { - value = local.gitops_platform_path -} - -output "gitops_platform_revision" { - value = local.gitops_platform_revision -} - -output "gitops_platform_basepath" { - value = local.gitops_platform_basepath -} - -output "gitops_workload_url" { - value = local.gitops_workload_url -} - -output "gitops_workload_path" { - value = local.gitops_workload_path -} - -output "gitops_workload_revision" { - value = local.gitops_workload_revision -} - -output "gitops_workload_basepath" { - value = local.gitops_workload_basepath -} - -EOF -``` ### 4. Update Labels and Annotations We need to update the labels and annotations on the hub-cluster Cluster object. To do this, we will use the GitOps Bridge. The GitOps Bridge is configured to update labels and annotations on the specified cluster object. @@ -342,8 +198,9 @@ Goto to the **Settings > Clusters > hub-cluster** in the Argo CD dashboard. Exa ![Hub Cluster Updated Metadata](/static/images/hubcluster-update-metadata.png) -You can check that the Labels and annotations are correctly propagated to the cluster secret: +ArgoCD pulls lables and annotations for the cluster object from a kubernetes secret. We used gitops bridge to update labels and annotations for the secret. +You can check the Labels and annotations on the cluster secret: ```bash kubectl --context hub get secrets -n argocd hub-cluster -o yaml diff --git a/content/030_base/060_addons/033-argocd-repos.md b/content/030_base/060_addons/033-argocd-repos.md new file mode 100644 index 0000000..2e7cee0 --- /dev/null +++ b/content/030_base/060_addons/033-argocd-repos.md @@ -0,0 +1,62 @@ +--- +title: 'Create ArgoCD Repositories' +weight: 33 +--- + +In the previous chapter, we created "gitops-platform" and "gitops-workload" CodeCommit repositories. There are [different ways](https://argo-cd.readthedocs.io/en/stable/user-guide/private-repositories/) to provide ArgoCD access to these repositories. In this chapter, we will use SSH private keys to grant ArgoCD access to the CodeCommit repositories. + +### 1. Create ArgoCD git repositories + +```json +cat <<'EOF' >> ~/environment/hub/main.tf +locals{ + git_private_ssh_key = data.terraform_remote_state.git.outputs.git_private_ssh_key +} + +resource "kubernetes_secret" "git_secrets" { + for_each = { + git-platform = { + type = "git" + url = local.gitops_platform_url + sshPrivateKey = file(pathexpand(local.git_private_ssh_key)) + insecureIgnoreHostKey = "true" + } + git-workloads = { + type = "git" + url = local.gitops_workload_url + sshPrivateKey = file(pathexpand(local.git_private_ssh_key)) + insecureIgnoreHostKey = "true" + } + + } + metadata { + name = each.key + namespace = local.argocd_namespace + labels = { + "argocd.argoproj.io/secret-type" = "repository" + } + } + data = each.value +} +EOF +``` + +### 2. Apply Terraform + +```bash +cd ~/environment/hub +terraform apply --auto-approve +``` + +Navigate to the ArgoCD dashboard, then go to the Settings page, and select Repositories to view gitops-platform and gitops-workload repositories + +![ArgoCD Repositories](/static/images/argocd-repositories.png) + +The Git repository connection data for ArgoCD is stored in a Kubernetes Secret. You can verify that ArgoCD has created the Secret object that contains the configuration, including the SSH private keys, to access Git repositories. + +```json +kubectl get secret -n argocd --selector=argocd.argoproj.io/secret-type=repository --context hub +``` + +![ArgoCD Repository Secret](/static/images/argocd_k8s_repos.png) + diff --git a/content/030_base/060_addons/040-addons-applicationset.md b/content/030_base/060_addons/040-addons-applicationset.md index cc3c6f9..8083372 100644 --- a/content/030_base/060_addons/040-addons-applicationset.md +++ b/content/030_base/060_addons/040-addons-applicationset.md @@ -5,35 +5,16 @@ weight: 40 The focus of this chapter is to set up Argo CD to install and manage add-ons for EKS clusters. -### 1. Clone repository +### 1. Configure Addons ApplicationSet -Make a clone of your GitHub repository locally so that you can add applicationset files to it. Instead of cloning the entire repo, checkout only `assets` folder to keep things simple using sparse-checkout. (Note, this also checkouts files not tied to a directory) - - -```bash -cd ~/environment -git clone --no-checkout https://${GITHUB_USER}:${GITHUB_TOKEN}@github.com/${GITHUB_LOGIN}/eks-blueprints-for-terraform-workshop.git wgit -cd wgit -git sparse-checkout init --cone -git sparse-checkout set assets -git checkout -``` - -::::expand{header="What is in my cloned repo?"} -This repository contains resources for managing Kubernetes clusters in the **assets** directory. It includes Kubernetes YAML files for deploying workloads, ApplicationSets, and configuration values for addons, namespaces, and projects. - -![Kubernetes Addons](/static/images/platform-github-folders.png) -:::: - -### 2. Configure Addons ApplicationSet - -Previously, you created an "App of Apps" Application that referenced the "appofapps" folder to include all the files in this folder. You will add "cluster-addons" Argo CD Application, which is configured to point to the cloned copy of the GitOps Bridge ApplicationSet repository in your own Git repo. Addons repo is under `assets/platform/addons/applicationset` folder. +Previously, you created an "App of Apps" Application that referenced the "appofapps" folder. You will add "cluster-addons" Argo CD ApplicationSet in appofapps folder, which is configured to point to the cloned copy of the GitOps Bridge ApplicationSet repository in your own "gitops-platform" repo. ![cluster-addons](/static/images/cluster-addons.png) ```bash -cat > ~/environment/wgit/assets/platform/appofapps/addons-applicationset.yaml << 'EOF' + +cat > $GITOPS_DIR/platform/appofapps/addons-applicationset.yaml << 'EOF' apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: @@ -67,19 +48,25 @@ spec: EOF ``` -### 3. Commit addons ApplicationSet to Git + - When pushing to from a remote git repository, if you haven't authenticated before, it will prompt you for your credentials. + + + + + + + + + + You may need to authenticate with username="" and password="" to push on the repository--> ```bash -cd ~/environment/wgit -git add . -git commit -m "add addons applicationset" -git push +git -C ${GITOPS_DIR}/platform add . || true +git -C ${GITOPS_DIR}/platform commit -m "add addon applicationset" || true +git -C ${GITOPS_DIR}/platform push || true ``` -> You may need to authenticate with username="" and password="" to push on the repository - ### 4. Validate addons ApplicationSet ::alert[The default configuration for Argo CD is to check for updates in a git repository every 3 minutes. It might take upto 3 minutes to recognize the new file in the git repo. Click on REFRESH APPS on the Argo CD Dashboard to refresh rightaway.]{header="cluster-addons Application"} diff --git a/content/030_base/060_addons/050-loadbalancer-addon.md b/content/030_base/060_addons/050-loadbalancer-addon.md index 128cbcb..779b5b7 100644 --- a/content/030_base/060_addons/050-loadbalancer-addon.md +++ b/content/030_base/060_addons/050-loadbalancer-addon.md @@ -5,10 +5,10 @@ weight: 50 The goal of this chapter is to demonstrate how easy it can be to install an addon on a Kubernetes cluster using Argo CD. The steps will show you how a simple change to the Git repository can trigger Argo CD to deploy and manage an addon in an automated way. -In the previous chapter, we created ApplicationSets for various add-ons, but they did not generate any Applications yet because the conditions were not met. For example, looking at the `assets/platform/addons/applicationset/aws/addons-aws-load-balancer-controller-appset.yaml` file in your Git repo, the loadbalancer ApplicationSet requires clusters to have the label `enable_aws_load_balancer_controller=true`. Currently, your only cluster is hub-cluster and it does not have that label. +In the previous chapter, we created ApplicationSets for various add-ons, but they did not generate any Applications yet because the conditions were not met. For example, looking at the `addons/applicationset/aws/addons-aws-load-balancer-controller-appset.yaml` file in your "gitops-platform" repo, the loadbalancer ApplicationSet requires clusters to have the label `enable_aws_load_balancer_controller=true`. Currently, your only cluster is hub-cluster and it does not have that label. ```bash -c9 open ~/environment/wgit/assets/platform/addons/applicationset/aws/addons-aws-load-balancer-controller-appset.yaml +c9 open $GITOPS_DIR/platform/addons/applicationset/aws/addons-aws-load-balancer-controller-appset.yaml ``` :::code{showCopyAction=false showLineNumbers=false language=yaml highlightLines='7-10'} @@ -26,19 +26,17 @@ generators: ### 1. Set load balancer label in terraform variables -```bash -sed -i "s/enable_aws_load_balancer_controller = false/enable_aws_load_balancer_controller = true/g" ~/environment/terraform.tfvars -``` -The above code snippet will uncomment the label `enable_aws_load_balancer_controller=true` in the `~/environment/terraform.tfvars` file, as shown highlighted below. +We will set enable_aws_argocd to true in upcoming capter. + +```json +cat <> ~/environment/terraform.tfvars -:::code{showCopyAction=false showLineNumbers=false language=yaml highlightLines='6-6'} -... addons = { - ... enable_aws_load_balancer_controller = true + enable_aws_argocd = false } - -::: +EOF +``` ### 2. Create IAM roles for addon diff --git a/content/030_base/070_selfmanage_argocd/010-argocd-selfmanaged.md b/content/030_base/070_selfmanage_argocd/010-argocd-selfmanaged.md index b70f106..30fe3f7 100644 --- a/content/030_base/070_selfmanage_argocd/010-argocd-selfmanaged.md +++ b/content/030_base/070_selfmanage_argocd/010-argocd-selfmanaged.md @@ -21,14 +21,14 @@ addons = { ::: -The ApplicationSet addons-aws-oss-argocd-hub-appset.yaml file references configuration values for Argo CD from the `assets/platform/addons/environments/default/addons/argo-cd/values.yaml` file. You can update the `values.yaml` as per your need. The default Refresh interval for the Argo CD is 3 minutes (180 seconds). For this workshop, the Refresh interval has been updated to 5 seconds by setting the `timeout.reconciliation` value in `values.yaml` to 5. This shorter interval allows changes to happen faster during the workshop demonstrations. +The ApplicationSet addons-aws-oss-argocd-hub-appset.yaml file references configuration values for Argo CD from the `addons/environments/default/addons/argo-cd/values.yaml` file in gitops-platform . You can update the `values.yaml` as per your need. The default Refresh interval for the Argo CD is 3 minutes (180 seconds). For this workshop, the Refresh interval has been updated to 5 seconds by setting the `timeout.reconciliation` value in `values.yaml` to 5. This shorter interval allows changes to happen faster during the workshop demonstrations. ![argocd-values](/static/images/argocd-values.png) You can open the file in cloud9. Don't forget to commit if you make any changes. ```bash -c9 open ~/environment/wgit/assets/platform/addons/environments/default/addons/argo-cd/values.yaml +c9 open $GITOPS_DIR/platform/addons/environments/default/addons/argo-cd/values.yaml ``` ### 2. Apply Terraform diff --git a/content/030_base/075_namespace/010-enable-namespace.md b/content/030_base/075_namespace/010-enable-namespace.md index 0430b4d..b2ac8b0 100644 --- a/content/030_base/075_namespace/010-enable-namespace.md +++ b/content/030_base/075_namespace/010-enable-namespace.md @@ -3,19 +3,23 @@ title: 'Create Namespace' weight: 10 --- -You will use namespace helm templates to create namespace, limitrange, networkpolicy, rbac and resource quota. +In this chapter you will create webstore workload namespaces carts, catalog, checkout, orders, rabbitmq, assets, and ui. At the end of this chapter, we will setup ArgoCD so that creating namespaces for a new workload for example "payment" is as simple as creating a new "payment" folder with manifests. -![namespace-helm](/static/images/namespace-helm.png) - -::alert[In this workshop helm chart is in the GitHub repository to make it easy to understand. Use a Helm chart repository to store and serve charts - This is the preferred way to share charts. ]{header="Important" type="warning"} +![appofapps-applicationset-watch](/static/images/namespace-design.png) ### 1. Create AppofApps namespace applicationset -AppofApps Namespace application set scans workload folders under `config/workload` and creates specific application sets for each workload. When you add a new -workload it detects the change and creates workload specific namespace applicationset without requiring manual intervention. +In the "Kubernetes Addons" chapter, we added a file called "appofapps-applicationset.yaml" that watches the "appofapps" folder and processes any changes. + +![namespace-begin](/static/images/namespace-begin.png) + +Lets Add namespace applicationset into the appofapps folder. -:::code{showCopyAction=false showLineNumbers=true language=bash highlightLines='18, 21, 33'} -cat > ~/environment/wgit/assets/platform/appofapps/namespace-applicationset.yaml << 'EOF' +![namespace-add-namespace-applicationset](/static/images/namespace-namespace-applicationset.png) + +:::code{showCopyAction=true showLineNumbers=true language=json highlightLines='22,34'} + +cat > $GITOPS_DIR/platform/appofapps/namespace-applicationset.yaml << 'EOF' apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: @@ -63,30 +67,14 @@ spec: EOF ::: -Again, we can note, that it uses the annotations from the secret like {{metadata.annotations.platform_repo_url}}, which means that it will retrieve the value -from the secret, like we can do manually with: - -```bash -kubectl --context hub get secrets -n argocd hub-cluster -o json | jq ".metadata.annotations.platform_repo_url" -``` - -Additionaly, we are using a git generator, that will watch the defined directory : `{{metadata.annotations.platform_repo_basepath}}config/workload/*` which points -to your git repository, which is normally checkout locally, so you can check the content here: - -```bash -ls -la ~/environment/wgit/assets/platform/config/workload/ -``` +This ApplicationSet initiates the creation of namespaces for all the workloads. -The git generator will iterate for each item present in this directory and then generate an ApplicationSet that will add the `/namespace` to the item find, -this is done with the syntax: `path: '{{path}}/namespace'`, so the target will be `assets/platform/config/workload/xxxx/namespace/`where xxxx is every directory -find by the git generator. - -> Later we will use the same git generator to deploy also our workloads. +Git generator(line 22) iterates through folders under "config/workload" in gitops-workload repository. For each folder( line 34), ApplicationSet process files under "namespace" folder. Since there are currently no workload folders under "config/workload/webstore/workload", there are no files to process at this point. ### 2. Git commit ```bash -cd ~/environment/wgit +cd $GITOPS_DIR/platform git add . git commit -m "add appofapps namespace applicationset" git push @@ -99,16 +87,16 @@ On the Argo CD dashboard click on appofapps Application to see newly created nam ![namespace-helm](/static/images/appofapps-namespace-applicationset.png) +### 3. Create webstore namespace -### 3. Create webstore namespace applicationset +Let's create an ApplicationSet that is responsible for the namespaces associated with the webstore workload. -Now, we create an ApplicationSet stored in the directory that is watched by the `namespace` ApplicationSet we juste created. +![namespace-helm](/static/images/namespace-webstore-applicationset.png) -The Webstore Namespace ApplicationSet automatically creates an Argo CD Namespace Application for any clusters that have the label `workload_webstore: 'true'`, and use the `environment` label (line 20) from the cluster secret to customize the name of the Application, in our case the name will be `namespace-staging-webstore`. -:::code{showCopyAction=false showLineNumbers=true language=bash highlightLines='15,20'} -mkdir -p ~/environment/wgit/assets/platform/config/workload/webstore/namespace -cat > ~/environment/wgit/assets/platform/config/workload/webstore/namespace/namespace-webstore-applicationset.yaml << 'EOF' +:::code{showCopyAction=true showLineNumbers=true language=json highlightLines='15,29,35,36'} +mkdir -p $GITOPS_DIR/platform/config/workload/webstore/namespace +cat > $GITOPS_DIR/platform/config/workload/webstore/namespace/namespace-webstore-applicationset.yaml << 'EOF' apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: @@ -157,16 +145,22 @@ spec: EOF ::: -### 4. Create default namespace values +Line 17: Only clusters that have label workload_webstore: 'true' are selected +Line 29: Install the helm chart in the folder charts/namespace +![namespace-helm](/static/images/namespace-helm.png) + +::alert[In this workshop helm chart is in the GitHub repository to make it easy to understand. Use a Helm chart repository to store and serve charts - This is the preferred way to share charts. ]{header="Important" type="warning"} + +Line 35: Default values for the namespace helm chart +Line 36: (optional) Override values for the namespace helm chart. For example you could override default values for environment = hub with the file hub-values.yaml -The Webstore ApplicationSet reads the default namespace configuration values (line 35) from `assets/platform/config/workload/webstore/namespace/values/default-values.yaml` in the git repository. It then looks for environment specific overrides in the `-values.yaml` files if it exists. +### 4. Create webstore namespace values -For example, for the hub-cluster which has the environment label `"hub"`, it will check `assets/platform/config/workload/webstore/namespace/values/hub-values.yaml` for any overrides. If the override file for a specific environment label does not exist, such as `-values.yaml`, then the Webstore ApplicationSet will ignore it and just use the default values in `default-values.yaml`. -The `default-values.yaml` contains the namespaces to create, along with the **limitRanges** and **resourceQuotas** to apply for each namespace. +![namespace-helm](/static/images/namespace-webstore-defalut-values.png) -:::code{showCopyAction=false showLineNumbers=true language=yaml highlightLines='7,39,71,103,135,167,199'} -mkdir -p ~/environment/wgit/assets/platform/config/workload/webstore/namespace/values -cat > ~/environment/wgit/assets/platform/config/workload/webstore/namespace/values/default-values.yaml << 'EOF' +```json +mkdir -p $GITOPS_DIR/platform/config/workload/webstore/namespace/values +cat > $GITOPS_DIR/platform/config/workload/webstore/namespace/values/default-values.yaml << 'EOF' name: webstore labels: environment: hub @@ -396,12 +390,11 @@ namespaces: scopeName: PriorityClass values: ["high"] EOF -::: +``` -Thoses values will be used with the namespace helm Chart that you can find in the Application target which is `assets/platform/charts/namespace`. ```bash -tree ~/environment/wgit/assets/platform/charts/namespace +tree $GITOPS_DIR/platform/charts/namespace ``` Output: @@ -432,18 +425,10 @@ Output: └── values.yaml ``` -### 5. Git commit - -```bash -cd ~/environment/wgit -git add . -git commit -m "add webstore namespace applicationset and namespace values" -git push -``` -### 6. Set workload_webstore: 'true' label on the hub cluster +### 5. Enable hub cluster for webstore workload -You want to deploy the webstore workload on the hub cluster . You can do this by setting the label workload_webstore: 'true' on the hub cluster. +The webstore Namespace applicationset( step 5) only creates webstore namespaces on clusters labeled with workload_webstore: 'true'. Let's add this label to the hub cluster. ```bash sed -i "s/#enablewebstore//g" ~/environment/hub/main.tf @@ -461,7 +446,7 @@ locals{ } ::: -### 7. Apply Terraform +### 6. Apply Terraform This will set the label workload_webstore: 'true' on the hub cluster. @@ -469,6 +454,22 @@ This will set the label workload_webstore: 'true' on the hub cluster. cd ~/environment/hub terraform apply --auto-approve ``` +### 7. Git commit + +```bash +cd $GITOPS_DIR/platform +git add . +git commit -m "add webstore namespace applicationset and namespace values" +git push +``` +The namespace-applicationset.yaml file iterates through the folders under config/workload/\<\>/namespace. +With the recent commit, it now processes the files located under config/workload/webstore/namespace. + +![namespace-helm](/static/images/namespace-process-webstore-applicationset.png) + +The namespace-webstore-applicationset.yaml file installs the namespace Helm chart using the default values. +![namespace-helm](/static/images/namespace-create-webstore-namespace.png) + ### 8. Validate namespaces diff --git a/content/030_base/078_workload/010-deploy-workloads.md b/content/030_base/078_workload/010-deploy-workloads.md index ab1dee2..1efa717 100644 --- a/content/030_base/078_workload/010-deploy-workloads.md +++ b/content/030_base/078_workload/010-deploy-workloads.md @@ -2,12 +2,18 @@ title: 'Deploy Workloads' weight: 10 --- +In this chapter you will deploy webstore workload. Similiar to namespace in the previous chater , we will setup ArgoCD so that deploying a new workload is as simple as creating a new a folder with manifests. + ### 1. Create AppofApps workload applicationset -App of Apps workload application set scans workload folders under `config/workload` and creates specific application sets for each workload. When you add a new workload it detects the change and creates workload specific applicationset without requiring manual intervention. +This ApplicationSet initiates the deployment of all the workloads. + +![workload-appofapps](/static/images/workload-appofapps.png) + -:::code{showCopyAction=false showLineNumbers=true language=yaml highlightLines='13,17,21,32'} -cat > ~/environment/wgit/assets/platform/appofapps/workload-applicationset.yaml << 'EOF' +:::code{showCopyAction=true showLineNumbers=true language=yaml highlightLines='22,33'} + +cat > $GITOPS_DIR/platform/appofapps/workload-applicationset.yaml << 'EOF' apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: @@ -52,40 +58,50 @@ spec: syncOptions: - CreateNamespace=true EOF + ::: -Again, we have the git generator that will iterate in the directory `assets/platform/config/workload/*`, and will create it from the `path: '{{path}}/workload'`, so we will need to create this directory. +Line 22: Git generator iterates through folders under "config/workload" in gitops-platform repository +Line 33: {path} maps to each workload folder under config/workload. For webstore {path} maps to config/workload/webstore. Since there is no folder "config/workload/webstore/workload", there are no files to process at this point. ### 2. Git commit ```bash -cd ~/environment/wgit +cd $GITOPS_DIR/platform git add . git commit -m "add appofapps workload applicationset" git push ``` +As the appofapps folder is monitored, when a new file like workload-applicationset.yaml is added, it gets processed. + +![workload-appofapps-monitor](/static/images/workload-appofapps-monitor.png) + +The newly added workload-applicationset.yaml file iterates through the config/workload folders and processes any workload config files found under config/workload/\<>/workload. Since the folder config/workload/webstore/workload does not exist it has nothing to process. + +![workload-appofapps-monitor](/static/images/workload-appofapps-iteration.png) + + On the Argo CD dashboard click on appofapps Application to see newly created workload applicationset. ![appofapps-workload-applicationset](/static/images/appofapps-workload-applicationset.png) -### 3. Create webstore workload applicationset +### 3. Deploy webstore workload + +The webstore workload configuration files are in the **gitops-workload** repository, not in the **gitops-platform** repository. + +The webstore workload supports multiple environments like hub, staging and prod. Environment-specific configurations are applied using kustomization. -The Webstore Workload ApplicationSet automatically activate for for any clusters that have the label `workload_webstore: 'true'`, and will iterate for each items present in the target directory. -So we define a `webstore` ApplicationSet there, that will create Argo CD Application for each of our microservice. +![workload-webstore-folders](/static/images/workload-webstore-folders.png) -In this example, the Webstore ApplicationSet will deploy the `"hub"` version of the application to the hub-cluster, which is defined in the directory `assets/developer/webstore/xxx/hub/`: +Lets add webstore applicationset to deploy the webstore workload in the gitops-workload repository. -- There is only one cluster labeled with `workload_webstore: 'true'` -- That cluster also has the label `environment: 'hub'` -- `{{metadata.annotations.workload_repo_basepath}}` points to `assets/developer` -- `{{values.workload}}` points to `webstore` -- `'{{path}}/{{metadata.labels.environment}}'` (line 39) points to `assets/developer/webstore/xxx/hub/` where xxx is each webstore microservice +![workload-webstore](/static/images/workload-webstore.png) -:::code{showCopyAction=false showLineNumbers=true language=yaml highlightLines='14,21,25,39'} -mkdir -p ~/environment/wgit/assets/platform/config/workload/webstore/workload -cat > ~/environment/wgit/assets/platform/config/workload/webstore/workload/webstore-applicationset.yaml << 'EOF' +:::code{showCopyAction=true showLineNumbers=true language=yaml highlightLines='17,22,25,39,42'} +mkdir -p $GITOPS_DIR/platform/config/workload/webstore/workload +cat > $GITOPS_DIR/platform/config/workload/webstore/workload/webstore-applicationset.yaml << 'EOF' apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: @@ -137,14 +153,25 @@ spec: limit: 100 EOF + ::: +Line 17: The webstore workload is only deployed on clusters that have the label workload_webstore = true. The hub cluster has workload_webstore = true label. +Line 22: metadata.annotations.workload_repo_url i.e workload_repo_url annotation on the hub cluster has the value of the gitops-worload repository. +Line 25: It maps to webstore/* ( microservices under webstore folder). +Line 39: Path gets the value each microservice directory. The label environment on the hub cluster is "hub". Kustomization deploys "hub" environment of each microservice. +Line 42: path.basename maps to the microservice directory name, which maps to the target namespace for deployment. So each microservice deploys into its own matching namespace. This makes asset microservice deploy to asset namespace, carts to carts and so on. + + +![workload-webstore-folders](/static/images/workload-webstore-deployment.png) + + ### 4. Git commit ```bash -cd ~/environment/wgit +cd $GITOPS_DIR/platform git add . -git commit -m "add webstore workload applicationset" +git commit -m "add appofapps workload applicationset" git push ``` @@ -153,7 +180,7 @@ git push ::alert[It takes few minutes to deploy the workload and create a loadbalancer]{header="Important" type="warning"} ```bash -echo -n "Click here to open -> http://" ; kubectl get svc ui-nlb -n ui --context hub --output jsonpath='{.status.loadBalancer.ingress[0].hostname}'; echo "" +echo "Click here to open -> http://$(kubectl get svc ui-nlb -n ui --context hub --output jsonpath='{.status.loadBalancer.ingress[0].hostname}')" ``` Access webstore in the browser. diff --git a/content/030_base/index.en.md b/content/030_base/index.en.md index ecb17c4..65edd06 100644 --- a/content/030_base/index.en.md +++ b/content/030_base/index.en.md @@ -5,4 +5,4 @@ weight: 30 In this module, we are going to create a single EKS cluster using Terraform, and configure with Argo CD for addons and workloads. -![](/static/images/argocd-standalone.png) +![eks-blueprint-blue](/static/images/argocd-update-metadata.png) diff --git a/content/040_advanced/010_hub_and_spoke/020_create-spoke-cluster/010-provision-spoke.md b/content/040_advanced/010_hub_and_spoke/020_create-spoke-cluster/010-provision-spoke.md index 454baa4..38de9b4 100644 --- a/content/040_advanced/010_hub_and_spoke/020_create-spoke-cluster/010-provision-spoke.md +++ b/content/040_advanced/010_hub_and_spoke/020_create-spoke-cluster/010-provision-spoke.md @@ -24,6 +24,13 @@ data "terraform_remote_state" "hub" { path = "${path.module}/../hub/terraform.tfstate" } } +data "terraform_remote_state" "git" { + backend = "local" + + config = { + path = "${path.module}/../codecommit/terraform.tfstate" + } +} EOF ``` @@ -66,22 +73,23 @@ locals { - gitops_addons_url = data.terraform_remote_state.hub.outputs.gitops_addons_url - gitops_addons_basepath = data.terraform_remote_state.hub.outputs.gitops_addons_basepath - gitops_addons_path = data.terraform_remote_state.hub.outputs.gitops_addons_path - gitops_addons_revision = data.terraform_remote_state.hub.outputs.gitops_addons_revision + gitops_addons_url = data.terraform_remote_state.git.outputs.gitops_addons_url + gitops_addons_basepath = data.terraform_remote_state.git.outputs.gitops_addons_basepath + gitops_addons_path = data.terraform_remote_state.git.outputs.gitops_addons_path + gitops_addons_revision = data.terraform_remote_state.git.outputs.gitops_addons_revision - gitops_platform_url = data.terraform_remote_state.hub.outputs.gitops_platform_url - gitops_platform_basepath = data.terraform_remote_state.hub.outputs.gitops_platform_basepath - gitops_platform_path = data.terraform_remote_state.hub.outputs.gitops_platform_path - gitops_platform_revision = data.terraform_remote_state.hub.outputs.gitops_platform_revision + gitops_platform_url = data.terraform_remote_state.git.outputs.gitops_platform_url + gitops_platform_basepath = data.terraform_remote_state.git.outputs.gitops_platform_basepath + gitops_platform_path = data.terraform_remote_state.git.outputs.gitops_platform_path + gitops_platform_revision = data.terraform_remote_state.git.outputs.gitops_platform_revision - gitops_workload_url = data.terraform_remote_state.hub.outputs.gitops_workload_url - gitops_workload_basepath = data.terraform_remote_state.hub.outputs.gitops_workload_basepath - gitops_workload_path = data.terraform_remote_state.hub.outputs.gitops_workload_path - gitops_workload_revision = data.terraform_remote_state.hub.outputs.gitops_workload_revision + gitops_workload_url = data.terraform_remote_state.git.outputs.gitops_workload_url + gitops_workload_basepath = data.terraform_remote_state.git.outputs.gitops_workload_basepath + gitops_workload_path = data.terraform_remote_state.git.outputs.gitops_workload_path + gitops_workload_revision = data.terraform_remote_state.git.outputs.gitops_workload_revision aws_addons = { + enable_aws_argocd = try(var.addons.enable_aws_argocd, false) enable_cert_manager = try(var.addons.enable_cert_manager, false) enable_aws_efs_csi_driver = try(var.addons.enable_aws_efs_csi_driver, false) enable_aws_fsx_csi_driver = try(var.addons.enable_aws_fsx_csi_driver, false) diff --git a/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/010-configure-hub-cluster.md b/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/010-configure-hub-cluster.md index 66f0145..2b00749 100644 --- a/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/010-configure-hub-cluster.md +++ b/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/010-configure-hub-cluster.md @@ -16,7 +16,8 @@ The IAM policy aws_assume_policy attached to the hub-cluster-argocd-hub role inc By creating this role and policy, you establish a centralized identity management approach, enabling Argo CD to seamlessly deploy applications and manage resources across multiple EKS clusters within the same AWS account while maintaining proper access controls and security best practices. -:::code{showCopyAction=true showLineNumbers=true language=yaml highlightLines='29,35,50,56'} + +```json cat <<'EOF' >> ~/environment/hub/main.tf ################################################################################ @@ -80,7 +81,8 @@ resource "aws_eks_pod_identity_association" "argocd_api_server" { } EOF -::: +``` + We also configure EKS Pod Identity, with a Pod association, allowing our Argo CD application server and controller, to assume that role. @@ -118,7 +120,7 @@ kubectl rollout restart -n argocd statefulset argo-cd-argocd-application-control You can verify that EKS Pod Identity is correctly applied by looking at the injected environment variables: ```bash -kubectl exec -it deployment/argo-cd-argocd-server -n argocd -- env | grep AWS +kubectl --context hub exec -it deployment/argo-cd-argocd-server -n argocd -- env | grep AWS ``` should be like: diff --git a/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/020-configure-spoke-staging.md b/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/020-configure-spoke-staging.md index c722fac..69a8233 100644 --- a/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/020-configure-spoke-staging.md +++ b/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/020-configure-spoke-staging.md @@ -156,4 +156,3 @@ The Argo CD Dashboard should have the spoke-staging cluster ![Stagging Cluster](/static/images/spoke-staging-cluster.png) -It's perfectly normal to see the "Unknown" status displayed, as Argo CD has not yet attempted to deploy any resources to the spoke cluster. \ No newline at end of file diff --git a/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/030-test-hub-spoke-connectivity.md b/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/030-test-hub-spoke-connectivity.md index 0e80a23..f745573 100644 --- a/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/030-test-hub-spoke-connectivity.md +++ b/content/040_advanced/010_hub_and_spoke/030_hub_spoke_connectivity/030-test-hub-spoke-connectivity.md @@ -3,34 +3,34 @@ title: 'Test Hub Spoke Connectivity' weight: 30 --- -In this chapter, you will test hub-spoke connectivity by checking the installation of the AWS Load Balancer Controller on the spoke Kubernetes cluster. We have configured the `enable_aws_load_balancer_controller = true` on the spoke cluster's labels. As soon as the Hub's Argo CD has connectivity, it can reconcile the labels and install associated addons. +This chapter validates hub-spoke connectivity by checking for the AWS Load Balancer Controller installed on the spoke-staging cluster. In this workhshop, ArgoCD was configured to install addon by setting a label to true. The label enable_aws_load_balancer_controller=true installs the load balancer addon. This label was set during creation of the spoke cluster. Once hub-spoke connectivity between hub and spoke was established, ArgoCD installed the load balancer on the spoke by detecting this label had been set. + +:::code{showCopyAction=true showLineNumbers=false language=yaml} +cat ~/environment/spoke/terraform.tfvars +::: :::code{showCopyAction=false showLineNumbers=false language=yaml highlightLines='4'} -$ cat ~/environment/spoke/terraform.tfvars ... addons = { enable_aws_load_balancer_controller = true } ::: -In this contexte, the eks-blueprints-addons module from spoke-staging, will create necessary AWS resources for the load balancer controller to work, then it will update the spoke-staging secret in the hub-cluster with the label to activate the addon, and also provide additional metadatas like the IAM role to be used by the load balancer controller. - -You can check the label with: +You can check the label on the spoke-staging cluster: ```bash kubectl --context hub get secrets -n argocd spoke-staging -o json | jq ".metadata.labels" | grep load_balancer ``` -and the annotations with: +The Terraform blueprint modules and gitops bridge set up an IAM role that gets assigned to the service account for the load balancer. This configures the necessary permissions for the load balancer to operate. + +You can check the IAM role on the spoke-stagging annotations: ```bash kubectl --context hub get secrets -n argocd spoke-staging -o json | jq ".metadata.annotations" | grep load_balancer ``` -From then, Argo CD in the hub-cluster will trigger some deployments using the annoations in the secret to configure the addon, targeting the spoke-staging cluster, and installing the load balancer controller addon. - - The Argo CD dashboard should have the stagging load balancer addon. ![Stagging LB](/static/images/spoke-lb.png) diff --git a/content/040_advanced/010_hub_and_spoke/040_project/010-create-project.md b/content/040_advanced/010_hub_and_spoke/040_project/010-create-project.md index 7d9dd01..418f482 100644 --- a/content/040_advanced/010_hub_and_spoke/040_project/010-create-project.md +++ b/content/040_advanced/010_hub_and_spoke/040_project/010-create-project.md @@ -3,18 +3,21 @@ title: 'Argo CD Project' weight: 10 --- -### 1. Create App of Apps Project ApplicationSet +Projects define guardrails that set constraints for associated applications. When an application is associated with a project, it must operate within the guardrails established by that project. + +In this chapter we will create a project for the webstore workload. In upcoming chapters, we will associate the webstore workload deployment with this project. -The App of Apps Project ApplicationSet functionality offers a seamless approach to managing and deploying workloads within your Git repository. By automatically scanning the designated `assets/platform/config/workload` folder, it dynamically identifies any new or modified workloads. Consequently, it creates or updates the corresponding Project ApplicationSets without requiring manual intervention. You can link many different project to many different Git repository as source, and control which cluster and namespace destination they are allowed to deploy into. +### 1. Create App of Apps Project ApplicationSet -This automated process not only saves valuable time but also mitigates the risk of human errors, ensuring a consistent and reliable deployment experience across your environment. With this feature, you can dedicate your efforts to developing and maintaining workloads, while the App of Apps Project ApplicationSet handles the deployment aspect efficiently. +Create an applicationset that creates Argo CD project for each workload. +![Project AppofApps](/static/images/project-applicationset.png) -Overall, the App of Apps Project ApplicationSet feature streamlines workload management, enhances productivity, and promotes a more automated and consistent application lifecycle management process within your Git repository. -```bash -cat > ~/environment/wgit/assets/platform/appofapps/argoproject-applicationset.yaml << 'EOF' + +:::code{showCopyAction=true showLineNumbers=true language=json highlightLines='16,20,25,44,46,47'} +cat > $GITOPS_DIR/platform/appofapps/argoproject-applicationset.yaml << 'EOF' apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: @@ -73,15 +76,29 @@ spec: syncOptions: - CreateNamespace=true EOF -``` + +::: + +Line 16: Projects are installed on the hub cluster and not on the spoke clusters. +Line 20: Argo CD projects are created with a helm chart. Installs the project helm chart from argoproject. +Line 25: Iterates through all the workload folders under config/workload folder +Line 44: project values for each workload. +Line 46,47: Replace sourceRepos value with the gitops-workload url( Line 7 below in the project-values.yaml) + ### 2. Create Project Values +Lets create webstore project values. + +![project-values](/static/images/project-values.png) + + + The following helm values file contains source repositories, destinations, and allowed resources for the webstore workload. Few values are commented for the upcoming chapters. -```bash -mkdir -p ~/environment/wgit/assets/platform/config/workload/webstore/project -cat > ~/environment/wgit/assets/platform/config/workload/webstore/project/project-values.yaml << 'EOF' +:::code{showCopyAction=true showLineNumbers=true language=json highlightLines='7,12,39,47'} +mkdir -p $GITOPS_DIR/platform/config/workload/webstore/project +cat > $GITOPS_DIR/platform/config/workload/webstore/project/project-values.yaml << 'EOF' # using upstream argo chart https://github.com/argoproj/argo-helm/tree/main/charts/argocd-apps projects: - name: webstore @@ -152,12 +169,17 @@ projects: - group: 'autoscaling' kind: HorizontalPodAutoscaler EOF -``` +::: + +Line 7(Restrict what may be deployed): List of premitted git repositories that are allowed to deploy. The value gets replaced with gitops-workload url( Line 46,47 of argoproject-applicationset.yaml). +Line 12(Restrict where apps may be deployed to): Permitted destionation of clusters and namespaces. For example carts namespace is restricted to spoke-staging cluster. +Line 39: Restricted resource creation list. +Line 47: Allowed resource creation list. ### 3. Git commit ```bash -cd ~/environment/wgit +cd $GITOPS_DIR/platform git add . git commit -m "add appofapps project applicationset and webstore project values" git push diff --git a/content/040_advanced/010_hub_and_spoke/050-enable-namespace.md b/content/040_advanced/010_hub_and_spoke/050-enable-namespace.md index 2aa31b6..0aafa5a 100644 --- a/content/040_advanced/010_hub_and_spoke/050-enable-namespace.md +++ b/content/040_advanced/010_hub_and_spoke/050-enable-namespace.md @@ -8,13 +8,15 @@ In this chapter you will associate both namespace and workload application to we ### 1. Set Project ```bash -sed -i "s/project: default/project: webstore/g" ~/environment/wgit/assets/platform/config/workload/webstore/workload/webstore-applicationset.yaml +sed -i "s/project: default/project: webstore/g" $GITOPS_DIR/platform/config/workload/webstore/workload/webstore-applicationset.yaml ``` Changes by the code snippet is highlighted below. -:::code{showCopyAction=false showLineNumbers=false language=yaml highlightLines='9'} -$ git diff ---- a/assets/platform/config/workload/webstore/workload/webstore-applicationset.yaml -+++ b/assets/platform/config/workload/webstore/workload/webstore-applicationset.yaml +:::code{showCopyAction=true showLineNumbers=false language=yaml highlightLines='0'} +git diff +::: +:::code{showCopyAction=false showLineNumbers=false language=yaml highlightLines='8'} +--- a/config/workload/webstore/workload/webstore-applicationset.yaml ++++ b/config/workload/webstore/workload/webstore-applicationset.yaml @@ -31,7 +31,7 @@ spec: component: '{{path.basename}}' workloads: 'true' @@ -28,7 +30,7 @@ $ git diff ### 2. Git commit ```bash -cd ~/environment/wgit +cd $GITOPS_DIR/platform git add . git commit -m "set namespace and webstore applicationset project to webstore" git push @@ -53,7 +55,7 @@ terraform apply --auto-approve ::alert[It takes few minutes to deploy the workload and create a loadbalancer]{header="Important" type="warning"} ```bash -echo -n "Click here to open -> http://" ; kubectl get svc ui-nlb -n ui --context spoke-staging --output jsonpath='{.status.loadBalancer.ingress[0].hostname}'; echo "" +echo "Click here to open -> http://$(kubectl get svc ui-nlb -n ui --context spoke-staging --output jsonpath='{.status.loadBalancer.ingress[0].hostname}')" ``` Access webstore in the browser. diff --git a/content/090_cleanup/index.en.md b/content/090_cleanup/index.en.md index 7410363..7635a3c 100644 --- a/content/090_cleanup/index.en.md +++ b/content/090_cleanup/index.en.md @@ -17,7 +17,7 @@ If you have deployed additional resources, that may have created Wloud resources You can just execute the cleanup script. In this script, there can be somme errors, but that is normal and the script will repeat some of the actions until normally cleanup sucess. ```bash -~/environment/wgit/assets/scripts/destroy.sh +$SCRIPT_DIR/assets/scripts/destroy.sh ``` diff --git a/static/images/argocd-repositories.png b/static/images/argocd-repositories.png new file mode 100644 index 0000000000000000000000000000000000000000..4bc0cdaf953f2e2deef6b00894be6ade1eebdd35 GIT binary patch literal 40439 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 F_#aEt*vbF^ literal 0 HcmV?d00001 diff --git a/static/images/argocd_k8s_repos.png b/static/images/argocd_k8s_repos.png new file mode 100644 index 0000000000000000000000000000000000000000..5ae08984583d937d86781fbebe4fcaf05fc4e79c GIT binary patch literal 9323 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 b;|J1jtJ_i{!&kk3FEvYv%ZpWt7zY1e$i)dD literal 0 HcmV?d00001 diff --git a/static/images/clone_starterfiles.png b/static/images/clone_starterfiles.png new file mode 100644 index 0000000000000000000000000000000000000000..d631d50db90c99eee85be4640f93dff559bf3af9 GIT binary patch literal 16841 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 literal 0 HcmV?d00001 diff --git a/static/images/cluster-addons.png b/static/images/cluster-addons.png index fe928de569cdccc358c6ef91cd202bfac9587151..a1340dac5ddcc40eda3dde3f396c52628f112dc8 100644 GIT binary patch literal 33015 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�m_}`wpX2{ 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