diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 0000000..6fa37dc --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "crg-sdw-imgs" + } +} diff --git a/.gitignore b/.gitignore index ea299ce..2389ff8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,12 @@ build_assets/ -TODO.md \ No newline at end of file +TODO.md +workdir/ +_local/ + +terraform.tfvars +backend.tf +.terraform/ +.talismanrc +.DS_Store + +.firebase/ \ No newline at end of file diff --git a/README.md b/README.md index 69670b8..0e3776c 100644 --- a/README.md +++ b/README.md @@ -1,371 +1,49 @@ -# GKE Deployments with Cloud Build +# Software Delivery Workshop -The included scripts are intended to demonstrate how to use Google Cloud Build as a continuous integration system deploying code to GKE. This is not an official Google product. -The example here follows a pattern where: -- developers use cloud servers during local development -- all lower lifecycle testing occurs on branches other than master -- merges to master indicate a readiness for a canary (beta) deployment in production -- a tagged release indicates the canary deploy is fully signed off and rolled out to all remaining servers +This repository contains resources and materials targeted toward Software Delivery on Google Cloud. In addition to separate stand alone guides, an opinionated yet modular platform is provided to demonstrate software delivery practices. It contains scripts to standup a base platform infrastructure as well as other resources designed to facilitate hands on workshop and standard demo use cases. The platform provisioning resources are structured to be modular in nature supporting various runtime and tooling configurations. Ideally users can utilize their own choice of tooling for: Provisioning, Source Code Management, Templating, Build Engine, Image Storage and Deploy tooling. -There are 5 scripts included as part of the demo: -- cloudbuild-local.yaml - used by developers to compile and push local code to cloud servers -- cloudbuild-dev.yaml - used to deploy any branch to branch namespaces -- cloudbuild-canary.yaml - used to deploy the master branch to canary servers -- cloudbuild-prod.yaml - used to push repo tags to remaining production servers -- cloudbuild.yaml - an all in one script that can be used for branches, master and tags with one configuration +## Usage +This set of resources contains materials to provision the platform, deliver short demonstrations and facilitate hands on workshops. -This lab shows you how to setup a continuous delivery pipeline for GKE using Google Cloud Build. We’ll run through the following steps +### Workshop +The Software Delivery Workshop contains materials for a self led exploration or accompanying instructor led sessions. To get started click the button below to open the resources in Google Cloud Shell. -- Create a GKE Cluster -- Review the application structure -- Manually deploy the application -- Create a repository for our source -- Setup automated triggers in Cloud Build -- Automatically deploy Branches to custom namespaces -- Automatically deploy Master as a canary -- Automatically deploy Tags to production +[![Software Delivery Workshop](http://www.gstatic.com/cloudssh/images/open-btn.svg)](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/GoogleCloudPlatform/software-delivery-workshop.git&cloudshell_workspace=.&cloudshell_tutorial=delivery-platform/docs/workshop/1.2-provision.md) - - - -## Set Variables - -``` - - export PROJECT=[[YOUR PROJECT NAME]] - # On Cloudshell - # export PROJECT=$(gcloud info --format='value(config.project)') - export CLUSTER=gke-deploy-cluster - export ZONE=us-central1-a - - gcloud config set compute/zone $ZONE - -``` -## Enable Services -``` -gcloud services enable container.googleapis.com --async -gcloud services enable containerregistry.googleapis.com --async -gcloud services enable cloudbuild.googleapis.com --async -gcloud services enable sourcerepo.googleapis.com --async -``` -## Create Cluster - -``` - - gcloud container clusters create ${CLUSTER} \ - --project=${PROJECT} \ - --zone=${ZONE} \ - --quiet - -``` - - -## Get Credentials - -``` - gcloud container clusters get-credentials ${CLUSTER} \ - --project=${PROJECT} \ - --zone=${ZONE} -``` - -## Give Cloud Build Rights - -For `kubectl` commands against GKE youll need to give Cloud Build Service Account container.developer role access on your clusters [details](https://github.com/GoogleCloudPlatform/cloud-builders/tree/master/kubectl). - -``` -PROJECT_NUMBER="$(gcloud projects describe \ - $(gcloud config get-value core/project -q) --format='get(projectNumber)')" - -gcloud projects add-iam-policy-binding ${PROJECT} \ - --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \ - --role=roles/container.developer - -``` - -## Deploy the application manually - -``` -kubectl create ns production -kubectl apply -f kubernetes/deployments/prod -n production -kubectl apply -f kubernetes/deployments/canary -n production -kubectl apply -f kubernetes/services -n production - -kubectl scale deployment gceme-frontend-production -n production --replicas 4 - -kubectl get pods -n production -l app=gceme -l role=frontend -kubectl get pods -n production -l app=gceme -l role=backend - -kubectl get service gceme-frontend -n production - -export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=production services gceme-frontend) - -curl http://$FRONTEND_SERVICE_IP/version - -``` - - -## Create a repo for the code - -``` -gcloud alpha source repos create default -git init -git config credential.helper gcloud.sh -git remote add gcp https://source.developers.google.com/p/[PROJECT]/r/default -git add . -git commit -m "Initial Commit" -git push gcp master - -``` - -## Setup triggers -Ensure you have credentials available -``` -gcloud auth application-default login - -``` -**Branches** -``` - -cat < branch-build-trigger.json -{ - "triggerTemplate": { - "projectId": "${PROJECT}", - "repoName": "default", - "branchName": "[^(?!.*master)].*" - }, - "description": "branch", - "substitutions": { - "_CLOUDSDK_COMPUTE_ZONE": "${ZONE}", - "_CLOUDSDK_CONTAINER_CLUSTER": "${CLUSTER}" - }, - "filename": "builder/cloudbuild-dev.yaml" -} -EOF - - -curl -X POST \ - https://cloudbuild.googleapis.com/v1/projects/${PROJECT}/triggers \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ - --data-binary @branch-build-trigger.json -``` - -**Master** -``` - -cat < master-build-trigger.json -{ - "triggerTemplate": { - "projectId": "${PROJECT}", - "repoName": "default", - "branchName": "master" - }, - "description": "master", - "substitutions": { - "_CLOUDSDK_COMPUTE_ZONE": "${ZONE}", - "_CLOUDSDK_CONTAINER_CLUSTER": "${CLUSTER}" - }, - "filename": "builder/cloudbuild-canary.yaml" -} -EOF - - -curl -X POST \ - https://cloudbuild.googleapis.com/v1/projects/${PROJECT}/triggers \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ - --data-binary @master-build-trigger.json -``` - -**Tag** -``` - -cat < tag-build-trigger.json -{ - "triggerTemplate": { - "projectId": "${PROJECT}", - "repoName": "default", - "tagName": ".*" - }, - "description": "tag", - "substitutions": { - "_CLOUDSDK_COMPUTE_ZONE": "${ZONE}", - "_CLOUDSDK_CONTAINER_CLUSTER": "${CLUSTER}" - }, - "filename": "builder/cloudbuild-prod.yaml" -} -EOF - - -curl -X POST \ - https://cloudbuild.googleapis.com/v1/projects/${PROJECT}/triggers \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ - --data-binary @tag-build-trigger.json -``` - -Review triggers are setup on the [Build Triggers Page](https://console.cloud.google.com/gcr/triggers) - - - - -### Build & Deploy of local content (optional) - -The following submits a build to Cloud Build and deploys the results to a user's namespace. +If at any time during the workshop you somehow exit out of the lab instructions, you can relaunch the lab using the related command below: ``` -gcloud container builds submit \ - --config builder/cloudbuild-local.yaml \ - --substitutions=_VERSION=someversion,_USER=$(whoami),_CLOUDSDK_COMPUTE_ZONE=${ZONE},_CLOUDSDK_CONTAINER_CLUSTER=${CLUSTER} . - - -``` - - -## Deploy Branches to Namespaces - -Development branches are a set of environments your developers use to test their code changes before submitting them for integration into the live site. These environments are scaled-down versions of your application, but need to be deployed using the same mechanisms as the live environment. - -### Create a development branch - -To create a development environment from a feature branch, you can push the branch to the Git server and let Cloud Build deploy your environment. - -Create a development branch and push it to the Git server. - -``` -git checkout -b new-feature -``` - - -### Modify the site - -In order to demonstrate changing the application, you will be change the gceme cards from blue to orange. - -**Step 1** -Open html.go and replace the two instances of blue with orange. - -**Step 2** -Open main.go and change the version number from 1.0.0 to 2.0.0. The version is defined in this line: - -const version string = "2.0.0" - -### Kick off deployment - -**Step 1** - -Commit and push your changes. This will kick off a build of your development environment. - -``` -git add html.go main.go - -git commit -m "Version 2.0.0" - -git push gcp new-feature -``` - -**Step 2** - -After the change is pushed to the Git repository, navigate to the [Build History Page](https://console.cloud.google.com/cloud-build/builds) user interface where you can see that your build started for the new-feature branch - -Click into the build to review the details of the job - -**Step 3** - -Once that completes, verify that your application is accessible. You should see it respond with 2.0.0, which is the version that is now running. - -Retrieve the external IP for the production services. - -It can take several minutes before you see the load balancer external IP address. - -``` -kubectl get service gceme-frontend -n new-feature -``` - -Once an External-IP is provided store it for later use - -``` -export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=new-feature services gceme-frontend) - -curl http://$FRONTEND_SERVICE_IP/version +teachme "${BASE_DIR}/docs/workshop/1.2-provision.md" +teachme "${BASE_DIR}/docs/workshop/1.3-kustomize.md" +teachme "${BASE_DIR}/docs/workshop/2.1-app-onboarding.md" +teachme "${BASE_DIR}/docs/workshop/2.2-develop.md" +teachme "${BASE_DIR}/docs/workshop/3.2-release-progression.md" ``` +If at anytime you lose your terminal session, ensure you are in the `software-delivery-workshop/delivery-platform` directory then run the following commands to reset your environment variables. ->Congratulations! You've setup a pipeline and deployed code to GKE with Cloud Build. - - -The rest of this example follows the same pattern but demonstrates the triggers for Master and Tags. - -## Deploy Master to canary - -Now that you have verified that your app is running your latest code in the development environment, deploy that code to the canary environment. - -**Step 1** -Create a canary branch and push it to the Git server. +```shell +gcloud config set project +source ./env.sh ``` -git checkout master - -git merge new-feature - -git push gcp master -``` - -Again after you’ve pushed to the Git repository, navigate to the [Build History Page](https://console.cloud.google.com/gcr/builds) user interface where you can see that your build started for the master branch -Click into the build to review the details of the job +### Demo +For a mostly automated experience follow the instructions in the `docs\demo` folder. You will run an automated script to fully provision the platform before your demonstration. A separate guide describes the steps to perform during the demonstration and concludes with instructions how to reset the demo or tear down the infrastructure. -**Step 2** +### Provision -Once complete, you can check the service URL to ensure that some of the traffic is being served by your new version. You should see about 1 in 5 requests returning version 2.0.0. +If you just want to install the base platform and run your own exercises and workloads, run the following commands from within the `delivery-platform` directory. +```shell +gcloud config set project +source ./env.sh +${BASE_DIR}/resources/provision/provision-all.sh ``` -export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=production services gceme-frontend) - -while true; do curl http://$FRONTEND_SERVICE_IP/version; sleep 1; done -``` - -You can stop this command by pressing `Ctrl-C`. - ->Congratulations! -> ->You have deployed a canary release. Next you will deploy the new version to production by creating a tag. - - -## Deploy Tags to production - -Now that your canary release was successful and you haven't heard any customer complaints, you can deploy to the rest of your production fleet. - -**Step 1** -Merge the canary branch and push it to the Git server. - -``` -git tag v2.0.0 - -git push gcp v2.0.0 -``` - -Review the job on the the [Build History Page](https://console.cloud.google.com/gcr/builds) user interface where you can see that your build started for the v2.0.0 tag - -Click into the build to review the details of the job - -**Step 2** -Once complete, you can check the service URL to ensure that all of the traffic is being served by your new version, 2.0.0. You can also navigate to the site using your browser to see your orange cards. - -``` -export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=production services gceme-frontend) - -while true; do curl http://$FRONTEND_SERVICE_IP/version; sleep 1; done -``` - -You can stop this command by pressing `Ctrl-C`. - ->Congratulations! -> ->You have successfully deployed your application to production! diff --git a/builder/cloudbuild-canary.yaml b/builder/cloudbuild-canary.yaml deleted file mode 100644 index 03b54b4..0000000 --- a/builder/cloudbuild-canary.yaml +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -steps: - -### Build - - - id: 'build' - name: 'gcr.io/cloud-builders/docker' - entrypoint: 'bash' - args: - - '-c' - - | - docker build -t gcr.io/$PROJECT_ID/gceme:${SHORT_SHA} . - - - -### Test - - -### Publish - - id: 'publish' - name: 'gcr.io/cloud-builders/docker' - entrypoint: 'bash' - args: - - '-c' - - | - docker push gcr.io/$PROJECT_ID/gceme:${SHORT_SHA} - - - -### Deploy - - id: 'deploy' - name: 'gcr.io/cloud-builders/gcloud' - env: - - 'CLOUDSDK_COMPUTE_ZONE=${_CLOUDSDK_COMPUTE_ZONE}' - - 'CLOUDSDK_CONTAINER_CLUSTER=${_CLOUDSDK_CONTAINER_CLUSTER}' - - 'KUBECONFIG=/kube/config' - entrypoint: 'bash' - args: - - '-c' - - | - CLUSTER=$$(gcloud config get-value container/cluster) - PROJECT=$$(gcloud config get-value core/project) - ZONE=$$(gcloud config get-value compute/zone) - - gcloud container clusters get-credentials "$${CLUSTER}" \ - --project "$${PROJECT}" \ - --zone "$${ZONE}" - - - sed -i 's|gcr.io/cloud-solutions-images/gceme:.*|gcr.io/$PROJECT_ID/gceme:${SHORT_SHA}|' ./kubernetes/deployments/canary/*.yaml - - kubectl apply --namespace production --recursive -f kubernetes/deployments/canary - kubectl apply --namespace production --recursive -f kubernetes/services - diff --git a/builder/cloudbuild-local.yaml b/builder/cloudbuild-local.yaml deleted file mode 100644 index 9c29607..0000000 --- a/builder/cloudbuild-local.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Usage: -# gcloud container builds submit \ -# --config cloudbuild-local.yaml \ -# --substitutions=_VERSION=someversion,_USER=$(whoami),_CLOUDSDK_COMPUTE_ZONE=${ZONE},_CLOUDSDK_CONTAINER_CLUSTER=${CLUSTER} . - - -steps: - -### Build - - - id: 'build' - name: 'gcr.io/cloud-builders/docker' - entrypoint: 'bash' - args: - - '-c' - - | - echo $PROJECT_ID - docker build -t gcr.io/$PROJECT_ID/gceme:${_USER}-${_VERSION} . - - - -### Test - - -### Publish - - id: 'publish' - name: 'gcr.io/cloud-builders/docker' - entrypoint: 'bash' - args: - - '-c' - - | - docker push gcr.io/$PROJECT_ID/gceme:${_USER}-${_VERSION} - - -### Deploy - - id: 'deploy' - name: 'gcr.io/cloud-builders/gcloud' - env: - - 'CLOUDSDK_COMPUTE_ZONE=${_CLOUDSDK_COMPUTE_ZONE}' - - 'CLOUDSDK_CONTAINER_CLUSTER=${_CLOUDSDK_CONTAINER_CLUSTER}' - - 'KUBECONFIG=/kube/config' - entrypoint: 'bash' - args: - - '-c' - - | - CLUSTER=$$(gcloud config get-value container/cluster) - PROJECT=$$(gcloud config get-value core/project) - ZONE=$$(gcloud config get-value compute/zone) - - gcloud container clusters get-credentials "$${CLUSTER}" \ - --project "$${PROJECT}" \ - --zone "$${ZONE}" - - sed -i 's|gcr.io/cloud-solutions-images/gceme:.*|gcr.io/$PROJECT_ID/gceme:${_USER}-${_VERSION}|' ./kubernetes/deployments/dev/*.yaml - - kubectl get ns ${_USER} || kubectl create ns ${_USER} - kubectl apply --namespace ${_USER} --recursive -f kubernetes/deployments/dev - kubectl apply --namespace ${_USER} --recursive -f kubernetes/services - - echo service available at http://`kubectl --namespace=${_USER} get service/gceme-frontend -o jsonpath="{.status.loadBalancer.ingress[0].ip}"` - - \ No newline at end of file diff --git a/builder/cloudbuild.yaml b/builder/cloudbuild.yaml deleted file mode 100644 index 9548cae..0000000 --- a/builder/cloudbuild.yaml +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -steps: - -### Build - - - id: 'build' - name: 'gcr.io/cloud-builders/docker' - entrypoint: 'bash' - args: - - '-c' - - | - [[ "$BRANCH_NAME" ]] && VERSION=${BRANCH_NAME}-${SHORT_SHA} - [[ "$TAG_NAME" ]] && VERSION=$TAG_NAME - docker build -t gcr.io/$PROJECT_ID/helloworld:$${VERSION} . - - - -### Test - - -### Publish - - id: 'publish' - name: 'gcr.io/cloud-builders/docker' - entrypoint: 'bash' - args: - - '-c' - - | - [[ "$BRANCH_NAME" ]] && VERSION=${BRANCH_NAME}-${SHORT_SHA} - [[ "$TAG_NAME" ]] && VERSION=$TAG_NAME - docker push gcr.io/$PROJECT_ID/helloworld:$${VERSION} - - - -### Deploy - - id: 'deploy' - name: 'gcr.io/cloud-builders/gcloud' - env: - - 'CLOUDSDK_COMPUTE_ZONE=${_CLOUDSDK_COMPUTE_ZONE}' - - 'CLOUDSDK_CONTAINER_CLUSTER=${_CLOUDSDK_CONTAINER_CLUSTER}' - - 'KUBECONFIG=/kube/config' - entrypoint: 'bash' - args: - - '-c' - - | - CLUSTER=$$(gcloud config get-value container/cluster) - PROJECT=$$(gcloud config get-value core/project) - ZONE=$$(gcloud config get-value compute/zone) - - [[ "$BRANCH_NAME" ]] && VERSION=${BRANCH_NAME}-${SHORT_SHA} - [[ "$TAG_NAME" ]] && VERSION=$TAG_NAME - - gcloud container clusters get-credentials "$${CLUSTER}" \ - --project "$${PROJECT}" \ - --zone "$${ZONE}" - - - if [[ "$TAG_NAME" ]] ; then - # Production Deploy - TARGET_ENV="prod" - NS="default" - - elif [[ ${BRANCH_NAME} == "master" ]] ; then - # Canary Deploy - TARGET_ENV="canary" - NS="default" - else - # Dev Deploy - TARGET_ENV="dev" - NS=$${BRANCH_NAME} - VERSION=$${BRANCH_NAME}-$${VERSION} - fi - - sed -i 's|gcr.io/$PROJECT_ID/helloworld:.*|gcr.io/$PROJECT_ID/helloworld:$${VERSION}|' ./kubernetes/deployments/$${TARGET_ENV}/*.yaml - - kubectl get ns $${NS} || kubectl create ns $${NS} - kubectl apply --namespace $${NS} --recursive -f kubernetes/deployments/$${TARGET_ENV} - kubectl apply --namespace $${NS} --recursive -f kubernetes/services - diff --git a/delivery-platform/app.sh b/delivery-platform/app.sh new file mode 100755 index 0000000..4f7fc7f --- /dev/null +++ b/delivery-platform/app.sh @@ -0,0 +1,180 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +### +# +# Used for app oboarding and termination. +# App creation clones the templates repo then copies the +# desierd template folder to a temporary workspace. The script +# then substitutes place holder values for actual values creates a +# new remote remote and pushes the initial version. +# Additonally if ACM is in use this script adds appropriate namespace +# configurations to ensure the app is managed by ACM. +# +# USAGE: +# app.sh +# +### + + +create () { + APP_NAME=${1:-"my-app"} + APP_LANG=${2:-"golang"} + + + # Ensure the git vendor script is set + if [[ -z "$GIT_CMD" ]]; then + echo "GIT_CMD not set - exiting" 1>&2 + exit 1 + fi + # Ensure the git token is set + if [[ -z "$GIT_TOKEN" ]]; then + echo "GIT_TOKEN not set - exiting" 1>&2 + exit 1 + fi + + + printf 'Creating application: %s \n' $APP_NAME + + # Create an instance of the template. + rm -rf $WORK_DIR/app-templates + cd $WORK_DIR/ + git clone -b main $GIT_BASE_URL/$APP_TEMPLATES_REPO app-templates + rm -rf app-templates/.git + cd app-templates/${APP_LANG} + + ## Insert name of new app + for template in $(find . -name '*.tmpl'); do envsubst < ${template} > ${template%.*}; done + + + ## Create and push to new repo + git init + git checkout -b main + git symbolic-ref HEAD refs/heads/main + $BASE_DIR/scripts/git/${GIT_CMD} create ${APP_NAME} + git remote add origin $GIT_BASE_URL/${APP_NAME} + git add . && git commit -m "initial commit" + git push origin main + # Auth fails intermittetly on the very first client call for some reason + # Adding a retry to ensure the source is pushed. + git push origin main + + + # Configure Pipeline + create_cloudbuild_trigger ${APP_NAME} + + # Initial deploy + cd $WORK_DIR/app-templates/${APP_LANG} + git pull origin main + echo "v1" > version.txt + git add . && git commit -m "v1" + git push origin main + sleep 10 + git pull origin main + git tag v1 + git push origin v1 + + # Cleanup + cd $BASE_DIR + rm -rf $WORK_DIR/app-templates +} + + + +delete () { + echo 'Destroy Application' + APP_NAME=${1:-"my-app"} + + $BASE_DIR/scripts/git/${GIT_CMD} delete $APP_NAME + + # Remove any orphaned hydrated directories from other processes + rm -rf $WORK_DIR/$APP_NAME-hydrated + + + #Also delete CD pipelines. Pipelines are in us-central1 + gcloud alpha deploy delivery-pipelines delete ${APP_NAME} --region="us-central1" --force -q || true + + + # Delete secret + SECRET_NAME=${APP_NAME}-webhook-trigger-cd-secret + gcloud secrets delete ${SECRET_NAME} -q + + + # Delete trigger + TRIGGER_NAME=${APP_NAME}-clouddeploy-webhook-trigger + gcloud alpha builds triggers delete ${TRIGGER_NAME} -q + +} + + +create_cloudbuild_trigger () { + APP_NAME=${1:-"my-app"} + ## Project variables + if [[ ${PROJECT_ID} == "" ]]; then + echo "PROJECT_ID env variable is not set" + exit -1 + fi + if [[ ${PROJECT_NUMBER} == "" ]]; then + echo "PROJECT_NUMBER env variable is not set" + exit -1 + fi + + ## API Key + if [[ ${APP_LANG} == "" ]]; then + echo "APP_LANG env variable is not set" + exit -1 + fi + + ## API Key + if [[ ${API_KEY_VALUE} == "" ]]; then + echo "API_KEY_VALUE env variable is not set" + exit -1 + fi + + + ## Create Secret + SECRET_NAME=${APP_NAME}-webhook-trigger-cd-secret + SECRET_VALUE=$(sed "s/[^a-zA-Z0-9]//g" <<< $(openssl rand -base64 15)) + SECRET_PATH=projects/${PROJECT_NUMBER}/secrets/${SECRET_NAME}/versions/1 + printf ${SECRET_VALUE} | gcloud secrets create ${SECRET_NAME} --data-file=- + gcloud secrets add-iam-policy-binding ${SECRET_NAME} \ + --member=serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-cloudbuild.iam.gserviceaccount.com \ + --role='roles/secretmanager.secretAccessor' + + ## Create CloudBuild Webhook Endpoint + REPO_LOCATION=https://github.com/${GIT_USERNAME}/${APP_NAME} + + TRIGGER_NAME=${APP_NAME}-clouddeploy-webhook-trigger + BUILD_YAML_PATH=$WORK_DIR/app-templates/${APP_LANG}/build/cloudbuild-build-only.yaml + + ## Setup Trigger & Webhook + gcloud alpha builds triggers create webhook \ + --name=${TRIGGER_NAME} \ + --substitutions='_APP_NAME='${APP_NAME}',_APP_REPO=$(body.repository.git_url),_CONFIG_REPO='${GIT_BASE_URL}'/'${CLUSTER_CONFIG_REPO}',_DEFAULT_IMAGE_REPO='${IMAGE_REPO}',_KUSTOMIZE_REPO='${GIT_BASE_URL}'/'${SHARED_KUSTOMIZE_REPO}',_REF=$(body.ref)' \ + --inline-config=$BUILD_YAML_PATH \ + --secret=${SECRET_PATH} + + ## Retrieve the URL + WEBHOOK_URL="https://cloudbuild.googleapis.com/v1/projects/${PROJECT_ID}/triggers/${TRIGGER_NAME}:webhook?key=${API_KEY_VALUE}&secret=${SECRET_VALUE}" + + ## Create Github Webhook + $BASE_DIR/scripts/git/${GIT_CMD} create_webhook ${APP_NAME} $WEBHOOK_URL + +} + +# execute function matching first arg and pass rest of args through +$1 $2 $3 $4 $5 $6 diff --git a/delivery-platform/docs/demo/1-Provision.md b/delivery-platform/docs/demo/1-Provision.md new file mode 100644 index 0000000..b6e0653 --- /dev/null +++ b/delivery-platform/docs/demo/1-Provision.md @@ -0,0 +1,16 @@ + +# Provision Platform Infrastructure + +```shell +gcloud config set project +source ./env.sh +${BASE_DIR}/resources/provision/provision-all.sh +``` + +# Cleanup + +```shell +gcloud config set project +source ./env.sh +${BASE_DIR}/resources/provision/teardown-all.sh +``` \ No newline at end of file diff --git a/delivery-platform/docs/demo/2-Demo.md b/delivery-platform/docs/demo/2-Demo.md new file mode 100644 index 0000000..b6a795f --- /dev/null +++ b/delivery-platform/docs/demo/2-Demo.md @@ -0,0 +1,155 @@ + +# Delivery Platform Demo + + +## Prerequisites + +From the `delivery-platform` directory run the following commands + +```shell +gcloud config set project +source ./env.sh +export APP_NAME="hello-web" +export TARGET_ENV=dev + +``` + + +### Create a new app + +This process covers the app onboarding process. In this case it includes copying a template repo to a new app repo, enables policy management for the app in ACM. + +This step: +- Copies code from a sample repo +- Updates the name and unique references +- Creates a new repo for the app team +- Adds entries into ACM for dev, stage, prod +- Performs initial code push and deploys to environments + +```shell +./scripts/app.sh create ${APP_NAME} golang +``` + + +### Checkout new app to make changes. + + +Clone Repos & open editor +``` + +git clone -b main $GIT_BASE_URL/${APP_NAME} $WORK_DIR/${APP_NAME} +git clone -b main $GIT_BASE_URL/${SHARED_KUSTOMIZE_REPO} $WORK_DIR/kustomize-base + +cd $WORK_DIR/${APP_NAME} +cloudshell workspace $WORK_DIR/${APP_NAME} +``` + +## Debug on GKE in Cloud Code + +In the editor use ctrl/cmd+shift+p to open the command palet and type `Cloud Code: Run on GKE' +Choose the default profile +Choose/type the `dev` cluster for deployment (any will do) +Accept the default image registry `gcr.io/{project}` +Switch to the output tab to watch progress +Click the URL when complete to see the web page + +Make a change to line 31 of main.go +Save and warch the build / deploy complete +Check the web page again + +This process can be done locally with minikube instead of GKE as well simply by switching the cluster used + + +## Deploy to stage + +### Review the initial state of stage + +open a tunnel +``` +kubectx stage \ + && kubectl port-forward --namespace hello-web $(kubectl get pod --namespace hello-web --selector="app=hello-web,role=backend" --output jsonpath='{.items[0].metadata.name}') 8080:8080 +``` + +Review the web page +use the web page preview in the top right of the browser just to the left of your profile. select preview on port 8080 + +Exit the tunnel +use `ctrl+c` to exit the tunnel + + +### Commit the code to `main` to trigger the deploy to `stage` + +``` +git add . +git commit -m "Updating to V2" +git push origin main +``` + +review the job progress in the [the build history page](https://console.cloud.google.com/cloud-build/builds) + + +### Review the changes +open a tunnel +``` +kubectx stage \ + && kubectl port-forward --namespace hello-web $(kubectl get pod --namespace hello-web --selector="app=hello-web,role=backend" --output jsonpath='{.items[0].metadata.name}') 8080:8080 +``` + +Review the web page +use the web page preview in the top right of the browser just to the left of your profile. select preview on port 8080 + +Exit the tunnel +use `ctrl+c` to exit the tunnel + + +## Release code to prod +Create a release by executing the following command + +```bash +git tag v2 +git push origin v2 +``` +Again review the latest job progress in the [the build history page](https://console.cloud.google.com/cloud-build/builds) + +When complete review the page live by creating your tunnel + + +```bash +kubectx prod \ + && kubectl port-forward --namespace hello-web $(kubectl get pod --namespace hello-web --selector="app=hello-web,role=backend" --output jsonpath='{.items[0].metadata.name}') 8080:8080 + ``` + +And again utilizing the web preview in the top right + +When you're done use `ctrl+c` in the terminal to exit out of the tunnel + + + +## Reset + +To reset the demo and start over delete the application with the following commands + +``` +cd $BASE_DIR +cloudshell workspace . +rm -rf $WORK_DIR/hello-web +./scripts/app.sh delete ${APP_NAME} + +``` + +## Example repo reset +The provision process creates new repos in your git provider for the workshop. In order to use the lastest versions you will need to pull the latest to this local directory then recreate the remote repos using the following commands + + +``` +./resources/provision/repos/teardown.sh +./resources/provision/repos/create-config-repo.sh +./resources/provision/repos/create-template-repos.sh +``` + +## Complete Teardown +Run the following comand to delete all the infrastructure + +``` +./resources/provision/teardown-all.sh +``` diff --git a/delivery-platform/docs/images/clouddeploy-prod.png b/delivery-platform/docs/images/clouddeploy-prod.png new file mode 100644 index 0000000..7c0d3a9 Binary files /dev/null and b/delivery-platform/docs/images/clouddeploy-prod.png differ diff --git a/delivery-platform/docs/images/clouddeploy-stage.png b/delivery-platform/docs/images/clouddeploy-stage.png new file mode 100644 index 0000000..4398a8b Binary files /dev/null and b/delivery-platform/docs/images/clouddeploy-stage.png differ diff --git a/delivery-platform/docs/images/clusters.png b/delivery-platform/docs/images/clusters.png new file mode 100644 index 0000000..0453f5a Binary files /dev/null and b/delivery-platform/docs/images/clusters.png differ diff --git a/delivery-platform/docs/images/platform.png b/delivery-platform/docs/images/platform.png new file mode 100644 index 0000000..8d72c90 Binary files /dev/null and b/delivery-platform/docs/images/platform.png differ diff --git a/delivery-platform/docs/workshop/1.2-provision.md b/delivery-platform/docs/workshop/1.2-provision.md new file mode 100644 index 0000000..9f8b313 --- /dev/null +++ b/delivery-platform/docs/workshop/1.2-provision.md @@ -0,0 +1,201 @@ +# +![](https://crg-sdw-imgs.web.app/sdw-title.svg?) + +In this lab, you will execute initial setup steps to configure your workspace for use with this workshop. You will also review key Terraform files for use in your own customizations in the future. Finally, you will initiate the provision process to create the software delivery platform used in this workshop. + +## Objectives + +- Setup Workspace +- Review overall folder structure +- Review Terraform components (Remote state, Networking, Clusters) +- Review the Cloud Build job for platform provisioning +- Submit the job to provision platform + +Click **Start** to proceed. + +## Select a project + +Choose a project for this tutorial. The project will contain the services and tools to build and deploy applications and the GKE clusters that will act as your development, staging, and production environments. + +**We recommend you create a new project for this tutorial. You may experience undesired side effects if you use an existing project with conflicting settings. If you are using QwikLabs, choose the project starting with _qwiklabs-_** + + + +Change into the `delivery-platform` directory + +```bash +cd delivery-platform +``` + +Click **Next** to proceed. + +## Submit the job to provision platform + +In this step, you will run a command to begin provisioning the platform used in this workshop. + +All of the steps have been tied together for you in a single script. When customizing your own platform, you can utilize only the pieces and sequences that apply in the context of your requirements. + +While the script is running, you will review the details of the scripts and resources. + +First, take a moment to review the complete provision script. Note how each section is broken out for easy execution and customization. + +Review the provision-all.sh file + +### Provision +The provision process begins by prompting you for three values. + +First you will be prompted to select your git provider, and then your git username. Choose Github for the provider and enter your github username. + +Next you will be prompted to provide a github personal access token. A link is provided in the terminal to help you generate one. Click on the link and provide a name such as DeliveryWorkshop for the token. Once generated copy the token and paste into the terminal. + +The final prompt will be asking for an API key. Again a link is provided that will take you to the credentials page where you will create the key. Instructions are provided in the terminal. Once completed copy the API key and paste into the terminal. + + +To initiate the process execute the following commands. + + ```bash +gcloud config set project {{project-id}} +source ./env.sh +${BASE_DIR}/resources/provision/provision-all.sh + ``` + +The provision process will take approximately 20 minutes to complete. Continue with this and future labs while the script is provisioning. + + **Note:** You do not need to wait for the provisioning process to complete before continuing. + +Click **Next** to proceed. + +## Review overall folder structure + +This repository provides a variety of resources for use both in a workshop setting as well as during your own exploration and customization. The majority of key resources are located in the `delivery-platform` folder. + +![](https://crg-sdw-imgs.web.app/folder-structure.png) + +### Docs + +The `delivery-platform/docs` folder contains instructions for demos and workshops. The file you're reading right now is located here as well. You can view it by opening delivery-platform/docs/workshop/1.2-provision.md. + +### Scripts + +The `delivery-platform/scripts` folder contains a few key scripts you'll review in later sections. In practice, you'll incorporate scripts like delivery-platform/scripts/hydrate.sh into your own software delivery pipelines. + +### Workdir + +The `delivery-platform/workdir` folder is used during the workshop as a temporary workspace to house various folders and files either managed by the tools or modified by you in the lessons. One example is your environment state file delivery-platform/workdir/state.env that contains various variables, including your clear text GitHub token. This entire folder is ignored by git and can be deleted safely at the completion of your workshop. + +### Resources + +The final directory of note is the `delivery-platform/resources` folder. This directory contains sample repositories used throughout the lessons and a series of Terraform scripts used to provision the platform. + +Click **Next** to proceed. + +## +![](https://crg-sdw-imgs.web.app/provisioning.png) + +In this lab, you'll provision the underlying infrastructure needed to run the workshop. A software delivery platform consists of four main components: Foundation, Clusters, Tools, and Applications. + +### Foundation + +The foundation includes all of the base GCP resources needed for the software delivery platform in general. In practice, this may include enabling APIs, setting up IAM resources and VPC networks, among others things. In this example, you're creating the underlying networking used throughout the workshop. + + +Click here to review the network configuration + + +This file can be adjusted to meet your organizational needs. + +### Remote State +The Terraform scripts in this workshop utilize remote state management to ensure the state is persisted between users and workspaces. If you were to lose your Cloud Shell instance or chose to access the scripts from another device, remote state management would allow you to interact with your Terraform environment correctly. + +This state is provided by defining a backend of type Google Cloud Storage (GCS) as seen in `/foundation/tf/backend.tmpl` + + +Click here to view the code. + + +The values in this file are replaced with your actual project name and the file is renamed to `backend.tf` automatically in a later step. + +Click **Next** to proceed. + +## Clusters + +Three clusters are created as part of this workshop: `dev`, `stage`, and `prod`. + +* The `prod` cluster will act as a production resource. +* The `stage` cluster will act as a pre-production resource. +* The `dev` cluster is used by the application teams for application development and testing. The `dev` cluster is also used to run the various management, build and deployment tools needed by your delivery process. + +Applications are deployed to production and staging through your delivery pipeline. + +![](https://crg-sdw-imgs.web.app/images/clusters.png) + +In the lab, you are using GKE zonal clusters for cost-saving. To use regional clusters or configure the clusters, +you can modify the `clusters/tf/clusters.tf` file. +Click here to review the current configuration + + +Click **Next** to proceed. + +## Sample Repositories + +A series of sample repositories are provided for use in the workshop. These provide a consistent base configuration for the clusters and the applications that will be deployed on the platform. More details of these repositories and concepts will be covered in later labs. + +In this step of the provisioning process, the scripts will create and push new repos in your git provider for each of these sample repos. From then on, the workshop will use the repos located in your git provider. + +These sample repositories can also be used to speed up the customization of your own platform. + + +Review the folders in the `resources/repos` directory + + +Click **Next** to proceed. + + +## Review the Cloud Build job for platform provisioning + +This workshop utilizes Cloud Build to manage the execution of the Terraform scripts used to provision the platform. + +Executing these steps with Cloud Build allows you to include the exact set of tools and utilities needed for your provisioning process, not having to depend on the user to install tools locally. + +Additionally, running in this fully managed runtime ensures that any issues with local connectivity or session state won't impact the execution of the job. + +### cloudbuild.yaml +To submit workloads to Cloud Build, a `cloudbuild.yaml` file is submitted through the gcloud CLI + + +Review the key lines of the foundation/tf/cloudbuild.yaml + + +## Congratulations !! + + + + +You've reached the end of this lab! + +At this point, you'll probably still have tabs in your editor open from reviewing the various files. Go ahead and close out of those files. + +Also, your provisioning process may not be complete yet. Go ahead and open a new cloud shell terminal to continue with the next few labs. + + + +Once the shell opens runthe following commands to return to the workshop state. + +```bash +gcloud config set project {{project-id}} +cd cloudshell_open/software-delivery-workshop/delivery-platform/ +source ./env.sh + +``` + +Next, your instructor will discuss the concepts related to the next section. + +After the lecture, run the following command to launch the next lab. + +```bash +teachme "${BASE_DIR}/docs/workshop/1.3-kustomize.md" +``` diff --git a/delivery-platform/docs/workshop/1.3-kustomize.md b/delivery-platform/docs/workshop/1.3-kustomize.md new file mode 100644 index 0000000..d5f8745 --- /dev/null +++ b/delivery-platform/docs/workshop/1.3-kustomize.md @@ -0,0 +1,416 @@ + + +# Kustomize + +Kustomize is a tool that introduces a template-free way to customize application configuration that simplifies the use of off-the-shelf applications. It's available as a stand alone utility and is built into kubectl through `kubectl apply -k`. For additional details read more at [kustomize.io](https://kustomize.io/). + + +In this lab, you will work through some of the core concepts of Kustomize and learn how it can be used to help manage variations in applications and environments in your software delivery platform. + + +## Objectives + +- Combine a base YAML file with an overlay +- Use common types: Images, Namespaces, Labels +- Apply multiple overlays to create an application + + +## Prerequisites + +This lab assumes you have already cloned the main repository and starts in the `delivery-platform/` subfolder. + +Copy and execute the following commands in your terminal to set your project and local environment variables + + +```bash +gcloud config set project {{project-id}} +source ./env.sh +``` + + +To demonstrate the kustomize features, you will work out of a temporary directory specifically for this lab. + +Begin by changing to the work directory and creating the lab folder + + +```shell +mkdir $WORK_DIR/kustomize-lab +cd $WORK_DIR/kustomize-lab +cloudshell workspace . +``` + + +Click **Next** to proceed. + + +## Kustomize Basics + +One of the core features of Kustomize is its ability to overlay multiple file configurations. This allows you to manage a base set of resources and add overlays to configure the specific variations you may see between apps and environments. + +Rather than calling `kubectl apply` directly on your k8s manifests, you would first run `kustomize build` to render or “hydrate” your manifests with the variations you’ve included from kustomize. + +The `kustomize` command looks for a file called `kustomization.yaml` as the primary configuration. + +To start, you will create a folder to hold your base configuration files + + +```shell +mkdir -p chat-app/base +``` + + +Create a simple `deployment.yaml` in the base folder + +Copy and execute the following commands in your terminal + + +```yaml +cat < chat-app/base/deployment.yaml +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + metadata: + name: chat-app + spec: + containers: + - name: chat-app + image: chat-app-image +EOF +``` + + +Now create a `kustomization.yaml` file that references the `deployment.yaml` as the base resources where the variations will be applied. + +Create the base `kustomization.yaml` + +Copy and execute the following commands in your terminal + +```yaml +cat < chat-app/base/kustomization.yaml +bases: + - deployment.yaml +EOF +``` + + +Running the kustomize command on the base folder outputs the deployment YAML files with no changes, which is expected since you haven’t included any variations yet. + + +```shell +kustomize build chat-app/base +``` + + +Click **Next** to proceed. + + +## Common Overlays: Images, Namespaces, Labels + +Images, namespaces and labels are very commonly customized for each application and environment. Since they are commonly changed, Kustomize lets you declare them directly in the `kustomize.yaml`, eliminating the need to create many patches for these common scenarios. + +This technique is often used to create a specific instance of a template. One base set of resources can now be used for multiple implementations by simply changing the name and its namespace. + +In this example, you will add a namespace, name prefix and add some labels to your `kustomization.yaml`. + +Update the `kustomization.yaml` file to include common labels and namespaces. + +Copy and execute the following commands in your terminal + +```yaml +cat < chat-app/base/kustomization.yaml +bases: + - deployment.yaml + +namespace: my-namespace +nameprefix: my- +commonLabels: + app: my-app + +EOF +``` + + +Executing the build at this point shows that the resulting YAML file now contains the namespace, labels and prefixed names in both the service and deployment definitions. + + +```shell +kustomize build chat-app/base +``` + + +Note how the output contains labels and namespaces that are not in the deployment YAML file. Note also how the name was changed from `chat-app` to `my-chat-app` + +(Output do not copy) + + +
+kind: Deployment
+metadata:
+  labels:
+    app: my-app
+  name: my-chat-app
+  namespace: my-namespace
+
+ + +Click **Next** to proceed. + + +## Patches and Overlays + +Kustomize also provides the ability to apply patches that overlay the base resources. This technique is often used to provide variability between applications and environments. + +In this step, you will create environment variations for a single application that use the same base resources. + +Start by creating folders for the different environments + + +```shell +mkdir -p chat-app/dev +mkdir -p chat-app/prod +``` + + +Notice that the patches below do not contain the container image name. That value is provided in the base/deployment.yaml you created in the previous step. These patches do however contain unique environment variables for dev and prod. + +Write the patches with the following command + + +```yaml +cat < chat-app/dev/deployment.yaml +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: chat-app + env: + - name: ENVIRONMENT + value: dev +EOF +``` + + +Now do the same for prod + + +```yaml +cat < chat-app/prod/deployment.yaml +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: chat-app + env: + - name: ENVIRONMENT + value: prod +EOF +``` + + +Now implement the kustomize YAML files for each directory. + +First, rewrite the base customization.yaml, remove the namespace and name prefix as this is just the base config with no variation. Those fields will be moved to the environment files in just a moment. + +Rewrite the `base/kustomization.yaml `file + +Copy and execute the following commands in your terminal + +```yaml +cat < chat-app/base/kustomization.yaml +bases: + - deployment.yaml + +commonLabels: + app: chat-app + +EOF +``` + + +Now implement the variations for dev and prod. Note the addition now of the `patches:` section of the file. This indicates that kustomize should overlay those files on top of the base resources. + +Create the `dev/kustomization.yaml `file + +Copy and execute the following commands in your terminal + +```yaml +cat < chat-app/dev/kustomization.yaml +bases: +- ../base + +namespace: dev +nameprefix: dev- +commonLabels: + env: dev + + +patches: +- deployment.yaml +EOF +``` + + +Now create the `prod/kustomization.yaml `file + +Copy and execute the following commands in your terminal + +```yaml +cat < chat-app/prod/kustomization.yaml +bases: +- ../base + +namespace: prod +nameprefix: prod- +commonLabels: + env: prod + +patches: +- deployment.yaml +EOF +``` + + +With the base and environment files created, you can execute the kustomize process to patch the base files. + +Run the following command to see the merged result. + + +```bash +kustomize build chat-app/dev +``` + + +Note the output contains merged results such as labels from base and dev configurations as well as the container image name from the base and the environment variable from the dev folders. + +Click **Next** to proceed. + + +## Multiple layers of overlays + +Many organizations have a team that helps support the app teams and manage the platform. Frequently these teams will want to include specific details that are to be included in all apps across all environments, such as a logging agent. + +In this example, you will create a `shared-kustomize` folder and resources which will be included in all applications regardless of which environment they’re deployed. + +Start by creating the folder + + +```shell +mkdir shared-kustomize +``` + + +Create a simple `deployment.yaml` in the base folder + +Copy and execute the following commands in your terminal + +```yaml +cat < shared-kustomize/deployment.yaml +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: logging-agent + image: logging-agent-image +EOF +``` + + +Now create a `kustomization.yaml` file that references the `deployment.yaml` as the base resources where the variations will be applied. + +Create the base `kustomization.yaml` + +Copy and execute the following commands in your terminal + +```yaml +cat < shared-kustomize/kustomization.yaml +bases: + - deployment.yaml +EOF +``` + + +Since you want the `shared-kustomize` folder to be the base for all your applications, you will need to update your `chat-app/base/kustomization.yaml` to use `shared-kustomize` as the base. Then patch its own deployment.yaml on top. The environment folders will then patch again on top of that. + +Copy and execute the following commands in your terminal + +```yaml +cat < chat-app/base/kustomization.yaml +bases: + - ../../shared-kustomize + +commonLabels: + app: chat-app + +patches: +- deployment.yaml + +EOF +``` + + +Run the following command to see the merged result. + + +```shell +kustomize build chat-app/dev +``` + + +Note the output contains merged results from the app base, the app environment, and the shared-kustomize folders. Specifically, you can see in the containers section values from all three locations. + +(output do not copy) +
+    containers:
+          - env:
+            - name: ENVIRONMENT
+              value: dev
+            name: chat-app
+          - image: image
+            name: app
+          - image: logging-agent-image
+            name: logging-agent
+
+ +Click **Next** to proceed. + + +## Congratulations !! + + + + +You've reached the end of this lab. + +Close any editor tabs you still have open and change back to the main lab directory with the following commands. + + +```bash +cd $BASE_DIR +cloudshell workspace . +``` + + +Next your instructor will discuss the concepts related to the next section + +After the lecture, run the following command to launch the next lab. + + +```bash +teachme "${BASE_DIR}/docs/workshop/2.1-app-onboarding.md" +``` + diff --git a/delivery-platform/docs/workshop/2.1-app-onboarding.md b/delivery-platform/docs/workshop/2.1-app-onboarding.md new file mode 100644 index 0000000..ebaece1 --- /dev/null +++ b/delivery-platform/docs/workshop/2.1-app-onboarding.md @@ -0,0 +1,152 @@ +# App Onboarding + +Creating a new application often includes more than loading a language's example "hello world" app and start coding. + +In reality, the process invariably includes at least a minimal set of core tasks such as: + +* Creating a new app repository for the developers. +* Pulling in a template from an approved list of base applications. +* Setting up foundational build and deploy mechanics. +* Setting up various other ancillary elements like registering with enterprise change management systems. + +Mature organizations implement automation processes that enable developers to self-serve and minimize platform administrator engagement. + +This lab examines patterns for automating the creation of a new application and reviews an automation script that can be modified and extended to meet your custom needs. + +## Objectives + +- Review Base Repos +- Review app-create script +- Create and review a new application + + +Click **Start** to begin the lab. + +## Prerequisites + +This lab assumes you have already cloned the main repository and are starting in the `delivery-platform/` folder. + +This lab does not require infrastructure to be fully provisioned. + + + + + +Execute the following command to set your project and local environment variables + +```bash +gcloud config set project {{project-id}} +source ./env.sh + +``` + +Click **Next** to continue. + + +## Base Repositories + +The app onboarding process included in this workshop performs a few key functions, one of which is to create a new repository from a template. + +As part of the provisioning process starter repos from this workshop were copied to your remote git provider for use throughout the lessons. This lab will utilize these repos in the onboarding process. + +Review the local version of the app templates repo in the `resources/repos/app-templates` directory + + +Click here to locate the folder + +Take note of the folder names used for each of the template types. These names are used to indicate which template should be used for the new application. + +Next to the `app-templates` folder is a folder called `shared-kustomize`. This directory contains base kustomize overlays that will be merged with the app configs as part of the hydration step in the pipeline. Review the contents of that folder and note that the subfolder names match those of the app-template directory. + +To customize or add additional templates, add the appropriate folders to both of these directories. + +The third folder in this directory, named `cluster-config` is a sample implementation of a repo used by `Anthos Config Manager`, which you'll use later in this lab. This repo will contain the full rendered manifests to be applied to the various clusters. + +## App Creation Script + +The script utilized in this workshop was created to not only facilitate app onboarding within this workshop but also to make the patterns visible and customizable. + +While you can utilize the example script as is, it's assumed your organization will have different onboarding needs and thus it will need to be modified. + +Rather than provide a locked-down opinionated binary, this script is written as an accessible bash script to inform and inspire your own implementation. + + +Review the `scripts/app.sh` file + +The usage of this script is simply `app.sh create new_app_name template_to_use`. The create function performs the following steps: + +- Clones down templates repo +- Modify template placeholders with actual values (i.e. the actual app name) +- Creates new remote repo in your git provider to hold the app instance +- Configures deployment targets for the various environments +- Configures base software delivery pipeline + +Variations and customizations you may choose to implement within your organization might include registering the app in a CMDB, updating ingress or load balancer entries, incorporating a user interface for developer self-service, etc. + + + +## Create & Review new application + +In this step, you will create a new instance of an app using the provided app creation script that will be used throughout the remainder of the workshop. + +First, set some variables, including the name of the app to be created. + +```bash +export APP_NAME=hello-web + +``` + + +Next execute the app create function to instantiate the app + +```bash +./scripts/app.sh create ${APP_NAME} golang +``` + +## Review your new app + +The onboarding process created a variety of resources and updated configurations related to your new application instance. Take a moment to review what the script did. + +- Review your new app in your git provider +- Review the entry in the config repo +- Review the webhook in the app repo +- Review the trigger in Cloud Build + + +*Review your new app in your git provider* + +Head over to [github.com](https://github.com) for your username. Under your repositories locate the newly created `hello-web` repository. + +Review the configuration file such as `skaffold.yaml` or `k8s/dev/deployment.yaml` and note that the onboarding script inserted the unique app name throughout. + +*Review the entry in the config repo* +Again in [github.com](https://github.com) for your username. Under your repositories locate the `mcd-cluster-config` repo. This base repository was created during the provisioning process, but has just been updated to include a `hello-web` namespace in the dev stage and prod folders. + + +*Review the webhook in the app repo* +A webhook was added to the `hello-web` application repo in github for your new app. + +Back on [github.com](https://github.com) for your username, under your repositories locate the newly created `hello-web` repository. Then on settings page select webhooks from the left nav. + +Review the webhook that was added to the repository. + +*Review the trigger in Cloud Build* + +The webhook in the github repo makes a call out to Cloud Build to execute the build and deploy processes. + +Review the Cloud Build trigger by visiting the [Cloud Build Triggers page](https://console.cloud.google.com/cloud-build/triggers) in the Google Cloud console. + +## Congratulations !! + + + + +You've reached the end of this lab + + +After the next lecture, run the following command to launch the next lab. + +```bash +teachme "${BASE_DIR}/docs/workshop/2.2-develop.md" +``` + diff --git a/delivery-platform/docs/workshop/2.2-develop.md b/delivery-platform/docs/workshop/2.2-develop.md new file mode 100644 index 0000000..0440bb9 --- /dev/null +++ b/delivery-platform/docs/workshop/2.2-develop.md @@ -0,0 +1,179 @@ + +# App Development + +In this lab, you will walk through a typical developer workflow use case that highlights local development integrations available within Cloud Code. + + +## Objectives + + +* Introduce Cloud Code Plugins +* Introduce skaffold for pipeline abstraction +* Utilize local and remote clusters +* Utilize hot reloading +* Debug apps on remote GKE realtime + + +## Prerequisites + +This lab assumes you have already cloned the main repository and are starting in the `delivery-platform/` folder. + +Next, select the project you are using for the lab. + + + +Execute the following command to set your project and local environment variables. + + +```bash +gcloud config set project {{project-id}} +source ./env.sh +export APP_NAME=hello-web +``` + + + +## Clone the repositories + +In a previous lab, you created a new application for use by the development team. In practice, a dev team member would trigger the script through a UI or other process. Once the app is created, the developer and the rest of their team would need to clone the remote repo first before making changes. + +In practice, some organizations may choose to include kustomize overlays through HTTP references rather than local references to the files. + +In this workshop, the kustomize overlays are included as local files for clarity and discussion. So you'll be cloning that repo as well. + +Clone the application and kustomize repositories. + + +```bash +git clone -b main $GIT_BASE_URL/${APP_NAME} $WORK_DIR/${APP_NAME} +git clone -b main $GIT_BASE_URL/${SHARED_KUSTOMIZE_REPO} $WORK_DIR/kustomize-base +``` + + +Switch your editor's workspace to the application directory. + + +```bash +cd $WORK_DIR/${APP_NAME} +cloudshell workspace $WORK_DIR/${APP_NAME} +``` + + +## Develop with Cloud Code + +Cloud Code reduces many tedious and repetitive steps a developer typically needs to execute for developing container based applications. + +Cloud Code features can be accessed through the command palette by typing `Cmd+Shift+P` on Mac or `Ctrl+Shift+P` on Windows or ChromeOS, then typing `Cloud Code` to filter down to Cloud Code commands. + +Alternatively, many of the most commonly used commands are available by clicking on the Cloud Code indicator in the status bar below the editor. + +Finally, there are use-case specific icons on the left side of the navigator that take you directly to sections like the API or GKE Explorer. + + +Click here to highlight the GKE explorer + + + +## Local Development Loop + +During development, it’s useful to work with your application against a local Kubernetes cluster like `minikube`. In this section, you’ll use the Cloud Code plugin to deploy your application to a local instance of minikube, then hot reload changes made in the code. + +Start minikube + +In Cloud Shell IDE, click the word `minikube` in the status bar. In the prompt at the top of the screen, click on the `minikube` option, then click. `start` + +**Wait for `minikube` to finish starting**. It takes 1-3 minutes. + +Be sure you’re using the minikube context by setting it on the config in your terminal. + +```bash +kubectl config use-context minikube +``` + +### Run on Minikube + +Once minikube is running, build and deploy the application with Cloud Code. Locate the Run on K8s command in your command pallet by: + +1. Using the hotkey combination `cmd/ctrl+shift+p` +1. Type “`Cloud Code: Run on Kubernetes`” and select the option +1. Select `Kubernetes: Run/Debug - local` and confirm you want to use the current context (minikube) to run the app. + +Once the deployment is complete, review the app by clicking on the URL provided in the output window. + +Change this line in the main.go file to a different output. When you save the file, notice the build and deploy automatically begin to redeploy the application to the cluster. + +Once completed, return to the tab with the application deployed and refresh the window to see the results updated. + +To stop the hot deploy process, find the stop button in the debug configuration. + + +Locate the debug configuration pane + + +Stopping the process not only detaches the process but also cleans up all the resources used on the cluster. + + +## Remote Debugging + +Cloud Code also simplifies developing for Kubernetes by integrating live debugging of applications running in Kubernetes clusters. For this section, you will deploy your application to a remote dev cluster and perform some simple live debugging. + +Cloud Code can utilize any Kubernetes cluster in your local contexts. For this example, you'll use the remote dev cluster. + +Cloud Code understands remote vs. local deployment patterns. When you used minikube earlier, Cloud Code defaulted to using your local Docker build and store the image locally. Since this is a remote deployment the system will prompt you for a remote registry. + +Under the hood, Cloud Code is using skaffold to build and deploy. For the remote clusters, you’ll be prompted for which profile to use. The workshop is designed to use the [default] profile for local development. + +These prompts will occur only when there are no existing configurations found. To set or change configurations, switch to the debug view then select the settings icon next to the launch configurations dropdown. + +First, start by switching to the dev cluster context with the command below + + +```bash +kubectl config use-context dev +``` + + +This time you’ll choose `Cloud Code: Debug on Kubernetes` from the command pallet. + +1. Using the hotkey combination `cmd/ctrl+shift+p` +1. Type “`Cloud Code: Debug on Kubernetes`” and select the option +1. Select `Kubernetes: Run/Debug - dev` and confirm you want to use the current context (dev) to run the app. +1. If asked which image repo to use, choose the default value of `gcr.io/{project}` +1. If asked which cluster to use, choose to use the current `dev` context. + +To watch the progress be sure you’ve selected the Output window. The editor may have switched to debug view. + +Once the build and deployment complete, the output will provide a URL for viewing the deployed application. + +Click the URL provided to see the application results. + +Stopping the process by clicking the stop button in the debug console + + +## Return to the main workspace + +When you're done modifying the application and reviewing the changes, return to the main workshop workspace by executing the following command. + +Switch your editor's workspace to the application directory to prepare for the next lab. + + +```bash +cd $BASE_DIR +cloudshell workspace $BASE_DIR/.. +``` + + +## Congratulations !! + + + + +You've reached the end of this lab. + +After the next lecture, run the following command to launch the next lab. + + +```bash +teachme "${BASE_DIR}/docs/workshop/3.2-release-progression.md" +``` + diff --git a/delivery-platform/docs/workshop/3.2-release-progression.md b/delivery-platform/docs/workshop/3.2-release-progression.md new file mode 100644 index 0000000..0909e66 --- /dev/null +++ b/delivery-platform/docs/workshop/3.2-release-progression.md @@ -0,0 +1,207 @@ +# Release Progression w/ Git Event + +In this lab, you will implement a release progression process that utilizes git events as the primary workflow. Using git to manage the process allows organizations to move many of the stage gates and checks towards the development team. While this practice is a core concept in the popular gitops practices, git-based workflows can be used with traditional imperative as well as gitops oriented declarative processes. + +## Objectives +- Review lifecycle variations +- Create commit & tag triggers +- Initiate a release +- Approve a release + + + +Click **Start** to begin the lab. + +## Prerequisites + +This lab assumes you have already cloned the main repository and are starting in the `delivery-platform/` folder. + +This lab also assumes you are continuing from the previous lab and should still have the hello-web app in your workdir. + + +### Setup your environment + + + +Execute the following command to set your project and local environment variables + +```bash +gcloud config set project {{project-id}} +source ./env.sh +export APP_NAME=hello-web +``` + + +## CI/CD Models and Workflows + +### Source control models +There are many models for how exactly to structure your branches and releases. This lab follows a model similar to how Google manages code, but the techniques can be applied to any model. + +At Google, software engineers create a branch for small updates to the code base. These changes are then submitted for validation and review as a ChangeList (CL). The CL process runs automated tests as well as requiring approval by code owners. Once approved, the code is automatically integrated into the main code base, and is a candidate to be deployed in the next release. Change management for a production release is handled through a separate process, but once approved the code is pulled from the main line. + +To approximate this model in Git, this lab assumes the following model: + +* Tags represent production releases. +* The Main branch represents code ready for production, or "staged" for production. +* Any development efforts are handled on branches other than Main. + +The PR process in most Git providers allows you to implement your own review processes. Many providers allow you to restrict which users are allowed to perform actions on the various branches and tags, which can be used for separation of concerns. + +### Git events for workflow +A central part of any CI/CD system utilizes git events as the trigger for continuous processes. + +This lab is currently triggered by events from pushes to branches as well as the creation of new tags. + +Click **Next** to begin reviewing the implementation. + +## Review what Onboarding setup + +Earlier, you created an application using the scripts provided on this platform. In that process, a few key elements were set up for your application to enable the workflow in this lab. + +### Webhooks +Whenever events occur within your provider, the events can trigger a request to an external endpoint called a webhook. A webhook was created and configured for your `hello-web` application during the onboard process. You can review it in the settings section of your repository. + +Execute the following command to generate a link to that location in your project. + +```bash +echo $GIT_BASE_URL/$APP_NAME/settings/hooks +``` +Copy the value and paste it into a new browser tab to review the webhook on your application repo. + +### Triggers + +The webhook is configured to call an endpoint with details about the event that occurred. The onboarding script created an endpoint in Google Cloud to accept these webhook requests. + +Review the trigger setup for your application in Cloud Build with the link below +#### If you chose ACM as continuous delivery platform: +[https://console.cloud.google.com/cloud-build/triggers/edit/hello-web-webhook-trigger](https://console.cloud.google.com/cloud-build/triggers/edit/hello-web-webhook-trigger) +#### If you chose Clouddeploy as continuous delivery platform: +[https://console.cloud.google.com/cloud-build/triggers/edit/hello-web-webhook-trigger](https://console.cloud.google.com/cloud-build/triggers/edit/hello-web-clouddeploy-webhook-trigger) + +### Workflow Configuration +#### If you chose ACM as continuous delivery platform +The webhook endpoint triggers a Cloud Build job that executes a workflow defined in a `cloudbuild.yaml` file. The cloud build implementation includes steps to clone the repo, build and push an image, hydrate resources, and finally deploy the assets to the appropriate environment. + +Review the `cloudbuild.yaml` in your `hello-web` project + +Click here to open cloudbuild.yaml + + +#### If you chose Clouddeploy as continuous delivery platform +The webhook endpoint triggers a Cloud Build job that executes a workflow defined in a `cloudbuild-cd.yaml` file. The cloud build implementation includes steps to clone the repo, build and push an image, and finally create pipelines for deployment in stage and prod. + +Review the `cloudbuild-cd.yaml` in your `hello-web` project + +Click here to open cloudbuild-cd.yaml + + +### View Initial Deployment +During the onboarding process, the workflow was exercised for the first time to create an initial deployment for your application. You can see your application running from the GKE Workloads page: + +Note: If you chose Clouddeploy as delivery system, the pipelines created in previous steps automatically runs in its first target which is stage and completes the deployment. +[Cick here to view the deployment](https://console.cloud.google.com/kubernetes/workload) + +Now since these services are not exposed publicly, you'll need to create a tunnel to the cluster to view the web page. + +Execute the following command to create the stage tunnel + +```bash +kubectx stage \ + && kubectl port-forward --namespace hello-web $(kubectl get pod --namespace hello-web --selector="app=hello-web,role=backend" --output jsonpath='{.items[0].metadata.name}') 8080:8080 + ``` + +With multiple clusters configured, you could use `kubectl` to switch context. In this lab, we use `kubectx`, a command available in Cloud Shell to help you manage and switch context. + +When that's' running, you can select the web preview in the top right of the screen, just to the left of your profile icon. Selecting "Preview on port 8080" will open a view of the app in the cluster. + +Exit out of the tunnel by typing `ctrl+c` in the terminal on your keyboard + +## Deploy code to Stage clusters + +Change the output of your hello-web application to say "Hello World - V2" + + +Changed this line + + + +Commit the code to the main branch by executing the following command + +```bash +cd $WORK_DIR/hello-web +git add . +git commit -m "Updating to V2" +git push origin main +``` +Review the Cloud Build trigger in progress by cicking into the latest job on [the build history page](https://console.cloud.google.com/cloud-build/builds) + +#### If you chose ACM as continuous delivery platform +When the Cloud Build trigger completes, you can review the updated change by opening your tunnel + +```bash +kubectx stage \ + && kubectl port-forward --namespace hello-web $(kubectl get pod --namespace hello-web --selector="app=hello-web,role=backend" --output jsonpath='{.items[0].metadata.name}') 8080:8080 + ``` + +And again utilizing the web preview in the top right + +When you're done use `ctrl+c` in the terminal to exit out of the tunnel + +#### If you chose Clouddeploy as continuous delivery platform +When the Cloud Build trigger completes, it will create a Clouddeploy pipeline in your project. Review it by clicking [Clouddeploy pipelines](https://pantheon.corp.google.com/deploy/delivery-pipelines). +You will see that the pipeline contains two stages : stage and prod. When the pipeline is created, it automatically deploys to stage environment. +![](https://crg-sdw-imgs.web.app/clouddeploy-stage.png) +Click the three dots on stage pipeline box and choose "View release" to see more details. +Now, you can review the updated change by opening your tunnel + +```bash +kubectx stage \ + && kubectl port-forward --namespace hello-web $(kubectl get pod --namespace hello-web --selector="app=hello-web,role=backend" --output jsonpath='{.items[0].metadata.name}') 8080:8080 + ``` + +## Release code to prod + +#### If you chose ACM as continuous delivery platform + +Releases to production are also triggered through git events. In this case, the creation of a tag is the triggering event instead of code being pushed to a branch. + +Create a release by executing the following command + +```bash +git tag v2 +git push origin v2 +``` +Again review the latest job progress in the [the build history page](https://console.cloud.google.com/cloud-build/builds) + +When complete review the page live by creating your tunnel + + +```bash +kubectx prod \ + && kubectl port-forward --namespace hello-web $(kubectl get pod --namespace hello-web --selector="app=hello-web,role=backend" --output jsonpath='{.items[0].metadata.name}') 8080:8080 + ``` + +And again utilizing the web preview in the top right + +When you're done use `ctrl+c` in the terminal to exit out of the tunnel + +#### If you chose Clouddeploy as continuous delivery platform +Go to the pipipeline created earlier by clicking [Clouddeploy pipelines](https://pantheon.corp.google.com/deploy/delivery-pipelines). + + +A "promote" link will appear on stage box, click it. It will open a dialog box, select Prod as target and click Promote button at the bottom left. +It will trigger the release in Prod. Once the pipeline finishes, you will see Prod pipelines in green status as shown in the pic below. + +![](https://crg-sdw-imgs.web.app/clouddeploy-prod.png) +Now, review the page live by creating your tunnel +```bash +kubectx prod \ + && kubectl port-forward --namespace hello-web $(kubectl get pod --namespace hello-web --selector="app=hello-web,role=backend" --output jsonpath='{.items[0].metadata.name}') 8080:8080 + ``` +## Congratulations!!! + + + +You've reached the end of the lab! diff --git a/delivery-platform/env.sh b/delivery-platform/env.sh new file mode 100755 index 0000000..500e4c2 --- /dev/null +++ b/delivery-platform/env.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +## Set Environment Variables +export PROJECT_ID=$(gcloud config get-value project) +export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)') + + +# Set Base directory & Working directory variables +export BASE_DIR=$PWD +export WORK_DIR=$BASE_DIR/workdir +export SCRIPTS=$BASE_DIR/scripts +mkdir -p $WORK_DIR/bin + +export PATH=$PATH:$WORK_DIR/bin:$SCRIPTS: + +# Load any persisted variables +source $SCRIPTS/common/manage-state.sh +load_state + +# Git details +git config --global user.email $(gcloud config get-value account) +git config --global user.name ${USER} +source $SCRIPTS/git/set-git-env.sh + +source $SCRIPTS/common/set-apikey-var.sh + +#set CD system +source $SCRIPTS/continuous-delivery/set-cd-env.sh + +# Set the image repo to use +# if [[ ${IMAGE_REPO} == "" ]]; then +# printf "Enter your image repo location eg: gcr.io/" && read imagerepo +# export IMAGE_REPO=${imagerepo} +# fi + + +# Platform Config +# TODO: PROVISION_TOOL=tf +# TODO: CONFIG_TOOL=acm +# TODO: BUILD_TOOL=cloudbuild +# TODO: IMAGE_REPO=gcr +# TODO: DEPLOY_TOOL=argo + +# Repo Names +export REPO_PREFIX=mcd +export APP_TEMPLATES_REPO=$REPO_PREFIX-app-templates +export SHARED_KUSTOMIZE_REPO=$REPO_PREFIX-shared_kustomize +export CLUSTER_CONFIG_REPO=$REPO_PREFIX-cluster-config +export HYDRATED_CONFIG_REPO=${CLUSTER_CONFIG_REPO} + +# Repository Name +export IMAGE_REPO=gcr.io/${PROJECT_ID} + +# variable pass through for access tokens +export GIT_ASKPASS=$SCRIPTS/git/git-ask-pass.sh + + +# Persist variables for later use +write_state \ No newline at end of file diff --git a/delivery-platform/onboard-env.sh b/delivery-platform/onboard-env.sh new file mode 100755 index 0000000..8ba14f8 --- /dev/null +++ b/delivery-platform/onboard-env.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +## Set Environment Variables +export PROJECT_ID=$(gcloud config get-value project) +export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)') + + +# Set Base directory & Working directory variables +export BASE_DIR=$PWD +export WORK_DIR=$BASE_DIR/workdir +export SCRIPTS=$BASE_DIR/scripts +mkdir -p $WORK_DIR/bin + +export PATH=$PATH:$WORK_DIR/bin:$SCRIPTS: + +export GIT_PROVIDER=GitHub +export GIT_CMD=gh.sh +export CONTINUOUS_DELIVERY_SYSTEM=Clouddeploy + +# Load any persisted variables +source $SCRIPTS/common/manage-state.sh +load_state + +# Git details +git config --global user.email $(gcloud config get-value account) +git config --global user.name ${USER} +source $SCRIPTS/git/set-git-env.sh + +source $SCRIPTS/common/set-apikey-var.sh + +# Repo Names +export REPO_PREFIX=mcd +export APP_TEMPLATES_REPO=$REPO_PREFIX-app-templates +export SHARED_KUSTOMIZE_REPO=$REPO_PREFIX-shared_kustomize +export CLUSTER_CONFIG_REPO=$REPO_PREFIX-cluster-config +export HYDRATED_CONFIG_REPO=${CLUSTER_CONFIG_REPO} + +# Repository Name +export IMAGE_REPO=gcr.io/${PROJECT_ID} + +# variable pass through for access tokens +export GIT_ASKPASS=$SCRIPTS/git/git-ask-pass.sh + + +# Persist variables for later use +write_state \ No newline at end of file diff --git a/delivery-platform/resources/provision/base_image/Dockerfile b/delivery-platform/resources/provision/base_image/Dockerfile new file mode 100644 index 0000000..e52233c --- /dev/null +++ b/delivery-platform/resources/provision/base_image/Dockerfile @@ -0,0 +1,59 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM ubuntu:18.04 +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && \ + apt-get install -y apt-utils wget gnupg2 unzip git jq \ + apt-transport-https ca-certificates \ + dnsutils curl gettext + +ENV TERRAFORM_VERSION=1.0.3 +ENV HELM_VERSION=2.14.3 +ENV KUBECTL_VERSION=1.16.1 +ENV GO_VERSION=1.14.2 +ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DO_NOT_WARN + +# Install terraform +RUN wget -q https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \ + unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \ + chmod +x terraform && \ + mv terraform /usr/local/bin && \ + rm -rf terraform_${TERRAFORM_VERSION}_linux_amd64.zip + +# Install helm +RUN wget -q https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz && \ + tar zxfv helm-v${HELM_VERSION}-linux-amd64.tar.gz && \ + mv linux-amd64/helm /usr/local/bin && \ + rm -rf linux-amd64 helm-v${HELM_VERSION}-linux-amd64.tar.gz + +# Install kubectl +RUN wget -q https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl && \ + chmod +x kubectl && \ + mv kubectl /usr/local/bin/ + +# Install gcloud +RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \ + wget -q -O- https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - && \ + apt-get update && \ + apt-get install -y google-cloud-sdk + +# Install anthos-platform-cli +# COPY cli anthos-platform-cli +# RUN wget -q https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz && \ +# tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz && \ +# PATH=$PATH:/usr/local/go/bin && \ +# cd anthos-platform-cli && \ +# go build && \ +# cp anthos-platform-cli /usr/local/bin diff --git a/delivery-platform/resources/provision/base_image/README.md b/delivery-platform/resources/provision/base_image/README.md new file mode 100644 index 0000000..c7c0065 --- /dev/null +++ b/delivery-platform/resources/provision/base_image/README.md @@ -0,0 +1,8 @@ + +Create the base imaged used throughout the provisioning processes + +``` + +gcloud builds submit --tag gcr.io/$PROJECT_ID/delivery-platform-installer + +``` \ No newline at end of file diff --git a/delivery-platform/resources/provision/clusters/tf/README.md b/delivery-platform/resources/provision/clusters/tf/README.md new file mode 100644 index 0000000..33e83d7 --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/README.md @@ -0,0 +1,14 @@ + + +Create the foundation +- Requires `base_image` and `foundation` to be created first + +``` +gcloud builds submit +``` + +Destroy the foundation + +``` +gcloud builds submit --config cloudbuild-destroy.yaml +``` \ No newline at end of file diff --git a/delivery-platform/resources/provision/clusters/tf/backend.tmpl b/delivery-platform/resources/provision/clusters/tf/backend.tmpl new file mode 100644 index 0000000..e0f4837 --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/backend.tmpl @@ -0,0 +1,22 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + backend "gcs" { + bucket = "YOUR_PROJECT_ID-delivery-platform-tf-state" + prefix = "clusters" + } +} diff --git a/delivery-platform/resources/provision/clusters/tf/cloudbuild-destroy.yaml b/delivery-platform/resources/provision/clusters/tf/cloudbuild-destroy.yaml new file mode 100644 index 0000000..5816115 --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/cloudbuild-destroy.yaml @@ -0,0 +1,33 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +timeout: 3600s # 1-hour +tags: + - delivery-platform + - delivery-platform-clusters + +steps: +- name: 'gcr.io/${PROJECT_ID}/delivery-platform-installer' + id: 'destroy-clusters' + entrypoint: 'bash' + args: + - '-xe' + - '-c' + - | + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" terraform.tmpl > terraform.tfvars + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" backend.tmpl > backend.tf + + terraform init + terraform destroy -auto-approve + diff --git a/delivery-platform/resources/provision/clusters/tf/cloudbuild.yaml b/delivery-platform/resources/provision/clusters/tf/cloudbuild.yaml new file mode 100644 index 0000000..e0fea85 --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/cloudbuild.yaml @@ -0,0 +1,34 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +timeout: 3600s # 1-hr +tags: + - delivery-platform + - delivery-platform-clusters +steps: +- name: 'gcr.io/${PROJECT_ID}/delivery-platform-installer' + id: 'create-clusters' + entrypoint: 'bash' + args: + - '-xe' + - '-c' + - | + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" terraform.tmpl > terraform.tfvars + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" backend.tmpl > backend.tf + + #export TF_LOG="ERROR" + + terraform init + terraform plan -out=terraform.tfplan + terraform apply -auto-approve terraform.tfplan diff --git a/delivery-platform/resources/provision/clusters/tf/clusters.tf b/delivery-platform/resources/provision/clusters/tf/clusters.tf new file mode 100644 index 0000000..5f9cb53 --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/clusters.tf @@ -0,0 +1,82 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + cluster_type = "regional" +} + +provider "google" { + project = var.project_id +} + +data "google_compute_network" "delivery-platform" { + name = "delivery-platform" +} + +data "google_compute_subnetwork" "delivery-platform-west1" { + name = "delivery-platform-west1" + region = "us-west1" +} + +data "google_compute_subnetwork" "delivery-platform-west2" { + name = "delivery-platform-west2" + region = "us-west2" +} + +data "google_compute_subnetwork" "delivery-platform-central1" { + name = "delivery-platform-central1" + region = "us-central1" +} + +module "delivery-platform-dev" { + source = "./modules/platform-cluster" + project_id = var.project_id + name = "dev" + region = "us-west1" + network = data.google_compute_network.delivery-platform.name + subnetwork = data.google_compute_subnetwork.delivery-platform-west1.name + ip_range_pods = "delivery-platform-pods-dev" + ip_range_services = "delivery-platform-services-dev" + release_channel = "STABLE" + zones = ["us-west1-a"] +} + +module "delivery-platform-staging" { + source = "./modules/platform-cluster" + project_id = var.project_id + name = "stage" + region = "us-west2" + network = data.google_compute_network.delivery-platform.name + subnetwork = data.google_compute_subnetwork.delivery-platform-west2.name + ip_range_pods = "delivery-platform-pods-staging" + ip_range_services = "delivery-platform-services-staging" + release_channel = "STABLE" + zones = ["us-west2-a"] +} + +module "delivery-platform-prod" { + source = "./modules/platform-cluster" + project_id = var.project_id + name = "prod" + region = "us-central1" + network = data.google_compute_network.delivery-platform.name + subnetwork = data.google_compute_subnetwork.delivery-platform-central1.name + ip_range_pods = "delivery-platform-pods-prod" + ip_range_services = "delivery-platform-services-prod" + release_channel = "STABLE" + zones = ["us-central1-a"] +} + diff --git a/delivery-platform/resources/provision/clusters/tf/modules/platform-cluster/main.tf b/delivery-platform/resources/provision/clusters/tf/modules/platform-cluster/main.tf new file mode 100644 index 0000000..bc930b7 --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/modules/platform-cluster/main.tf @@ -0,0 +1,88 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_service_account" "service_account" { + project = var.project_id + account_id = "tf-sa-${var.name}" + display_name = "Cluster Service Account for ${var.name}" +} + +resource "google_project_iam_member" "cluster_iam_logginglogwriter" { + project = var.project_id + role = "roles/logging.logWriter" + member = "serviceAccount:${google_service_account.service_account.email}" +} + +resource "google_project_iam_member" "cluster_iam_monitoringmetricwriter" { + project = var.project_id + role = "roles/monitoring.metricWriter" + member = "serviceAccount:${google_service_account.service_account.email}" +} + +resource "google_project_iam_member" "cluster_iam_monitoringviewer" { + project = var.project_id + role = "roles/monitoring.viewer" + member = "serviceAccount:${google_service_account.service_account.email}" +} + +resource "google_project_iam_member" "cluster_iam_artifactregistryreader" { + project = var.project_id + role = "roles/artifactregistry.reader" + member = "serviceAccount:${google_service_account.service_account.email}" +} + +resource "google_project_iam_member" "cluster_iam_storageobjectviewer" { + project = var.project_id + role = "roles/storage.objectViewer" + member = "serviceAccount:${google_service_account.service_account.email}" +} + +module "platform_cluster" { + source = "terraform-google-modules/kubernetes-engine/google//modules/beta-public-cluster" + version = "~> 15.0.0" + project_id = var.project_id + name = var.name + region = var.region + network = var.network + subnetwork = var.subnetwork + ip_range_pods = var.ip_range_pods + ip_range_services = var.ip_range_services + kubernetes_version = var.gke_kubernetes_version + release_channel = var.release_channel + regional = false + zones = var.zones + + enable_binary_authorization = true + + create_service_account = false + service_account = google_service_account.service_account.email + identity_namespace = "${var.project_id}.svc.id.goog" + node_metadata = "GKE_METADATA_SERVER" + + remove_default_node_pool = true + + node_pools = [ + { + name = "wi-pool" + machine_type = var.machine_type + min_count = var.minimum_node_pool_instances + max_count = var.maximum_node_pool_instances + auto_upgrade = true + initial_node_count = 1 + }, + ] +} + diff --git a/delivery-platform/resources/provision/clusters/tf/modules/platform-cluster/outputs.tf b/delivery-platform/resources/provision/clusters/tf/modules/platform-cluster/outputs.tf new file mode 100644 index 0000000..8309ada --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/modules/platform-cluster/outputs.tf @@ -0,0 +1,39 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "service_account" { + value = module.platform_cluster.service_account + description = "Service account used to create the cluster and node pool(s)" +} + +output "region" { + value = var.region + description = "Region for development cluster" +} + +output "name" { + value = var.name + description = "Cluster Name" +} + +output "endpoint" { + value = module.platform_cluster.endpoint + description = "Cluster endpoint used to identify the cluster" +} +output "location" { + value = module.platform_cluster.location + description = "Cluster location" +} \ No newline at end of file diff --git a/delivery-platform/resources/provision/clusters/tf/modules/platform-cluster/variables.tf b/delivery-platform/resources/provision/clusters/tf/modules/platform-cluster/variables.tf new file mode 100644 index 0000000..d88278b --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/modules/platform-cluster/variables.tf @@ -0,0 +1,78 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "project_id" { + description = "Project ID where the cluster will run" +} + +variable "name" { + description = "A unique name for the resource" +} + +variable "region" { + description = "The name of the region to run the cluster" +} + +variable "network" { + description = "The name of the network to run the cluster" +} + +variable "subnetwork" { + description = "The name of the subnet to run the cluster" +} + +variable "ip_range_pods" { + description = "The secondary range for the pods" +} + +variable "ip_range_services" { + description = "The secondary range for the services" +} + +variable "machine_type" { + description = "Type of node to use to run the cluster" + default = "e2-standard-2" +} + +variable "gke_kubernetes_version" { + description = "Kubernetes version to deploy Masters and Nodes with" + default = "1.18" +} + +variable "minimum_node_pool_instances" { + type = number + description = "Number of node-pool instances to have active" + default = 1 +} + +variable "maximum_node_pool_instances" { + type = number + description = "Maximum number of node-pool instances to scale to" + default = 1 +} + +variable "release_channel" { + type = string + description = "(Beta) The release channel of this cluster. Accepted values are `UNSPECIFIED`, `RAPID`, `REGULAR` and `STABLE`. Defaults to `UNSPECIFIED`." + default = "STABLE" +} + +variable "zones" { + type = list(string) + description = "The zones to host the cluster in (optional if regional cluster / required if zonal)" + default = [] +} + diff --git a/delivery-platform/resources/provision/clusters/tf/outputs.tf b/delivery-platform/resources/provision/clusters/tf/outputs.tf new file mode 100644 index 0000000..2da1ce9 --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/outputs.tf @@ -0,0 +1,46 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "development__cluster-service-account" { + value = module.delivery-platform-dev.service_account + description = "Service account used to create the cluster and node pool(s)" +} + +output "staging__cluster-service-account" { + value = module.delivery-platform-staging.service_account + description = "Service account used to create the cluster and node pool(s)" +} + +output "dev_name" { value = module.delivery-platform-dev.name } +output "dev_location" { value = module.delivery-platform-dev.location } +output "dev_endpoint" { + value = module.delivery-platform-dev.endpoint + sensitive = true +} + +output "stage_name" { value = module.delivery-platform-staging.name } +output "stage_location" { value = module.delivery-platform-staging.location } +output "stage_endpoint" { + value = module.delivery-platform-staging.endpoint + sensitive = true +} + +output "prod_name" { value = module.delivery-platform-prod.name } +output "prod_location" { value = module.delivery-platform-prod.location } +output "prod_endpoint" { + value = module.delivery-platform-prod.endpoint + sensitive = true +} diff --git a/delivery-platform/resources/provision/clusters/tf/terraform.tmpl b/delivery-platform/resources/provision/clusters/tf/terraform.tmpl new file mode 100644 index 0000000..19010a7 --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/terraform.tmpl @@ -0,0 +1,17 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +project_id = "YOUR_PROJECT_ID" diff --git a/delivery-platform/resources/provision/clusters/tf/variables.tf b/delivery-platform/resources/provision/clusters/tf/variables.tf new file mode 100644 index 0000000..9a97b7a --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/variables.tf @@ -0,0 +1,21 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "project_id" { + description = "The project ID to host the cluster in" +} + + diff --git a/delivery-platform/resources/provision/clusters/tf/versions.tf b/delivery-platform/resources/provision/clusters/tf/versions.tf new file mode 100644 index 0000000..0823918 --- /dev/null +++ b/delivery-platform/resources/provision/clusters/tf/versions.tf @@ -0,0 +1,25 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.13" + required_providers { + google = { + source = "hashicorp/google" + version = "~> 3.44.0" + } + } +} diff --git a/delivery-platform/resources/provision/foundation/tf/README.md b/delivery-platform/resources/provision/foundation/tf/README.md new file mode 100644 index 0000000..e73924f --- /dev/null +++ b/delivery-platform/resources/provision/foundation/tf/README.md @@ -0,0 +1,14 @@ + + +Create the foundation +- Requires `base_image` to be created first + +``` +gcloud builds submit +``` + +Destroy the foundation + +``` +gcloud builds submit --config cloudbuild-destroy.yaml +``` \ No newline at end of file diff --git a/delivery-platform/resources/provision/foundation/tf/backend.tmpl b/delivery-platform/resources/provision/foundation/tf/backend.tmpl new file mode 100644 index 0000000..f2ee1b3 --- /dev/null +++ b/delivery-platform/resources/provision/foundation/tf/backend.tmpl @@ -0,0 +1,22 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + backend "gcs" { + bucket = "YOUR_PROJECT_ID-delivery-platform-tf-state" + prefix = "foundation" + } +} diff --git a/delivery-platform/resources/provision/foundation/tf/cloudbuild-destroy.yaml b/delivery-platform/resources/provision/foundation/tf/cloudbuild-destroy.yaml new file mode 100644 index 0000000..352ef5e --- /dev/null +++ b/delivery-platform/resources/provision/foundation/tf/cloudbuild-destroy.yaml @@ -0,0 +1,31 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +timeout: 3600s # 1-hour +tags: + - delivery-platform + - delivery-platform-foundation +steps: +- name: 'gcr.io/${PROJECT_ID}/delivery-platform-installer' + id: 'destroy-foundation' + entrypoint: 'bash' + args: + - '-xe' + - '-c' + - | + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" terraform.tmpl > terraform.tfvars + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" backend.tmpl > backend.tf + + terraform init + terraform destroy -auto-approve diff --git a/delivery-platform/resources/provision/foundation/tf/cloudbuild.yaml b/delivery-platform/resources/provision/foundation/tf/cloudbuild.yaml new file mode 100644 index 0000000..453e50d --- /dev/null +++ b/delivery-platform/resources/provision/foundation/tf/cloudbuild.yaml @@ -0,0 +1,41 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +timeout: 3600s # 1-hr +tags: + - delivery-platform + - delivery-platform-foundation +steps: +- name: 'gcr.io/cloud-builders/gsutil' + id: 'create-tf-state-bucket' + entrypoint: 'bash' + args: + - '-xe' + - '-c' + - | + gsutil mb gs://${PROJECT_ID}-delivery-platform-tf-state || true + +- name: 'gcr.io/${PROJECT_ID}/delivery-platform-installer' + id: 'create-foundation' + entrypoint: 'bash' + args: + - '-xe' + - '-c' + - | + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" terraform.tmpl > terraform.tfvars + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" backend.tmpl > backend.tf + + terraform init + terraform plan -out=terraform.tfplan + terraform apply -auto-approve terraform.tfplan diff --git a/delivery-platform/resources/provision/foundation/tf/networks.tf b/delivery-platform/resources/provision/foundation/tf/networks.tf new file mode 100644 index 0000000..b50e07d --- /dev/null +++ b/delivery-platform/resources/provision/foundation/tf/networks.tf @@ -0,0 +1,73 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +provider "google" { + project = var.project_id +} + +resource "google_compute_network" "delivery-platform" { + name = "delivery-platform" + auto_create_subnetworks = false + depends_on = [module.project-services.project_id] +} + +resource "google_compute_subnetwork" "delivery-platform-central1" { + name = "delivery-platform-central1" + ip_cidr_range = "10.2.0.0/16" + region = "us-central1" + network = google_compute_network.delivery-platform.self_link + + secondary_ip_range { + range_name = "delivery-platform-pods-prod" + ip_cidr_range = "172.16.0.0/16" + } + secondary_ip_range { + range_name = "delivery-platform-services-prod" + ip_cidr_range = "192.168.2.0/24" + } +} + +resource "google_compute_subnetwork" "delivery-platform-west1" { + name = "delivery-platform-west1" + ip_cidr_range = "10.4.0.0/16" + region = "us-west1" + network = google_compute_network.delivery-platform.self_link + + secondary_ip_range { + range_name = "delivery-platform-pods-dev" + ip_cidr_range = "172.18.0.0/16" + } + secondary_ip_range { + range_name = "delivery-platform-services-dev" + ip_cidr_range = "192.168.4.0/24" + } +} + +resource "google_compute_subnetwork" "delivery-platform-west2" { + name = "delivery-platform-west2" + ip_cidr_range = "10.5.0.0/16" + region = "us-west2" + network = google_compute_network.delivery-platform.self_link + + secondary_ip_range { + range_name = "delivery-platform-pods-staging" + ip_cidr_range = "172.19.0.0/16" + } + secondary_ip_range { + range_name = "delivery-platform-services-staging" + ip_cidr_range = "192.168.5.0/24" + } +} diff --git a/delivery-platform/resources/provision/foundation/tf/services.tf b/delivery-platform/resources/provision/foundation/tf/services.tf new file mode 100644 index 0000000..dd47a08 --- /dev/null +++ b/delivery-platform/resources/provision/foundation/tf/services.tf @@ -0,0 +1,36 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "project-services" { + source = "terraform-google-modules/project-factory/google//modules/project_services" + version = "~> 11.1" + + project_id = var.project_id + + # Don't disable the services + disable_services_on_destroy = false + disable_dependent_services = false + + activate_apis = [ + "compute.googleapis.com", + "container.googleapis.com", + "cloudresourcemanager.googleapis.com", + "sqladmin.googleapis.com", + "artifactregistry.googleapis.com", + "gkehub.googleapis.com", + "multiclusteringress.googleapis.com" + ] +} diff --git a/delivery-platform/resources/provision/foundation/tf/terraform.tmpl b/delivery-platform/resources/provision/foundation/tf/terraform.tmpl new file mode 100644 index 0000000..19010a7 --- /dev/null +++ b/delivery-platform/resources/provision/foundation/tf/terraform.tmpl @@ -0,0 +1,17 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +project_id = "YOUR_PROJECT_ID" diff --git a/delivery-platform/resources/provision/foundation/tf/variables.tf b/delivery-platform/resources/provision/foundation/tf/variables.tf new file mode 100644 index 0000000..56b442b --- /dev/null +++ b/delivery-platform/resources/provision/foundation/tf/variables.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "project_id" { + description = "The project ID to host the cluster in" +} diff --git a/delivery-platform/resources/provision/foundation/tf/versions.tf b/delivery-platform/resources/provision/foundation/tf/versions.tf new file mode 100644 index 0000000..0823918 --- /dev/null +++ b/delivery-platform/resources/provision/foundation/tf/versions.tf @@ -0,0 +1,25 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.13" + required_providers { + google = { + source = "hashicorp/google" + version = "~> 3.44.0" + } + } +} diff --git a/main_test.go b/delivery-platform/resources/provision/management-tools/acm/acm-install.sh old mode 100644 new mode 100755 similarity index 56% rename from main_test.go rename to delivery-platform/resources/provision/management-tools/acm/acm-install.sh index 3d74151..b45ce61 --- a/main_test.go +++ b/delivery-platform/resources/provision/management-tools/acm/acm-install.sh @@ -1,29 +1,19 @@ -/** -# Copyright 2015 Google Inc. All rights reserved. +#!/usr/bin/env bash + +# Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -**/ - -package main - -import ( - "cloud.google.com/go/compute/metadata" - "testing" -) -func TestGCE(t *testing.T) { - i := newInstance() - if !metadata.OnGCE() && i.Error != "Not running on GCE" { - t.Error("Test not running on GCE, but error does not indicate that fact.") - } -} +#gsutil cp gs://config-management-release/released/latest/config-management-operator.yaml config-management-operator.yaml +gcloud builds submit \ + --substitutions=_CLUSTER_CONFIG_REPO="$GIT_BASE_URL/$CLUSTER_CONFIG_REPO",_BRANCH="main",_DEV_PATH="dev",_STAGE_PATH="stage",_PROD_PATH="prod" diff --git a/delivery-platform/resources/provision/management-tools/acm/acm.tf b/delivery-platform/resources/provision/management-tools/acm/acm.tf new file mode 100644 index 0000000..8f44267 --- /dev/null +++ b/delivery-platform/resources/provision/management-tools/acm/acm.tf @@ -0,0 +1,58 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "acm-dev" { + source = "terraform-google-modules/kubernetes-engine/google//modules/acm" + version = "14.1.0" + + project_id = var.project_id + cluster_name = data.terraform_remote_state.clusters.outputs.dev_name + location = data.terraform_remote_state.clusters.outputs.dev_location + cluster_endpoint = data.terraform_remote_state.clusters.outputs.dev_endpoint + + secret_type = "ssh" + sync_repo = var.acm_repo_location + sync_branch = var.acm_branch + policy_dir = var.dev_dir +} +module "acm-stage" { + source = "terraform-google-modules/kubernetes-engine/google//modules/acm" + version = "14.1.0" + + project_id = var.project_id + cluster_name = data.terraform_remote_state.clusters.outputs.stage_name + location = data.terraform_remote_state.clusters.outputs.stage_location + cluster_endpoint = data.terraform_remote_state.clusters.outputs.stage_endpoint + + secret_type = "ssh" + sync_repo = var.acm_repo_location + sync_branch = var.acm_branch + policy_dir = var.stage_dir +} +module "acm-prod" { + source = "terraform-google-modules/kubernetes-engine/google//modules/acm" + version = "14.1.0" + + project_id = var.project_id + cluster_name = data.terraform_remote_state.clusters.outputs.prod_name + location = data.terraform_remote_state.clusters.outputs.prod_location + cluster_endpoint = data.terraform_remote_state.clusters.outputs.prod_endpoint + + secret_type = "ssh" + sync_repo = var.acm_repo_location + sync_branch = var.acm_branch + policy_dir = var.prod_dir +} \ No newline at end of file diff --git a/delivery-platform/resources/provision/management-tools/acm/backend.tmpl b/delivery-platform/resources/provision/management-tools/acm/backend.tmpl new file mode 100644 index 0000000..ddeb6c4 --- /dev/null +++ b/delivery-platform/resources/provision/management-tools/acm/backend.tmpl @@ -0,0 +1,22 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + backend "gcs" { + bucket = "YOUR_PROJECT_ID-delivery-platform-tf-state" + prefix = "acm" + } +} diff --git a/delivery-platform/resources/provision/management-tools/acm/cloudbuild-destroy.yaml b/delivery-platform/resources/provision/management-tools/acm/cloudbuild-destroy.yaml new file mode 100644 index 0000000..2dc1e31 --- /dev/null +++ b/delivery-platform/resources/provision/management-tools/acm/cloudbuild-destroy.yaml @@ -0,0 +1,32 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +timeout: 3600s # 1-hour +tags: + - delivery-platform + - delivery-platform-acm +steps: +- name: 'gcr.io/${PROJECT_ID}/delivery-platform-installer' + id: 'remove-acm' + entrypoint: 'bash' + args: + - '-xe' + - '-c' + - | + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" terraform.tmpl > terraform.tfvars + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" backend.tmpl > backend.tf + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" remote_state.tmpl > remote_state.tf + + terraform init + terraform destroy -auto-approve diff --git a/delivery-platform/resources/provision/management-tools/acm/cloudbuild.yaml b/delivery-platform/resources/provision/management-tools/acm/cloudbuild.yaml new file mode 100644 index 0000000..9cc1390 --- /dev/null +++ b/delivery-platform/resources/provision/management-tools/acm/cloudbuild.yaml @@ -0,0 +1,47 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +timeout: 3600s # 1-hr +tags: + - delivery-platform + - delivery-platform-acm +steps: +- name: 'gcr.io/${PROJECT_ID}/delivery-platform-installer' + id: 'apply-acm' + entrypoint: 'bash' + args: + - '-xe' + - '-c' + - | + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" backend.tmpl > backend.tf + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" remote_state.tmpl > remote_state.tf + sed "s/YOUR_PROJECT_ID/${PROJECT_ID}/g" terraform.tmpl > terraform.tfvars + sed -i "s|CONFIG_REPO|${_CLUSTER_CONFIG_REPO}|g" terraform.tfvars + sed -i "s|BRANCH|${_BRANCH}|g" terraform.tfvars + sed -i "s|DEV_PATH|${_DEV_PATH}|g" terraform.tfvars + sed -i "s|STAGE_PATH|${_STAGE_PATH}|g" terraform.tfvars + sed -i "s|PROD_PATH|${_PROD_PATH}|g" terraform.tfvars + + #export TF_LOG="ERROR" + + terraform init + terraform plan -var-file="terraform.tfvars" -out=terraform.tfplan + terraform apply -auto-approve terraform.tfplan + +substitutions: + _CLUSTER_CONFIG_REPO: https://github.com/GoogleCloudPlatform/csp-config-management/ + _BRANCH: 1.0.0 + _DEV_PATH: foo-corp + _STAGE_PATH: foo-corp + _PROD_PATH: foo-corp diff --git a/delivery-platform/resources/provision/management-tools/acm/outputs.tf b/delivery-platform/resources/provision/management-tools/acm/outputs.tf new file mode 100644 index 0000000..67be211 --- /dev/null +++ b/delivery-platform/resources/provision/management-tools/acm/outputs.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "test" { + value = "test" +} \ No newline at end of file diff --git a/delivery-platform/resources/provision/management-tools/acm/remote_state.tmpl b/delivery-platform/resources/provision/management-tools/acm/remote_state.tmpl new file mode 100644 index 0000000..7d3b841 --- /dev/null +++ b/delivery-platform/resources/provision/management-tools/acm/remote_state.tmpl @@ -0,0 +1,24 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +data "terraform_remote_state" "clusters" { + backend = "gcs" + + config = { + bucket = "YOUR_PROJECT_ID-delivery-platform-tf-state" + prefix = "clusters" + } +} diff --git a/delivery-platform/resources/provision/management-tools/acm/terraform.tmpl b/delivery-platform/resources/provision/management-tools/acm/terraform.tmpl new file mode 100644 index 0000000..d153723 --- /dev/null +++ b/delivery-platform/resources/provision/management-tools/acm/terraform.tmpl @@ -0,0 +1,23 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +project_id = "YOUR_PROJECT_ID" +acm_repo_location = "CONFIG_REPO" +acm_branch = "BRANCH" +acm_dir = "DEV_PATH" +dev_dir = "DEV_PATH" +stage_dir = "STAGE_PATH" +prod_dir = "PROD_PATH" \ No newline at end of file diff --git a/delivery-platform/resources/provision/management-tools/acm/variables.tf b/delivery-platform/resources/provision/management-tools/acm/variables.tf new file mode 100644 index 0000000..914440a --- /dev/null +++ b/delivery-platform/resources/provision/management-tools/acm/variables.tf @@ -0,0 +1,39 @@ +/** + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "project_id" { + description = "The project ID to host the cluster in" +} + +variable "acm_repo_location" { + description = "The location of the git repo ACM will sync to" +} +variable "acm_branch" { + description = "The git branch ACM will sync to" +} +variable "acm_dir" { + description = "The directory in git ACM will sync to" +} + +variable "dev_dir" { + description = "The directory in git Dev ACM will sync to" +} +variable "stage_dir" { + description = "The directory in git Stage ACM will sync to" +} +variable "prod_dir" { + description = "The directory in git Prod ACM will sync to" +} \ No newline at end of file diff --git a/delivery-platform/resources/provision/management-tools/argo-install.sh b/delivery-platform/resources/provision/management-tools/argo-install.sh new file mode 100755 index 0000000..c1476bc --- /dev/null +++ b/delivery-platform/resources/provision/management-tools/argo-install.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +ARGOCD_VERSION="v1.8.7" + +gcloud container clusters get-credentials dev --region us-west1-a --project $PROJECT_ID +kubectl create namespace argocd +kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/${ARGOCD_VERSION}/manifests/install.yaml +echo Waiting for Argo install... +kubectl wait --for=condition=ready pod -n argocd -l app.kubernetes.io/name=argocd-server --timeout=60s + +curl -sSL -o ${WORK_DIR}/bin/argocd https://github.com/argoproj/argo-cd/releases/download/${ARGOCD_VERSION}/argocd-linux-amd64 +chmod 755 ${WORK_DIR}/bin/argocd + +USER=admin +PASSWORD=$(kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f 2) +kubectl port-forward svc/argocd-server -n argocd 8080:443 & +PORT_FWD_PID=$! + +# Argo UI http://localhost:8080 +argocd login localhost:8080 --insecure --username=$USER --password=$PASSWORD + +argocd cluster add dev +argocd cluster add stage +argocd cluster add prod +kill $PORT_FWD_PID + +echo --- Note: some errors are seen in argo install output. +echo You can ignore the following +echo FATA[0000] dial tcp [::1]:8080: connect: connection refused +echo FATA[0001] rpc error: code = Unauthenticated desc = invalid session: token signature is invalid +echo +echo Argo install completed diff --git a/delivery-platform/resources/provision/management-tools/gitea/gt-setup.sh b/delivery-platform/resources/provision/management-tools/gitea/gt-setup.sh new file mode 100644 index 0000000..5085f01 --- /dev/null +++ b/delivery-platform/resources/provision/management-tools/gitea/gt-setup.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Gitea setup + +gcloud compute firewall-rules create "allow-http" --allow=tcp:3000 + --source-ranges="0.0.0.0/0" --description="Allow http" + +gcloud compute addresses create gitea-ip +export IP_ADDRESS=$(gcloud compute addresses describe gitea-ip --format="value(address)") + +export ADMIN_PASS=$(date +%s | sha256sum | base64 | head -c 8 ; echo) + +cat < vals.yaml +gitea: + config: + server: + DOMAIN: ${IP_ADDRESS}:3000 + admin: + username: gitea_admin + password: ${ADMIN_PASS} + email: "gitea@local.domain" +service: + http: + type: LoadBalancer + loadBalancerIP: ${IP_ADDRESS} +EOF + +helm repo add gitea-charts https://dl.gitea.io/charts/ +helm upgrade --install -f vals.yaml gitea gitea-charts/gitea + +echo "================================" +echo +echo User: gitea_admin +echo Password: ${ADMIN_PASS} +echo Site: http://${IP_ADDRESS}:3000 +echo +echo "================================" \ No newline at end of file diff --git a/delivery-platform/resources/provision/management-tools/tekton-install.sh b/delivery-platform/resources/provision/management-tools/tekton-install.sh new file mode 100755 index 0000000..36178a5 --- /dev/null +++ b/delivery-platform/resources/provision/management-tools/tekton-install.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +gcloud container clusters get-credentials dev --region us-west1-a --project $PROJECT_ID +kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml diff --git a/delivery-platform/resources/provision/provision-all.sh b/delivery-platform/resources/provision/provision-all.sh new file mode 100755 index 0000000..9f8197e --- /dev/null +++ b/delivery-platform/resources/provision/provision-all.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Prerequisites + + source ${BASE_DIR}/scripts/git/set-git-env.sh + + ## Prepare CloudBuild + + ### Enable APIS + # TODO trim this down... one of these is needed to create the compute service account + gcloud services enable \ + cloudresourcemanager.googleapis.com \ + container.googleapis.com \ + sourcerepo.googleapis.com \ + cloudbuild.googleapis.com \ + containerregistry.googleapis.com \ + anthosconfigmanagement.googleapis.com \ + run.googleapis.com \ + apikeys.googleapis.com \ + secretmanager.googleapis.com + + #Enable CLoud Deploy APIs and grant the service account required roles. Since it is not GA yet, put enabling APIs behind an IF condition + + if [ ${CONTINUOUS_DELIVERY_SYSTEM}="Clouddeploy" ]; then + gcloud services enable clouddeploy.googleapis.com cloudresourcemanager.googleapis.com + # TODO trim the following down + gcloud projects add-iam-policy-binding --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" --role roles/clouddeploy.admin ${PROJECT_ID} + gcloud projects add-iam-policy-binding --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" --role roles/container.developer ${PROJECT_ID} + gcloud projects add-iam-policy-binding --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" --role roles/iam.serviceAccountUser ${PROJECT_ID} + gcloud projects add-iam-policy-binding --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" --role roles/clouddeploy.jobRunner ${PROJECT_ID} + gcloud projects add-iam-policy-binding --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" --role roles/container.admin ${PROJECT_ID} + fi + + + ### Grant the Project Editor role to the Cloud Build service account in order to provision project resources + + gcloud projects add-iam-policy-binding $PROJECT_ID \ + --member=serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \ + --role=roles/owner + + ### Grant the IAM Service Account User role to the Cloud Build service account for the Cloud Run runtime service account: + + gcloud iam service-accounts add-iam-policy-binding \ + $PROJECT_NUMBER-compute@developer.gserviceaccount.com \ + --member=serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \ + --role=roles/iam.serviceAccountUser + + ### Grant the Storage Object Viewer role to the default Compute service account in order to read GCR.io images + + gcloud projects add-iam-policy-binding $PROJECT_ID \ + --member=serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \ + --role=roles/storage.objectViewer + + ### Create the base image used for provisioning + cd ${BASE_DIR}/resources/provision/base_image + gcloud builds submit --tag gcr.io/$PROJECT_ID/delivery-platform-installer + cd $BASE_DIR + + ### Create GIT secret + printf ${GIT_TOKEN} | gcloud secrets create gh_token --data-file=- + + +# Create Template Repos +cd ${BASE_DIR}/resources/provision/repos +./create-template-repos.sh +cd $BASE_DIR + +# Create Config Repos +cd ${BASE_DIR}/resources/provision/repos +./create-config-repo.sh +cd $BASE_DIR + +# Provision network and foundational elements +cd ${BASE_DIR}/resources/provision/foundation/tf +gcloud builds submit +cd $BASE_DIR + +# Provision the clusters +cd ${BASE_DIR}/resources/provision/clusters/tf +gcloud builds submit +cd $BASE_DIR + +# Rename contexts +gcloud container clusters get-credentials dev --region us-west1-a --project $PROJECT_ID +kubectl config delete-context dev +kubectl config rename-context gke_${PROJECT_ID}_us-west1-a_dev dev + +gcloud container clusters get-credentials stage --region us-west2-a --project $PROJECT_ID +kubectl config delete-context stage +kubectl config rename-context gke_${PROJECT_ID}_us-west2-a_stage stage + +gcloud container clusters get-credentials prod --region us-central1-a --project $PROJECT_ID +kubectl config delete-context prod +kubectl config rename-context gke_${PROJECT_ID}_us-central1-a_prod prod + + +# Install ACM +cd ${BASE_DIR}/resources/provision/management-tools/acm +./acm-install.sh +cd $BASE_DIR + diff --git a/delivery-platform/resources/provision/repos/create-config-repo.sh b/delivery-platform/resources/provision/repos/create-config-repo.sh new file mode 100755 index 0000000..b3c9d8e --- /dev/null +++ b/delivery-platform/resources/provision/repos/create-config-repo.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +## Create sample repos +echo $GIT_BASE_URL + +# Create config repo +cp -R $BASE_DIR/resources/repos/cluster-config $WORK_DIR +cd $WORK_DIR/cluster-config +git init && git symbolic-ref HEAD refs/heads/main && git add . && git commit -m "initial commit" +$BASE_DIR/scripts/git/${GIT_CMD} create $CLUSTER_CONFIG_REPO +git remote add origin $GIT_BASE_URL/$CLUSTER_CONFIG_REPO +git push origin main +cd $BASE_DIR +rm -rf $WORK_DIR/cluster-config diff --git a/delivery-platform/resources/provision/repos/create-template-repos.sh b/delivery-platform/resources/provision/repos/create-template-repos.sh new file mode 100755 index 0000000..5400d11 --- /dev/null +++ b/delivery-platform/resources/provision/repos/create-template-repos.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +## Create sample repos +echo $GIT_BASE_URL + +# Create templates repo +cp -R $BASE_DIR/resources/repos/app-templates $WORK_DIR +cd $WORK_DIR/app-templates +git init && git symbolic-ref HEAD refs/heads/main && git add . && git commit -m "initial commit" +$BASE_DIR/scripts/git/${GIT_CMD} create $APP_TEMPLATES_REPO +sleep 5 +git remote add origin $GIT_BASE_URL/$APP_TEMPLATES_REPO +git push origin main +# Auth fails intermittetly on the very first client call for some reason + # Adding a retry to ensure the source is pushed. +git push origin main +cd $BASE_DIR +rm -rf $WORK_DIR/app-templates + + +# Create shared kustomize repo +cp -R $BASE_DIR/resources/repos/shared-kustomize $WORK_DIR +cd $WORK_DIR/shared-kustomize +git init && git symbolic-ref HEAD refs/heads/main && git add . && git commit -m "initial commit" +$BASE_DIR/scripts/git/${GIT_CMD} create $SHARED_KUSTOMIZE_REPO +git remote add origin $GIT_BASE_URL/$SHARED_KUSTOMIZE_REPO +git push origin main +cd $BASE_DIR +rm -rf $WORK_DIR/shared-kustomize + diff --git a/delivery-platform/resources/provision/repos/teardown.sh b/delivery-platform/resources/provision/repos/teardown.sh new file mode 100755 index 0000000..fa0aa22 --- /dev/null +++ b/delivery-platform/resources/provision/repos/teardown.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +$BASE_DIR/scripts/git/${GIT_CMD} delete $APP_TEMPLATES_REPO +$BASE_DIR/scripts/git/${GIT_CMD} delete $SHARED_KUSTOMIZE_REPO +$BASE_DIR/scripts/git/${GIT_CMD} delete $CLUSTER_CONFIG_REPO \ No newline at end of file diff --git a/delivery-platform/resources/provision/teardown-all.sh b/delivery-platform/resources/provision/teardown-all.sh new file mode 100755 index 0000000..28835b6 --- /dev/null +++ b/delivery-platform/resources/provision/teardown-all.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cd ${BASE_DIR}/resources/provision/clusters/tf +gcloud builds submit --config cloudbuild-destroy.yaml +cd ${BASE_DIR} + +cd ${BASE_DIR}/resources/provision/foundation/tf +gcloud builds submit --config cloudbuild-destroy.yaml +cd ${BASE_DIR} + +# Base Repos +cd ${BASE_DIR}/resources/provision/repos +./teardown.sh +cd ${BASE_DIR}/ + +# Delete git secret +gcloud secrets delete gh_token + +# Delete contexts +kubectl config delete-context dev +kubectl config delete-context stage +kubectl config delete-context prod + +# remove tfstate to avoid conflict with reprovision +gsutil rm gs://${PROJECT_ID}-delivery-platform-tf-state/**.tfstate diff --git a/delivery-platform/resources/repos/README.md b/delivery-platform/resources/repos/README.md new file mode 100644 index 0000000..85cda88 --- /dev/null +++ b/delivery-platform/resources/repos/README.md @@ -0,0 +1,7 @@ +# Sample Repos + +The sample repos provided in this directory are push to your git provider as part of the provisioning process. From then on the workshop and platform will utilize the repositories located in your git provider. + +This directory contains a simple `cluster-config` repo designed for use with Anthos Config Manager. + +Additional repositories are provided for application templates and the associated kustomize folders. To add aditional app templates, simply create the appropriate structures in the `app-templates` and `shared-kustomize` tempalates forlder prior to provisioning, or add them directly to your git provider's repo anytime after provisioning. \ No newline at end of file diff --git a/delivery-platform/resources/repos/app-templates/.gitignore b/delivery-platform/resources/repos/app-templates/.gitignore new file mode 100644 index 0000000..496ee2c --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/delivery-platform/resources/repos/app-templates/golang/Dockerfile b/delivery-platform/resources/repos/app-templates/golang/Dockerfile new file mode 100644 index 0000000..782ca9e --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/Dockerfile @@ -0,0 +1,19 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM golang:1 +WORKDIR /app +COPY * ./ +RUN go build -o golang-template +CMD ["/app/golang-template"] diff --git a/delivery-platform/resources/repos/app-templates/golang/README.md b/delivery-platform/resources/repos/app-templates/golang/README.md new file mode 100644 index 0000000..5043735 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/README.md @@ -0,0 +1 @@ +# app-template diff --git a/delivery-platform/resources/repos/app-templates/golang/build/cloudbuild-build-only.yaml b/delivery-platform/resources/repos/app-templates/golang/build/cloudbuild-build-only.yaml new file mode 100644 index 0000000..e8d1262 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/build/cloudbuild-build-only.yaml @@ -0,0 +1,51 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +steps: + + # Clone the repos + - id: clone-app + name: gcr.io/cloud-builders/git + entrypoint: bash + args: + - '-c' + - | + IFS='/' read -a array <<< "${_REF}" + git clone -b ${array[2]} ${_APP_REPO} app-repo + + - id: clone-kustomize + name: gcr.io/cloud-builders/git + entrypoint: bash + # changed from GIT_URL to APP_REPO + args: + - '-c' + - | + git clone ${_KUSTOMIZE_REPO} kustomize-base + sleep 5 + + # Build and push the image + - id: skaffold-build + name: gcr.io/k8s-skaffold/skaffold + entrypoint: bash + args: + - '-c' + - | + cd app-repo + skaffold build --file-output=/workspace/artifacts.json \ + --default-repo ${_DEFAULT_IMAGE_REPO} \ + --push=true + + + + diff --git a/delivery-platform/resources/repos/app-templates/golang/build/cloudbuild-cd.yaml b/delivery-platform/resources/repos/app-templates/golang/build/cloudbuild-cd.yaml new file mode 100644 index 0000000..77a3b4c --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/build/cloudbuild-cd.yaml @@ -0,0 +1,79 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +steps: + + # Clone the repos + - id: clone-app + name: gcr.io/cloud-builders/git + entrypoint: bash + args: + - '-c' + - | + IFS='/' read -a array <<< "${_REF}" + git clone -b ${array[2]} ${_APP_REPO} app-repo + + - id: clone-kustomize + name: gcr.io/cloud-builders/git + entrypoint: bash + # changed from GIT_URL to APP_REPO + args: + - '-c' + - | + git clone ${_KUSTOMIZE_REPO} kustomize-base + sleep 5 + + - id: setup-for-clouddeploy + name: gcr.io/cloud-builders/git + entrypoint: bash + args: + - '-c' + - | + cd app-repo + find . -name kustomization.yaml -exec sed -i "s?- ../../../kustomize-base?- ../../kustomize-base?g" {} \; + cp -r ../kustomize-base . + + # Build and push the image + - id: skaffold-build + name: gcr.io/k8s-skaffold/skaffold + entrypoint: bash + args: + - '-c' + - | + cd app-repo + skaffold build --file-output=/workspace/artifacts.json \ + --default-repo ${_DEFAULT_IMAGE_REPO} \ + --push=true + + - name: 'google/cloud-sdk:latest' + entrypoint: 'sh' + args: + - -xe + - -c + - | + gcloud config set deploy/region us-central1 + cd app-repo + SHORT_SHA=$(git rev-parse --short HEAD) + sed -i s/PROJECT_ID/$PROJECT_ID/g deploy/* + gcloud beta deploy apply --file deploy/pipeline.yaml + gcloud beta deploy apply --file deploy/stage.yaml + gcloud beta deploy apply --file deploy/prod.yaml + gcloud beta deploy releases create rel-${SHORT_SHA}-$(date +%s) \ + --delivery-pipeline ${_APP_NAME} \ + --description "$(git log -1 --pretty='%s')" \ + --build-artifacts /workspace/artifacts.json \ + --annotations="commit_ui=${_APP_REPO}/+/$COMMIT_SHA" + + + diff --git a/delivery-platform/resources/repos/app-templates/golang/build/cloudbuild.yaml b/delivery-platform/resources/repos/app-templates/golang/build/cloudbuild.yaml new file mode 100644 index 0000000..ed34b31 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/build/cloudbuild.yaml @@ -0,0 +1,135 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +steps: + # Create the hydrated directories + - id: create-dirs + name: bash + entrypoint: bash + args: + - '-c' + - | + mkdir -p app-hydrated/dev + mkdir -p app-hydrated/stage + mkdir -p app-hydrated/prod + + + # Clone the repos + - id: clone-app + name: gcr.io/cloud-builders/git + entrypoint: bash + args: + - '-c' + - | + IFS='/' read -a array <<< "${_REF}" + git clone -b ${array[2]} ${_APP_REPO} app-repo + + - id: clone-kustomize + name: gcr.io/cloud-builders/git + entrypoint: bash + # changed from GIT_URL to APP_REPO + args: + - '-c' + - | + git clone ${_KUSTOMIZE_REPO} kustomize-base + + - id: clone-config + name: gcr.io/cloud-builders/git + entrypoint: bash + + args: + - '-c' + - | + git clone ${_CONFIG_REPO} config-repo + + # Build and push the image + - id: skaffold-build + name: gcr.io/k8s-skaffold/skaffold + entrypoint: bash + args: + - '-c' + - | + cd app-repo + skaffold build --default-repo ${_DEFAULT_IMAGE_REPO} + + # Render the manifests + - id: skaffold-render + name: gcr.io/k8s-skaffold/skaffold + entrypoint: bash + args: + - '-c' + - | + cd app-repo + skaffold render \ + --default-repo ${_DEFAULT_IMAGE_REPO} \ + --output ../app-hydrated/stage/resources.yaml + + # Deliver the app + - id: deliver + name: gcr.io/cloud-builders/git + secretEnv: ['GH_TOKEN'] + entrypoint: bash + args: + - '-c' + - | + + # Setup auth for commit + echo 'exec echo "$$GH_TOKEN"' > ~/git-ask-pass.sh + chmod +x ~/git-ask-pass.sh + export GIT_ASKPASS=~/git-ask-pass.sh + git config --global user.email "CloudBuild@yourcompany.com" + git config --global user.name "Cloud Build" + + # Copy the hydrated file to the config repo & push + cd config-repo + git pull + + # Main, Tag or Branch + IFS='/' read -a array <<< "${_REF}" + + # Tag + if [[ ${array[1]} = 'tags' ]] ; then + cp ../app-hydrated/stage/resources.yaml \ + ./prod/namespaces/${_APP_NAME} + + # Main + elif [[ ${array[2]} = 'main' ]] ; then + cp ../app-hydrated/stage/resources.yaml \ + ./stage/namespaces/${_APP_NAME} + + # Feature Branch + else + echo "NOT_IMPLEMENTED: Not deploying feature branches" + fi + + git add . && git commit -m "Deploying new image" + git push origin main + + # Cleanup the workspace + - id: cleanup + name: bash + entrypoint: bash + args: + - '-c' + - | + rm -rf app-hydrated + rm -rf app-hydrated + rm -rf kustomize-base + + +availableSecrets: + secretManager: + - versionName: projects/$PROJECT_ID/secrets/gh_token/versions/latest + env: GH_TOKEN + diff --git a/delivery-platform/resources/repos/app-templates/golang/deploy/pipeline.yaml.tmpl b/delivery-platform/resources/repos/app-templates/golang/deploy/pipeline.yaml.tmpl new file mode 100644 index 0000000..a93d8a6 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/deploy/pipeline.yaml.tmpl @@ -0,0 +1,29 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: deploy.cloud.google.com/v1beta1 +kind: DeliveryPipeline +metadata: + name: ${APP_NAME} + labels: + app: ${APP_NAME} +description: delivery pipeline +serialPipeline: + stages: + - targetId: stage + profiles: + - stage + - targetId: prod + profiles: + - prod \ No newline at end of file diff --git a/delivery-platform/resources/repos/app-templates/golang/deploy/prod.yaml b/delivery-platform/resources/repos/app-templates/golang/deploy/prod.yaml new file mode 100644 index 0000000..e8f9d1a --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/deploy/prod.yaml @@ -0,0 +1,24 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: deploy.cloud.google.com/v1beta1 +kind: Target +metadata: + name: prod + annotations: {} + labels: {} +description: prod +requireApproval: false +gke: + cluster: projects/PROJECT_ID/locations/us-central1-a/clusters/prod diff --git a/delivery-platform/resources/repos/app-templates/golang/deploy/stage.yaml b/delivery-platform/resources/repos/app-templates/golang/deploy/stage.yaml new file mode 100644 index 0000000..6995e53 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/deploy/stage.yaml @@ -0,0 +1,23 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: deploy.cloud.google.com/v1beta1 +kind: Target +metadata: + name: stage + annotations: {} + labels: {} +description: stage +gke: + cluster: projects/PROJECT_ID/locations/us-west2-a/clusters/stage \ No newline at end of file diff --git a/delivery-platform/resources/repos/app-templates/golang/go.mod b/delivery-platform/resources/repos/app-templates/golang/go.mod new file mode 100644 index 0000000..343c10a --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/go.mod @@ -0,0 +1,3 @@ +module example.com/golang + +go 1.16 diff --git a/delivery-platform/resources/repos/app-templates/golang/k8s/dev/deployment.yaml b/delivery-platform/resources/repos/app-templates/golang/k8s/dev/deployment.yaml new file mode 100644 index 0000000..742fd06 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/k8s/dev/deployment.yaml @@ -0,0 +1,34 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: app # Must match base value + image: app # Overwrites values from base - Needs to match skaffold artifact + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + cpu: 100m + memory: 256Mi + env: + - name: ENVIRONMENT + value: dev diff --git a/delivery-platform/resources/repos/app-templates/golang/k8s/dev/deployment.yaml.tmpl b/delivery-platform/resources/repos/app-templates/golang/k8s/dev/deployment.yaml.tmpl new file mode 100644 index 0000000..d139a24 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/k8s/dev/deployment.yaml.tmpl @@ -0,0 +1,34 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: app # Must match base value + image: ${APP_NAME} # Overwrites values from base - Needs to match skaffold artifact + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + cpu: 100m + memory: 256Mi + env: + - name: ENVIRONMENT + value: dev diff --git a/delivery-platform/resources/repos/app-templates/golang/k8s/dev/kustomization.yaml.tmpl b/delivery-platform/resources/repos/app-templates/golang/k8s/dev/kustomization.yaml.tmpl new file mode 100644 index 0000000..9951235 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/k8s/dev/kustomization.yaml.tmpl @@ -0,0 +1,22 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bases: +- ../../../kustomize-base/golang +patches: +- deployment.yaml +namePrefix: "${APP_NAME}-" # App name (dash) +commonLabels: + app: ${APP_NAME} # App name for selectors + role: backend diff --git a/delivery-platform/resources/repos/app-templates/golang/k8s/prod/deployment.yaml b/delivery-platform/resources/repos/app-templates/golang/k8s/prod/deployment.yaml new file mode 100644 index 0000000..f32c239 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/k8s/prod/deployment.yaml @@ -0,0 +1,34 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: app + image: app + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + cpu: 100m + memory: 256Mi + env: + - name: ENVIRONMENT + value: prod diff --git a/delivery-platform/resources/repos/app-templates/golang/k8s/prod/deployment.yaml.tmpl b/delivery-platform/resources/repos/app-templates/golang/k8s/prod/deployment.yaml.tmpl new file mode 100644 index 0000000..f554824 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/k8s/prod/deployment.yaml.tmpl @@ -0,0 +1,34 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: app + image: ${APP_NAME} + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + cpu: 100m + memory: 256Mi + env: + - name: ENVIRONMENT + value: prod diff --git a/delivery-platform/resources/repos/app-templates/golang/k8s/prod/kustomization.yaml.tmpl b/delivery-platform/resources/repos/app-templates/golang/k8s/prod/kustomization.yaml.tmpl new file mode 100644 index 0000000..a49b3ab --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/k8s/prod/kustomization.yaml.tmpl @@ -0,0 +1,22 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bases: +- ../../../kustomize-base/golang +patches: +- deployment.yaml +namePrefix: "${APP_NAME}-" +commonLabels: + app: ${APP_NAME} + role: backend diff --git a/delivery-platform/resources/repos/app-templates/golang/k8s/stage/deployment.yaml b/delivery-platform/resources/repos/app-templates/golang/k8s/stage/deployment.yaml new file mode 100644 index 0000000..b449115 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/k8s/stage/deployment.yaml @@ -0,0 +1,35 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + replicas: 1 + template: + spec: + containers: + - name: app + image: app + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + cpu: 100m + memory: 256Mi + env: + - name: ENVIRONMENT + value: stg diff --git a/delivery-platform/resources/repos/app-templates/golang/k8s/stage/deployment.yaml.tmpl b/delivery-platform/resources/repos/app-templates/golang/k8s/stage/deployment.yaml.tmpl new file mode 100644 index 0000000..aab1747 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/k8s/stage/deployment.yaml.tmpl @@ -0,0 +1,35 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + replicas: 1 + template: + spec: + containers: + - name: app + image: ${APP_NAME} + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + cpu: 100m + memory: 256Mi + env: + - name: ENVIRONMENT + value: stg diff --git a/delivery-platform/resources/repos/app-templates/golang/k8s/stage/kustomization.yaml.tmpl b/delivery-platform/resources/repos/app-templates/golang/k8s/stage/kustomization.yaml.tmpl new file mode 100644 index 0000000..cea5b6a --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/k8s/stage/kustomization.yaml.tmpl @@ -0,0 +1,22 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bases: +- ../../../kustomize-base/golang +patches: +- deployment.yaml +commonLabels: + app: ${APP_NAME} + role: backend +namePrefix: ${APP_NAME}- diff --git a/delivery-platform/resources/repos/app-templates/golang/main.go b/delivery-platform/resources/repos/app-templates/golang/main.go new file mode 100644 index 0000000..4b896c1 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/main.go @@ -0,0 +1,40 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "log" + "net/http" + "os" +) + +func main() { + env := os.Getenv("ENVIRONMENT") + port := 8080 + log.Printf("Running in environment: %s\n", env) + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + log.Printf("Received request from %s at %s", r.RemoteAddr, r.URL.EscapedPath()) + fmt.Fprint(w, "Hello World!") + }) + http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + log.Printf("Received health check from %s", r.RemoteAddr) + w.WriteHeader(http.StatusOK) + }) + log.Printf("Starting server on port: %v", port) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) + +} diff --git a/delivery-platform/resources/repos/app-templates/golang/skaffold.yaml.tmpl b/delivery-platform/resources/repos/app-templates/golang/skaffold.yaml.tmpl new file mode 100644 index 0000000..4cf7d94 --- /dev/null +++ b/delivery-platform/resources/repos/app-templates/golang/skaffold.yaml.tmpl @@ -0,0 +1,41 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: skaffold/v1beta14 +kind: Config +build: + artifacts: + - image: ${APP_NAME} # Match name in deployment yaml + context: ./ +deploy: + kustomize: + path: k8s/dev + +profiles: +- name: dev + activation: + - command: dev + deploy: + kustomize: + path: k8s/dev + +- name: stage + deploy: + kustomize: + path: k8s/stage + +- name: prod + deploy: + kustomize: + path: k8s/prod diff --git a/delivery-platform/resources/repos/cluster-config/.gitignore b/delivery-platform/resources/repos/cluster-config/.gitignore new file mode 100644 index 0000000..496ee2c --- /dev/null +++ b/delivery-platform/resources/repos/cluster-config/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/delivery-platform/resources/repos/cluster-config/dev/system/repo.yaml b/delivery-platform/resources/repos/cluster-config/dev/system/repo.yaml new file mode 100644 index 0000000..ff73b49 --- /dev/null +++ b/delivery-platform/resources/repos/cluster-config/dev/system/repo.yaml @@ -0,0 +1,20 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: configmanagement.gke.io/v1 +kind: Repo +metadata: + name: repo +spec: + version: 1.0.0 \ No newline at end of file diff --git a/delivery-platform/resources/repos/cluster-config/prod/system/repo.yaml b/delivery-platform/resources/repos/cluster-config/prod/system/repo.yaml new file mode 100644 index 0000000..ff73b49 --- /dev/null +++ b/delivery-platform/resources/repos/cluster-config/prod/system/repo.yaml @@ -0,0 +1,20 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: configmanagement.gke.io/v1 +kind: Repo +metadata: + name: repo +spec: + version: 1.0.0 \ No newline at end of file diff --git a/delivery-platform/resources/repos/cluster-config/stage/system/repo.yaml b/delivery-platform/resources/repos/cluster-config/stage/system/repo.yaml new file mode 100644 index 0000000..ff73b49 --- /dev/null +++ b/delivery-platform/resources/repos/cluster-config/stage/system/repo.yaml @@ -0,0 +1,20 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: configmanagement.gke.io/v1 +kind: Repo +metadata: + name: repo +spec: + version: 1.0.0 \ No newline at end of file diff --git a/delivery-platform/resources/repos/shared-kustomize/.gitignore b/delivery-platform/resources/repos/shared-kustomize/.gitignore new file mode 100644 index 0000000..496ee2c --- /dev/null +++ b/delivery-platform/resources/repos/shared-kustomize/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/kubernetes/deployments/dev/backend-dev.yaml b/delivery-platform/resources/repos/shared-kustomize/golang/deployment.yaml similarity index 58% rename from kubernetes/deployments/dev/backend-dev.yaml rename to delivery-platform/resources/repos/shared-kustomize/golang/deployment.yaml index c028866..080de4c 100644 --- a/kubernetes/deployments/dev/backend-dev.yaml +++ b/delivery-platform/resources/repos/shared-kustomize/golang/deployment.yaml @@ -1,10 +1,10 @@ -# Copyright 2015 Google Inc. All rights reserved. +# Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,35 +15,34 @@ kind: Deployment apiVersion: apps/v1 metadata: - name: gceme-backend-dev + name: app spec: - selector: - matchLabels: - app: gceme - role: backend - env: dev - replicas: 1 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 template: metadata: - name: backend - labels: - app: gceme - role: backend - env: dev + name: app spec: containers: - - name: backend - image: gcr.io/cloud-solutions-images/gceme:1.0.0 + - name: app + image: app resources: limits: - memory: "500Mi" - cpu: "100m" - imagePullPolicy: Always + memory: "512Mi" + cpu: "500m" + env: + - name: ENVIRONMENT + value: base + - name: LOG_LEVEL + value: info readinessProbe: + initialDelaySeconds: 1 + periodSeconds: 1 httpGet: path: /healthz port: 8080 - command: ["sh", "-c", "app -port=8080"] ports: - - name: backend + - name: http containerPort: 8080 diff --git a/delivery-platform/resources/repos/shared-kustomize/golang/kustomization.yaml b/delivery-platform/resources/repos/shared-kustomize/golang/kustomization.yaml new file mode 100644 index 0000000..8c06dd4 --- /dev/null +++ b/delivery-platform/resources/repos/shared-kustomize/golang/kustomization.yaml @@ -0,0 +1,17 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +resources: + - service.yaml + - deployment.yaml diff --git a/kubernetes/services/backend.yaml b/delivery-platform/resources/repos/shared-kustomize/golang/service.yaml similarity index 85% rename from kubernetes/services/backend.yaml rename to delivery-platform/resources/repos/shared-kustomize/golang/service.yaml index 9fb093c..5807847 100644 --- a/kubernetes/services/backend.yaml +++ b/delivery-platform/resources/repos/shared-kustomize/golang/service.yaml @@ -1,4 +1,4 @@ -# Copyright 2015 Google Inc. All rights reserved. +# Copyright 2021 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,13 +15,11 @@ kind: Service apiVersion: v1 metadata: - name: gceme-backend + name: app spec: + type: ClusterIP ports: - name: http port: 8080 targetPort: 8080 protocol: TCP - selector: - role: backend - app: gceme diff --git a/delivery-platform/scripts/app.sh b/delivery-platform/scripts/app.sh new file mode 100755 index 0000000..44e3e65 --- /dev/null +++ b/delivery-platform/scripts/app.sh @@ -0,0 +1,308 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +### +# +# Used for app oboarding and termination. +# App creation clones the templates repo then copies the +# desierd template folder to a temporary workspace. The script +# then substitutes place holder values for actual values creates a +# new remote remote and pushes the initial version. +# Additonally if ACM is in use this script adds appropriate namespace +# configurations to ensure the app is managed by ACM. +# +# USAGE: +# app.sh +# +### + + +create () { + APP_NAME=${1:-"my-app"} + APP_LANG=${2:-"golang"} + BASE_PATH=${3:-""} + + # Ensure the git vendor script is set + if [[ -z "$GIT_CMD" ]]; then + echo "GIT_CMD not set - exiting" 1>&2 + exit 1 + fi + + printf 'Creating application: %s \n' $APP_NAME + + # Create an instance of the template. + cd $WORK_DIR/ + git clone -b main $GIT_BASE_URL/$APP_TEMPLATES_REPO app-templates + rm -rf app-templates/.git + cd app-templates/${APP_LANG} + + ## Insert name of new app + for template in $(find . -name '*.tmpl'); do envsubst < ${template} > ${template%.*}; done + + + ## Create and push to new repo + git init + git checkout -b main + git symbolic-ref HEAD refs/heads/main + $BASE_DIR/scripts/git/${GIT_CMD} create ${APP_NAME} + git remote add origin $GIT_BASE_URL/${APP_NAME} + git add . && git commit -m "initial commit" + git push origin main + # Auth fails intermittetly on the very first client call for some reason + # Adding a retry to ensure the source is pushed. + git push origin main + + + # Configure Build based on CD system. + if [[ ${CONTINUOUS_DELIVERY_SYSTEM} == "ACM" ]]; then + echo "calling regular webhook" + create_cloudbuild_trigger ${APP_NAME} + elif [[ ${CONTINUOUS_DELIVERY_SYSTEM} == "Clouddeploy" ]]; then + echo "calling CD webhook" + create_cloudbuild_trigger_for_clouddeploy ${APP_NAME} + fi + + # Configure Deployment + + ## Add App Namespace if using config manager + if [[ ${CONTINUOUS_DELIVERY_SYSTEM} == "ACM" ]]; then + for dir in k8s/* ; do + #if [ -d "$dir" ]; then + echo ---adding ${dir##*/} + addAcmEntry ${dir##*/} + #fi + done + fi + + # Initial deploy + cd $WORK_DIR/app-templates/${APP_LANG} + git pull + echo "v1" > version.txt + git add . && git commit -m "v1" + git push origin main + sleep 10 + git pull + git tag v1 + git push origin v1 + + # Cleanup + cd $BASE_DIR + rm -rf $WORK_DIR/app-templates +} + + + +delete () { + echo 'Destroy Application' + APP_NAME=${1:-"my-app"} + BASE_PATH=${2:-""} + $BASE_DIR/scripts/git/${GIT_CMD} delete $APP_NAME + + # Remove any orphaned hydrated directories from other processes + rm -rf $WORK_DIR/$APP_NAME-hydrated + + if [[ ${CONTINUOUS_DELIVERY_SYSTEM} == "ACM" ]]; then + cd $WORK_DIR/ + git clone -b main $GIT_BASE_URL/$CLUSTER_CONFIG_REPO acm-repo + cd acm-repo + for dir in * ; do + #if [ -d "$dir" ]; then + echo ---deleting ${dir##*/} + + echo "Do ACM Stuff" + + rm -rf ${dir##*/}/namespaces/${APP_NAME} + + #fi + done + git add . && git commit -m "Removing app: ${APP_NAME}" && git push origin main + cd $BASE_DIR + rm -rf $WORK_DIR/acm-repo + elif [[ ${CONTINUOUS_DELIVERY_SYSTEM} == "Clouddeploy" ]]; then + #Delete the deployments for dev, staging and prod. The deployments with CD are created with default namespace + kubectx dev && kubectl delete deploy $(kubectl get deploy --namespace default --selector="app=${APP_NAME}" --output jsonpath='{.items[0].metadata.name}') || true + kubectx stage && kubectl delete deploy $(kubectl get deploy --namespace default --selector="app=${APP_NAME}" --output jsonpath='{.items[0].metadata.name}') || true + kubectx prod && kubectl delete deploy $(kubectl get deploy --namespace default --selector="app=${APP_NAME}" --output jsonpath='{.items[0].metadata.name}') || true + + #Also delete CD pipelines. Pipelines are in us-central1 + gcloud alpha deploy delivery-pipelines delete ${APP_NAME} --region="us-central1" --force -q || true + fi + + # Delete secret + if [[ ${CONTINUOUS_DELIVERY_SYSTEM} == "ACM" ]]; then + SECRET_NAME=${APP_NAME}-webhook-trigger-secret + gcloud secrets delete ${SECRET_NAME} -q + elif [[ ${CONTINUOUS_DELIVERY_SYSTEM} == "Clouddeploy" ]]; then + SECRET_NAME=${APP_NAME}-webhook-trigger-cd-secret + gcloud secrets delete ${SECRET_NAME} -q + fi + + # Delete trigger + if [[ ${CONTINUOUS_DELIVERY_SYSTEM} == "ACM" ]]; then + TRIGGER_NAME=${APP_NAME}-webhook-trigger + gcloud alpha builds triggers delete ${TRIGGER_NAME} -q + elif [[ ${CONTINUOUS_DELIVERY_SYSTEM} == "Clouddeploy" ]]; then + TRIGGER_NAME=${APP_NAME}-clouddeploy-webhook-trigger + gcloud alpha builds triggers delete ${TRIGGER_NAME} -q + fi + +} + + +addAcmEntry(){ + + ENV=${1:-"dev"} + + + #for env in ${APP_NAME}/k8s + echo "Do ACM Stuff" + + cd $WORK_DIR/ + git clone -b main $GIT_BASE_URL/$CLUSTER_CONFIG_REPO acm-repo + cd acm-repo + mkdir -p ${ENV}/namespaces/${APP_NAME} + cat < ${ENV}/namespaces/${APP_NAME}/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: ${APP_NAME} + labels: + istio-injection: enabled + +EOF + git add . && git commit -m "Adding app: ${APP_NAME}" && git push origin main + cd $BASE_DIR + rm -rf $WORK_DIR/acm-repo +} + + + +create_cloudbuild_trigger () { + APP_NAME=${1:-"my-app"} + ## Project variables + if [[ ${PROJECT_ID} == "" ]]; then + echo "PROJECT_ID env variable is not set" + exit -1 + fi + if [[ ${PROJECT_NUMBER} == "" ]]; then + echo "PROJECT_NUMBER env variable is not set" + exit -1 + fi + + ## API Key + if [[ ${APP_LANG} == "" ]]; then + echo "APP_LANG env variable is not set" + exit -1 + fi + + ## API Key + if [[ ${API_KEY_VALUE} == "" ]]; then + echo "API_KEY_VALUE env variable is not set" + exit -1 + fi + + + ## Create Secret + SECRET_NAME=${APP_NAME}-webhook-trigger-secret + SECRET_VALUE=$(sed "s/[^a-zA-Z0-9]//g" <<< $(openssl rand -base64 15)) + SECRET_PATH=projects/${PROJECT_NUMBER}/secrets/${SECRET_NAME}/versions/1 + printf ${SECRET_VALUE} | gcloud secrets create ${SECRET_NAME} --data-file=- + gcloud secrets add-iam-policy-binding ${SECRET_NAME} \ + --member=serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-cloudbuild.iam.gserviceaccount.com \ + --role='roles/secretmanager.secretAccessor' + + ## Create CloudBuild Webhook Endpoint + REPO_LOCATION=https://github.com/${GIT_USERNAME}/${APP_NAME} + + TRIGGER_NAME=${APP_NAME}-webhook-trigger + BUILD_YAML_PATH=$WORK_DIR/app-templates/${APP_LANG}/build/cloudbuild.yaml + + ## Setup Trigger & Webhook + gcloud alpha builds triggers create webhook \ + --name=${TRIGGER_NAME} \ + --substitutions='_APP_NAME='${APP_NAME}',_APP_REPO=$(body.repository.git_url),_CONFIG_REPO='${GIT_BASE_URL}'/'${CLUSTER_CONFIG_REPO}',_DEFAULT_IMAGE_REPO='${IMAGE_REPO}',_KUSTOMIZE_REPO='${GIT_BASE_URL}'/'${SHARED_KUSTOMIZE_REPO}',_REF=$(body.ref)' \ + --inline-config=$BUILD_YAML_PATH \ + --secret=${SECRET_PATH} + + + + ## Retrieve the URL + WEBHOOK_URL="https://cloudbuild.googleapis.com/v1/projects/${PROJECT_ID}/triggers/${TRIGGER_NAME}:webhook?key=${API_KEY_VALUE}&secret=${SECRET_VALUE}" + + ## Create Github Webhook + $BASE_DIR/scripts/git/${GIT_CMD} create_webhook ${APP_NAME} $WEBHOOK_URL + +} + + +create_cloudbuild_trigger_for_clouddeploy () { + APP_NAME=${1:-"my-app"} + ## Project variables + if [[ ${PROJECT_ID} == "" ]]; then + echo "PROJECT_ID env variable is not set" + exit -1 + fi + if [[ ${PROJECT_NUMBER} == "" ]]; then + echo "PROJECT_NUMBER env variable is not set" + exit -1 + fi + + ## API Key + if [[ ${APP_LANG} == "" ]]; then + echo "APP_LANG env variable is not set" + exit -1 + fi + + ## API Key + if [[ ${API_KEY_VALUE} == "" ]]; then + echo "API_KEY_VALUE env variable is not set" + exit -1 + fi + + + ## Create Secret + SECRET_NAME=${APP_NAME}-webhook-trigger-cd-secret + SECRET_VALUE=$(sed "s/[^a-zA-Z0-9]//g" <<< $(openssl rand -base64 15)) + SECRET_PATH=projects/${PROJECT_NUMBER}/secrets/${SECRET_NAME}/versions/1 + printf ${SECRET_VALUE} | gcloud secrets create ${SECRET_NAME} --data-file=- + gcloud secrets add-iam-policy-binding ${SECRET_NAME} \ + --member=serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-cloudbuild.iam.gserviceaccount.com \ + --role='roles/secretmanager.secretAccessor' + + ## Create CloudBuild Webhook Endpoint + REPO_LOCATION=https://github.com/${GIT_USERNAME}/${APP_NAME} + + TRIGGER_NAME=${APP_NAME}-clouddeploy-webhook-trigger + BUILD_YAML_PATH=$WORK_DIR/app-templates/${APP_LANG}/build/cloudbuild-cd.yaml + + ## Setup Trigger & Webhook + gcloud alpha builds triggers create webhook \ + --name=${TRIGGER_NAME} \ + --substitutions='_APP_NAME='${APP_NAME}',_APP_REPO=$(body.repository.git_url),_CONFIG_REPO='${GIT_BASE_URL}'/'${CLUSTER_CONFIG_REPO}',_DEFAULT_IMAGE_REPO='${IMAGE_REPO}',_KUSTOMIZE_REPO='${GIT_BASE_URL}'/'${SHARED_KUSTOMIZE_REPO}',_REF=$(body.ref)' \ + --inline-config=$BUILD_YAML_PATH \ + --secret=${SECRET_PATH} + + ## Retrieve the URL + WEBHOOK_URL="https://cloudbuild.googleapis.com/v1/projects/${PROJECT_ID}/triggers/${TRIGGER_NAME}:webhook?key=${API_KEY_VALUE}&secret=${SECRET_VALUE}" + + ## Create Github Webhook + $BASE_DIR/scripts/git/${GIT_CMD} create_webhook ${APP_NAME} $WEBHOOK_URL + +} + +# execute function matching first arg and pass rest of args through +$1 $2 $3 $4 $5 $6 diff --git a/delivery-platform/scripts/common/clearvars.sh b/delivery-platform/scripts/common/clearvars.sh new file mode 100755 index 0000000..8f00036 --- /dev/null +++ b/delivery-platform/scripts/common/clearvars.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +unset GIT_TOKEN +unset GIT_PROVIDER +unset GIT_USERNAME +rm -f workdir/state.env + diff --git a/delivery-platform/scripts/common/manage-state.sh b/delivery-platform/scripts/common/manage-state.sh new file mode 100644 index 0000000..9797452 --- /dev/null +++ b/delivery-platform/scripts/common/manage-state.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +function load_state() { + mkdir -p $WORK_DIR + touch $WORK_DIR/state.env + source $WORK_DIR/state.env +} + +function write_state() { + mkdir -p $WORK_DIR + rm -f $WORK_DIR/state.env + echo "# Updated $(date)" > $WORK_DIR/state.env + echo "export GIT_PROVIDER=${GIT_PROVIDER}" >> $WORK_DIR/state.env + echo "export GIT_USERNAME=${GIT_USERNAME}" >> $WORK_DIR/state.env + echo "export GIT_TOKEN=${GIT_TOKEN}" >> $WORK_DIR/state.env + echo "export GIT_BASE_URL=${GIT_BASE_URL}" >> $WORK_DIR/state.env + echo "export GIT_CMD=${GIT_CMD}" >> $WORK_DIR/state.env + echo "export API_KEY_VALUE=${API_KEY_VALUE}" >> $WORK_DIR/state.env + echo "export CONTINUOUS_DELIVERY_SYSTEM=${CONTINUOUS_DELIVERY_SYSTEM}" >> $WORK_DIR/state.env + +} diff --git a/delivery-platform/scripts/common/set-apikey-var.sh b/delivery-platform/scripts/common/set-apikey-var.sh new file mode 100755 index 0000000..fc4da91 --- /dev/null +++ b/delivery-platform/scripts/common/set-apikey-var.sh @@ -0,0 +1,14 @@ +source ${BASE_DIR}/scripts/common/manage-state.sh + +if [[ ${API_KEY_VALUE} == "" ]]; then + echo "" + echo "No API Key found. Please generate a key from the URL and paste it at the prompt." + echo "Goto https://console.cloud.google.com/apis/credentials > 'Create Credentials' > 'API Key'" + echo "As a best practice, you can restrict the key to Cloud Build API under 'API restrictions' > 'Restrict key'" + echo "" + printf "Paste your API Key here and press enter: " && read keyval + export API_KEY_VALUE=${keyval} + echo "" +fi + +write_state \ No newline at end of file diff --git a/delivery-platform/scripts/continuous-delivery/set-cd-env.sh b/delivery-platform/scripts/continuous-delivery/set-cd-env.sh new file mode 100755 index 0000000..a3dce9d --- /dev/null +++ b/delivery-platform/scripts/continuous-delivery/set-cd-env.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +source ${BASE_DIR}/scripts/common/manage-state.sh +# Set continuous delivery system as either ACM or Cloud deploy +if [[ ${CONTINUOUS_DELIVERY_SYSTEM} == "" ]]; then + PS3="Select a Continuous Delivery system: " + select provider in ACM Clouddeploy; do + case $provider in + "ACM") + echo "you chose ACM"; + export CONTINUOUS_DELIVERY_SYSTEM="ACM" + break + ;; + "Clouddeploy") + echo "you chose Clouddeploy"; + export CONTINUOUS_DELIVERY_SYSTEM="Clouddeploy" + break + ;; + esac + done +fi + +write_state diff --git a/delivery-platform/scripts/deliver.sh b/delivery-platform/scripts/deliver.sh new file mode 100755 index 0000000..eb4105a --- /dev/null +++ b/delivery-platform/scripts/deliver.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# inputs +# app-name +# target-env +# hydrated_repo_path + +APP_NAME=${1:-"my-app"} +TARGET_ENV=${2:-"dev"} +HYDRATED_REPO_PATH=${3} + + +cd $WORK_DIR +git clone -b main $GIT_BASE_URL/${HYDRATED_CONFIG_REPO} +cd ${HYDRATED_CONFIG_REPO} +mkdir -p ${HYDRATED_REPO_PATH} +cp ${WORK_DIR}/${APP_NAME}-hydrated/${TARGET_ENV}/resources.yaml \ + ${WORK_DIR}/${HYDRATED_CONFIG_REPO}/${HYDRATED_REPO_PATH} + +git add . && git commit -m "Deploying new image ${IMAGE_ID}" +git push origin main + + +cd ${BASE_DIR} +rm -rf ${WORK_DIR}/${HYDRATED_CONFIG_REPO} +#rm -rf ${WORK_DIR}/${APP_NAME}-hydrated diff --git a/delivery-platform/scripts/git/gcsr.sh b/delivery-platform/scripts/git/gcsr.sh new file mode 100755 index 0000000..6688a1b --- /dev/null +++ b/delivery-platform/scripts/git/gcsr.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +action=$1 +repo=$2 +base=https://source.developers.google.com/p/${PROJECT_ID}/r/ + +if [[ $action == 'create' ]]; then + # Create + gcloud source repos create ${repo} + echo "Created ${repo}" +fi + +if [[ $action == 'delete' ]]; then + # Delete + echo "Deleting https://source.developers.google.com/p/${PROJECT_ID}/r/${repo}" + gcloud source repos delete ${repo} --quiet + echo "Deleted ${repo}" +fi + + diff --git a/delivery-platform/scripts/git/gh.sh b/delivery-platform/scripts/git/gh.sh new file mode 100755 index 0000000..825c22c --- /dev/null +++ b/delivery-platform/scripts/git/gh.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +## Input Validation +if [[ ${GIT_TOKEN} == "" ]]; then + echo "GIT_TOKEN variable not set. Please rerun the env script" + exit -1 +fi +if [[ ${GIT_USERNAME} == "" ]]; then + echo "GIT_USERNAME variable not set. Please rerun the env script" + exit -1 +fi + +if [[ $1 == "create_webhook" ]]; then + if [[ $2 == "" || $3 == "" ]]; then + echo "Missing parameters" + echo "Usage: gh create_webhook " + exit -1 + fi +fi + +if [[ $2 == "" || $1 == "" ]]; then + echo "Usage: gh [webhook_url]" + exit -1 +fi + +## Local variables +action=$1 +repo=$2 +WEBHOOK_URL=$3 +GIT_API_BASE="https://api.github.com" + +export GIT_ASKPASS=$BASE_DIR/common/ghp.sh + +## Execution +create_webhook () { + curl -H "Authorization: token ${GIT_TOKEN}" \ + -d '{"config": {"url": "'${WEBHOOK_URL}'", "content_type": "json"}}' \ + -X POST ${GIT_API_BASE}/repos/${GIT_USERNAME}/${repo}/hooks +} + + +if [[ $action == 'create' ]]; then + # Create + curl -H "Authorization: token ${GIT_TOKEN}" ${GIT_API_BASE}/user/repos -d '{"name": "'"${repo}"'"}' > /dev/null + echo "Created ${repo}" + + #TODO: Check if repo exists first +fi + +if [[ $action == 'delete' ]]; then + # Delete + echo + echo "Deleting ${GIT_API_BASE}/repos/${GIT_USERNAME}/${repo}" + curl -H "Authorization: token ${GIT_TOKEN}" -X "DELETE" ${GIT_API_BASE}/repos/${GIT_USERNAME}/${repo} + echo "Deleted ${repo}" +fi + +if [[ $action == 'create_webhook' ]]; then + # Webhook + create_webhook +fi + diff --git a/Dockerfile b/delivery-platform/scripts/git/git-ask-pass.sh old mode 100644 new mode 100755 similarity index 80% rename from Dockerfile rename to delivery-platform/scripts/git/git-ask-pass.sh index 957415a..6d5aa12 --- a/Dockerfile +++ b/delivery-platform/scripts/git/git-ask-pass.sh @@ -1,10 +1,12 @@ -# Copyright 2015 Google Inc. All rights reserved. +#!/usr/bin/env bash + +# Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -12,4 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:onbuild + +exec echo "${GIT_TOKEN}" \ No newline at end of file diff --git a/delivery-platform/scripts/git/gl.sh b/delivery-platform/scripts/git/gl.sh new file mode 100755 index 0000000..42c49ef --- /dev/null +++ b/delivery-platform/scripts/git/gl.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + + +if [[ ${GL_TOKEN} == "" ]]; then + echo "GL_TOKEN variable not set. Please rerun the env script" + exit -1 +fi +if [[ ${GITLAB_USERNAME} == "" ]]; then + echo "GITLAB_USERNAME variable not set. Please rerun the env script" + exit -1 +fi +if [[ $2 == "" || $1 == "" ]]; then + echo "Usage: gl " + exit -1 +fi +action=$1 +repo=$2 +user=$GITLAB_USERNAME +token=${GL_TOKEN} +base=https://gitlab.com/api/v4 + + + +if [[ $action == 'create' ]]; then + # Create + curl -H "Private-Token: ${token}" -H "Content-Type:application/json" ${base}/projects/ -d '{"name":"'$repo'"}' > /dev/null + echo "\nCreated ${repo}" + + #TODO: Check if repo exists first +fi + +if [[ $action == 'delete' ]]; then + # Delete + echo "Deleting ${user}%2f${repo}" + curl -H "Private-Token: ${token}" -X "DELETE" ${base}/projects/${user}%2f${repo} + echo "\nDeleted ${repo}" +fi diff --git a/delivery-platform/scripts/git/set-git-env.sh b/delivery-platform/scripts/git/set-git-env.sh new file mode 100755 index 0000000..1199759 --- /dev/null +++ b/delivery-platform/scripts/git/set-git-env.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +source ${BASE_DIR}/scripts/common/manage-state.sh +# Set Git provider as either GitHub, GitLab, or Cloud Source Repository +if [[ ${GIT_PROVIDER} == "" ]]; then + PS3="Select a Git Provider: " + select provider in GitHub GitLab Cloud-Source-Repository; do + case $provider in + "GitHub") + echo "you chose GitHub"; + export GIT_PROVIDER=GitHub + export GIT_CMD=gh.sh + break + ;; + "GitLab") + echo "you chose GitLab"; + export GIT_PROVIDER=GitLab + export GIT_CMD=gl.sh + break + ;; + "Cloud-Source-Repository") + echo "you chose Cloud Source Repository"; + export GIT_PROVIDER=Cloud-Source-Repository + export GIT_CMD=gcsr.sh + break + ;; + esac + done +fi + + +# Set Username to use with git +if [[ ${GIT_USERNAME} == "" ]] && [[ ${GIT_PROVIDER} != "Cloud-Source-Repository" ]]; then + printf "Enter your ${GIT_PROVIDER} username: " && read ghusername + export GIT_USERNAME=${ghusername} +fi + + + +# Set Personal Access Tokens to use for operations +if [[ ${GIT_TOKEN} == "" ]] && [[ ${GIT_PROVIDER} != "Cloud-Source-Repository" ]]; then + + if [[ ${GIT_PROVIDER} == "GitHub" ]]; then + echo "" + echo "No Github token found. Please generate a token from the following URL and paste it below." + echo "https://github.com/settings/tokens/new?scopes=repo%2Cread%3Auser%2Cread%3Aorg%2Cuser%3Aemail%2Cwrite%3Arepo_hook%2Cdelete_repo" + printf "Paste your token here and press enter: " && read ghtoken + export GIT_TOKEN=${ghtoken} + fi + + if [[ ${GIT_PROVIDER} == "GitLab" ]]; then + echo "" + echo "No GitLab token found. Please generate a token from the GitLab UI" + echo 'Provide a name and choose the "API" scope from the following URL:' + echo "https://gitlab.com/profile/personal_access_tokens" + printf "Once completed paste your token here and press enter: " && read ghtoken + export GIT_TOKEN=${ghtoken} + fi +fi + + +# Set Git URL +if [[ ${GIT_PROVIDER} == "GitHub" ]]; then + export GIT_BASE_URL=https://${GIT_USERNAME}@github.com/${GIT_USERNAME} +fi + +if [[ ${GIT_PROVIDER} == "GitLab" ]]; then + export GIT_BASE_URL=https://${GIT_USERNAME}@gitlab.com/${GIT_USERNAME} +fi + +if [[ ${GIT_PROVIDER} == "Cloud-Source-Repository" ]]; then + if [[ ${PROJECT_ID} == "" ]]; then + echo "" + echo "PROJECT_ID is not set. Please make sure to source the env.sh script." + exit -1 + fi + export GIT_BASE_URL=https://source.developers.google.com/p/${PROJECT_ID}/r/ +fi + +write_state diff --git a/delivery-platform/scripts/hydrate.sh b/delivery-platform/scripts/hydrate.sh new file mode 100755 index 0000000..d6436e4 --- /dev/null +++ b/delivery-platform/scripts/hydrate.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# inputs +# app-name +# target-env +# + +# outputs: +# ${WORK_DIR}/${APP_NAME}-hydrated/${TARGET_ENV}/resources.yaml + +APP_NAME=${1:-"my-app"} +TARGET_ENV=${2:-"dev"} + + +cd $WORK_DIR +git clone -b main $GIT_BASE_URL/${APP_NAME} +git clone -b main $GIT_BASE_URL/${SHARED_KUSTOMIZE_REPO} kustomize-base + +### Hydrate +cd ${WORK_DIR}/${APP_NAME}/k8s/${TARGET_ENV} +## use Git Commit sha +COMMIT_SHA=$(git rev-parse --short HEAD) +kustomize edit set image app=${IMAGE_REPO}/${APP_NAME}:${COMMIT_SHA} +IMAGE_ID=${COMMIT_SHA} + +## -OR- use image sha +##docker build --tag ${IMAGE_REPO}/${APP_NAME}:${COMMIT_SHA} . +##docker push ${IMAGE_REPO}/${APP_NAME}:${COMMIT_SHA} +##IMAGE_SHA=$(gcloud container images describe ${IMAGE_REPO}/${APP_NAME}:${COMMIT_SHA} --format='value(image_summary.digest)') +#kustomize edit set image app=${IMAGE_REPO}/${APP_NAME}@${IMAGE_SHA} +#IMAGE_ID=${IMAGE_SHA} + +mkdir -p ${WORK_DIR}/${APP_NAME}-hydrated/${TARGET_ENV} +kustomize build . -o ${WORK_DIR}/${APP_NAME}-hydrated/${TARGET_ENV}/resources.yaml + + +cd ${BASE_DIR} +rm -rf ${WORK_DIR}/${APP_NAME} +rm -rf ${WORK_DIR}/kustomize-base +rm -rf ${WORK_DIR}/${HYDRATED_CONFIG_REPO} + + + diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..ea53ae7 --- /dev/null +++ b/firebase.json @@ -0,0 +1,10 @@ +{ + "hosting": { + "public": "images", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ] + } +} diff --git a/html.go b/html.go deleted file mode 100644 index d274513..0000000 --- a/html.go +++ /dev/null @@ -1,110 +0,0 @@ -/** -# Copyright 2015 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -**/ - -package main - -const ( - html = ` - - - - - - - -Frontend Web Server - - -
-
-
 
-
- - -
-
-
Backend that serviced this request
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name{{.Name}}
Version{{.Version}}
ID{{.Id}}
Hostname{{.Hostname}}
Zone{{.Zone}}
Project{{.Project}}
Internal IP{{.InternalIP}}
External IP{{.ExternalIP}}
-
-
- -
-
-
Proxy that handled this request
-
-
- - - - - - - - - - - - - - - -
Address{{.ClientIP}}
Request{{.LBRequest}}
Error{{.Error}}
-
- -
-
-
 
-
-
-` -) diff --git a/images/README.md b/images/README.md new file mode 100644 index 0000000..9f7496a --- /dev/null +++ b/images/README.md @@ -0,0 +1,8 @@ +# Repo Images +The assets in this folder are used in the various instructions and readme files. They can be accessed via github or firebase hosting. + +To deploy images to firebase hosting perform the following actions. From the project's root directory run the following command to upload the contents of /images to firebase hosting + +``` +firebase deploy --only hosting +``` \ No newline at end of file diff --git a/images/clouddeploy-prod.png b/images/clouddeploy-prod.png new file mode 100644 index 0000000..7c0d3a9 Binary files /dev/null and b/images/clouddeploy-prod.png differ diff --git a/images/clouddeploy-stage.png b/images/clouddeploy-stage.png new file mode 100644 index 0000000..4398a8b Binary files /dev/null and b/images/clouddeploy-stage.png differ diff --git a/images/clusters.png b/images/clusters.png new file mode 100644 index 0000000..0453f5a Binary files /dev/null and b/images/clusters.png differ diff --git a/images/folder-structure.png b/images/folder-structure.png new file mode 100644 index 0000000..96e6e1c Binary files /dev/null and b/images/folder-structure.png differ diff --git a/images/provisioning.png b/images/provisioning.png new file mode 100644 index 0000000..4dab9eb Binary files /dev/null and b/images/provisioning.png differ diff --git a/images/sdw-title.png b/images/sdw-title.png new file mode 100644 index 0000000..dd18c66 Binary files /dev/null and b/images/sdw-title.png differ diff --git a/images/sdw-title.svg b/images/sdw-title.svg new file mode 100644 index 0000000..08d2953 --- /dev/null +++ b/images/sdw-title.svg @@ -0,0 +1 @@ +Software Delivery Workshop \ No newline at end of file diff --git a/kubernetes/deployments/prod/backend-production.yaml b/kubernetes/deployments/prod/backend-production.yaml deleted file mode 100644 index 3ae4af9..0000000 --- a/kubernetes/deployments/prod/backend-production.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2015 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -kind: Deployment -apiVersion: apps/v1 -metadata: - name: gceme-backend-production -spec: - selector: - matchLabels: - app: gceme - role: backend - env: production - replicas: 1 - template: - metadata: - name: backend - labels: - app: gceme - role: backend - env: production - spec: - containers: - - name: backend - image: gcr.io/cloud-solutions-images/gceme:1.0.0 - resources: - limits: - memory: "500Mi" - cpu: "100m" - imagePullPolicy: Always - readinessProbe: - httpGet: - path: /healthz - port: 8080 - command: ["sh", "-c", "app -port=8080"] - ports: - - name: backend - containerPort: 8080 diff --git a/labs/app-onboarding/README.md b/labs/app-onboarding/README.md new file mode 100644 index 0000000..89db507 --- /dev/null +++ b/labs/app-onboarding/README.md @@ -0,0 +1,341 @@ +# App Onboarding + +In this lab you will + +* Utilize predefined & custom app templates +* Create a new instance of an application +* Implement Build, Package and Deploy logic +* Configure automated pipeline execution +* Bundle onboarding logic for simple app creation + +## Preparing your workspace + +1. Open Cloud Shell editor by visiting the following url + +``` +https://ide.cloud.google.com +``` + +2. Ensure your project name is set in CLI + +``` +gcloud config set project {{project-id}} +``` + +3. Enable APIs + +``` +gcloud services enable \ + cloudbuild.googleapis.com \ + secretmanager.googleapis.com + +``` + +4. In the terminal window clone the application source with the following command: + +``` +git clone https://github.com/GoogleCloudPlatform/software-delivery-workshop.git +``` + +5. Change into the directory and set the IDE workspace to the repo root + +``` +cd software-delivery-workshop && rm -rf .git +cd delivery-platform && cloudshell workspace . +``` + +## Utilizing predefined & custom app templates + +Developers should be able to choose from a set of templates commonly used within the organization. The onboarding process will create a centralized set of template repositories stored in your GitHub account. In later steps these template repositories will be copied and modified for use as the base for new applications. For this lab you will seed your template repository with a sample structure provided here. You can add your own templates by adding additional folders modeled after the sample. + +In this step you will create your own repository to hold app templates, from the example files provided. A helper script is provided to simplify the interactions with GitHub. + +These are one time steps used to populate your template repositories. Future steps will reused these repositories. + +### Configure GitHub Access + +The steps in this tutorial call the GitHub API to create and configure repositories. Your GitHub username and a personal access token are required at various points that follow. The script below will help you acquire the values and store them as local variables for later use. + +``` +source ./onboard-env.sh +``` + +### Create App Template Repository + +Sample application templates are provided along with this lab as an example of how you might integrate your own base templates. In this step you create your own copy of these files in a repo called `mcd-app-templates` in your GitHub account. + +1. Copy the template to the working directory + +``` +cp -R $BASE_DIR/resources/repos/app-templates $WORK_DIR +cd $WORK_DIR/app-templates +``` + +2. Create an empty remote repository in your GitHub account + +``` +$BASE_DIR/scripts/git/gh.sh create mcd-app-templates +``` + +3. Push the template repository to your remote repository + +``` +git init && git symbolic-ref HEAD refs/heads/main && git add . && git commit -m "initial commit" +git remote add origin $GIT_BASE_URL/mcd-app-templates +git push origin main +``` + +4. Clean up the working directory + +``` +cd $BASE_DIR +rm -rf $WORK_DIR/app-templates +``` + +### Create Shared Base Configs Repository + +This tutorial utilizes a tool called Kustomize that uses base configuration files shared by multiple teams then overlays application specific configurations over top. This enables platform teams to scale across many teams and environments. + +In this step you create the shared configuration repository called `mcd-shared_kustomize `from the samples provided + +1. Copy the template to the working directory + +``` +cp -R $BASE_DIR/resources/repos/shared-kustomize $WORK_DIR +cd $WORK_DIR/shared-kustomize +``` + +2. Create an empty remote repository in your GitHub account + +``` +$BASE_DIR/scripts/git/gh.sh create mcd-shared_kustomize +``` + +3. Push the template repository to your remote repository + +``` +git init && git symbolic-ref HEAD refs/heads/main && git add . && git commit -m "initial commit" +git remote add origin $GIT_BASE_URL/mcd-shared_kustomize +git push origin main +``` + +4. Clean up the working directory + +``` +cd $BASE_DIR +rm -rf $WORK_DIR/shared-kustomize +``` + +With your template repositories created you're ready to use them to create an app instance + +## Creating a new instance of an application + +Creating a new application from a template often requires that placeholder variables be swapped out with real values across multiple files in the template structure. Once the substitution is completed a new repository is created for the new app instance. It is this app instance repository that the developers will clone and work with in their day to day development. + +In this step you will substitute values in an app template and post the resulting files to a new repository. + +### Define a name for the new application + +``` +export APP_NAME=my-app +``` + +### Retrieve the Golang template repository + +``` +cd $WORK_DIR/ +git clone -b main $GIT_BASE_URL/mcd-app-templates app-templates +rm -rf app-templates/.git +cd app-templates/golang +``` + +### Substitute placeholder values + +One of the most common needs for onboarding is swapping out variables in templates for actual instances used in the application. For example providing the application name. The following command creates instances of all .tmpl files with the values stored in environment variables. + +``` +for template in $(find . -name '*.tmpl'); do envsubst < ${template} > ${template%.*}; done +``` + +### Create a new repo and store the updated files + +1. Create an empty remote repository in your GitHub account + +``` +$BASE_DIR/scripts/git/gh.sh create ${APP_NAME} +``` + +2. Push the template repository to your remote repository + +``` +git init && git symbolic-ref HEAD refs/heads/main && git add . && git commit -m "initial commit" +git remote add origin $GIT_BASE_URL/${APP_NAME} +git push origin main +``` + +Now that the app instance has been created it's time to implement continuous builds. + + +## Configuring automated pipeline execution + +The central part of a Continuous Integration system is the ability to execute the pipeline logic based on the events originating in the source control system. When a developer commits code in their repository events are fired that can be configured to trigger processes in other systems. + +In this step you will configure GitHub to call Google Cloud Build and execute your pipeline, whenever users commit or tag code in their repository. + +### Enable Secure Access + +You will need 2 elements to configure secure access to your application pipeline. An API key and a secret unique to the pipeline. + +#### API Key + +The API key is used to identify the client that is calling into a given API. In this case the client will be GitHub. A best practice not covered here is to lock down the scope of the API key to only the specific APIs that client will be accessing. You created the key in a previous step. + +1. You can review the key by clicking on [this link]( https://console.cloud.google.com/apis/credentials) +2. You can ensure the value is set by running the following command + +``` +echo $API_KEY_VALUE +``` + +#### Pipeline Secret + +The secrets are used to authorize a caller and ensure they have rights to the specific cloud build target job. You may have 2 different repositories in GitHub that should only have access to their own pipelines. While the API_KEY limits which APIs can be utilized by github (in this case the Cloud Build API is being called), the secret limits which Job in the Cloud Build API can be executed by the client. + +1. Define the secret name, location and value + +``` +SECRET_NAME=${APP_NAME}-webhook-trigger-cd-secret +SECRET_PATH=projects/${PROJECT_NUMBER}/secrets/${SECRET_NAME}/versions/1 +SECRET_VALUE=$(sed "s/[^a-zA-Z0-9]//g" <<< $(openssl rand -base64 15)) +``` + +2. Create the secret + +``` +printf ${SECRET_VALUE} | gcloud secrets create ${SECRET_NAME} --data-file=- +``` + +3. Allow Cloud Build to read the secret + +``` +gcloud secrets add-iam-policy-binding ${SECRET_NAME} \ + --member=serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-cloudbuild.iam.gserviceaccount.com \ + --role='roles/secretmanager.secretAccessor' +``` + +### Create Cloud Build Trigger + +The Cloud Build Trigger is the configuration that will actually be executing the CICD processes. +The job requires a few key values to be provided on creation in order to properly configure the trigger. + +1. Define the name of the trigger and where the configuration file can be found + +``` +export TRIGGER_NAME=${APP_NAME}-clouddeploy-webhook-trigger +export BUILD_YAML_PATH=$WORK_DIR/app-templates/golang/build/cloudbuild-build-only.yaml +``` + +2. Define the location of the shared base configuration repo. + +``` +export KUSTOMIZE_REPO=${GIT_BASE_URL}/mcd-shared_kustomize +``` + +3. A variable was set in the onboard-env.sh script defining the project's container registry. Review the value with the command below. + +``` +echo $IMAGE_REPO +``` + + +5. Create CloudBuild Webhook Trigger using the variables created previously. The application repo location is pulled from the body of the request from GitHub. A value below references the path in the request body where it's located + +``` + gcloud alpha builds triggers create webhook \ + --name=${TRIGGER_NAME} \ + --substitutions='_APP_NAME='${APP_NAME}',_APP_REPO=$(body.repository.git_url),_CONFIG_REPO='${GIT_BASE_URL}'/'${CLUSTER_CONFIG_REPO}',_DEFAULT_IMAGE_REPO='${IMAGE_REPO}',_KUSTOMIZE_REPO='${GIT_BASE_URL}'/'${SHARED_KUSTOMIZE_REPO}',_REF=$(body.ref)' \ + --inline-config=$BUILD_YAML_PATH \ + --secret=${SECRET_PATH} +``` + +6. Review the newly created Cloud Build trigger in the Console by [visiting this link](https://console.cloud.google.com/cloud-build/triggers) + + + +### Configure GitHub Webhook + +1. Define a variable for the webhook URL + +``` +WEBHOOK_URL="https://cloudbuild.googleapis.com/v1/projects/${PROJECT_ID}/triggers/${TRIGGER_NAME}:webhook?key=${API_KEY_VALUE}&secret=${SECRET_VALUE}" +``` + +2. Configure the webhook in GitHub + +``` +$BASE_DIR/scripts/git/gh.sh create_webhook ${APP_NAME} $WEBHOOK_URL + +``` + +3. Go to the application repo and review the newly configured webhook + +``` +REPO_URL=${GIT_BASE_URL}/${APP_NAME}/settings/hooks +echo $REPO_URL +``` + +Now that you've manually performed all the steps needed to create a new application it's time to automate it in a script. + +## Automating all the onboarding steps + +In practice it's not feasible to execute each of the above steps for every new application. Instead the logic should be incorporated into a script for easy execution. The steps above have already been included in a script for your use. + +In this step you will use the script provided to create a new application + +### Create a new application + +1. Ensure you're in the right directory + +``` +cd $BASE_DIR +``` + +2. Create a new application + +The format is `app.sh create ` + +``` +./app.sh create demo-app golang +``` + +All of the steps are executed automatically. + +### Review the GitHub Repo + +At this point you will be able to review the new repository in Github + +1. Retrieve the GitHub repository URL by executing the following command + +``` +echo ${GIT_BASE_URL}/${APP_NAME} +``` + +2. Open the URL with your web browser to review the new application +3. Note examples where the template variables have been replace with instance values as shown in the url below + +``` +echo ${GIT_BASE_URL}/${APP_NAME}/blob/main/k8s/prod/deployment.yaml#L24 +``` + +4. Review the web hook configured at the url below + +``` +echo ${GIT_BASE_URL}/${APP_NAME}/settings/hooks +``` + +### Review the CloudBuild Trigger + +The trigger was automatically set up by the script + +1. Review the Cloud Build trigger in the Console by [visiting this link](https://console.cloud.google.com/cloud-build/triggers) +2. Review the build history [on this page](https://console.cloud.google.com/cloud-build/builds) diff --git a/labs/cloud-deploy/Dockerfile b/labs/cloud-deploy/Dockerfile new file mode 100644 index 0000000..28d9b14 --- /dev/null +++ b/labs/cloud-deploy/Dockerfile @@ -0,0 +1,19 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM golang:1 +WORKDIR /app +COPY * ./ +RUN go build -o cloud-deploy-tutorial +CMD ["/app/cloud-deploy-tutorial"] diff --git a/labs/cloud-deploy/README.md b/labs/cloud-deploy/README.md new file mode 100644 index 0000000..a1b175b --- /dev/null +++ b/labs/cloud-deploy/README.md @@ -0,0 +1,325 @@ + +# Releasing with Cloud Deploy + +## Objectives + +In this tutorial you will create three GKE clusters named preview, staging and prod. Then, create a Cloud Deploy target corresponding to each cluster and a Cloud Deploy pipeline that will define the sequence of steps to perform deployment in those targets. + +The deployment flow will be triggered by a cloudbuild pipeline that will create Cloud Deploy release and perform the deployment in the preview cluster. After you have verified that the deployment in preview was successful and working as expected, you will manually promote the release in the staging cluster. Promotion of the release in the prod cluster will require approval, you will approve the prod pipeline in Cloud Deploy UI and finally promote it. + +The objectives of this tutorial can be broken down into the following steps: + +- Prepare your workspace +- Define Cloud Deploy targets +- Define Cloud Deploy pipeline +- Create a Release +- Promote a deployment +- Approve a production release + +## Before you begin + +For this reference guide, you need a Google Cloud [project](https://cloud.google.com/resource-manager/docs/cloud-platform-resource-hierarchy#projects). You can create a new one, or select a project you already created: + +1. Select or create a Google Cloud project. + +[GO TO THE PROJECT SELECTOR PAGE](https://console.corp.google.com/projectselector2/home/dashboard) + +1. Enable billing for your project. + +[ENABLE BILLING](https://support.google.com/cloud/answer/6293499#enable-billing) + +## + +## Platform Setup + +### Preparing your workspace + +We will set up our environment here required to run this tutorial. When this step is completed, we will have a GKE cluster created where we can run the deployments. + +1. **Set gcloud config defaults** + +``` +gcloud config set project + +gcloud config set deploy/region us-central1 +``` + +2. **Clone Repo** + +``` +git clone https://github.com/GoogleCloudPlatform/software-delivery-workshop +cd software-delivery-workshop/labs/cloud-deploy/ +cloudshell workspace . +rm -rf deploy && mkdir deploy +``` + +3. **Set environment variables** + +``` +export PROJECT_ID=$(gcloud config get-value project) +export PROJECT_NUMBER=$(gcloud projects list --filter="$PROJECT_ID" --format="value(PROJECT_NUMBER)") +``` + +4. **Enable APIs** + +``` +gcloud services enable \ +cloudresourcemanager.googleapis.com \ + container.googleapis.com \ + cloudbuild.googleapis.com \ + containerregistry.googleapis.com \ + secretmanager.googleapis.com \ + clouddeploy.googleapis.com +``` + +5. **Create GKE clusters** + +``` + gcloud container clusters create preview \ +--zone=us-central1-a --async + gcloud container clusters create staging \ +--zone=us-central1-b --async + gcloud container clusters create prod \ +--zone=us-central1-c +``` + +### Defining Cloud Deploy Targets + +1. **Create a file in the deploy directory named preview.yaml with the following command in cloudshell:** + +``` +cat <deploy/preview.yaml +apiVersion: deploy.cloud.google.com/v1beta1 +kind: Target +metadata: + name: preview + annotations: {} + labels: {} +description: Target for preview environment +gke: + cluster: projects/$PROJECT_ID/locations/us-central1-a/clusters/preview +EOF +``` + + As you noticed, the "kind" tag is "Target". It allows us to add some metadata to the target, a description and finally the GKE cluster where the deployment is supposed to happen for this target. + +2. **Create a file in the deploy directory named staging.yaml with the following command in cloudshell:** + +``` +cat <deploy/staging.yaml +apiVersion: deploy.cloud.google.com/v1beta1 +kind: Target +metadata: + name: staging + annotations: {} + labels: {} +description: Target for staging environment +gke: + cluster: projects/$PROJECT_ID/locations/us-central1-b/clusters/staging +EOF +``` + +3. **Create a file in the deploy directory named prod.yaml with the following command in cloudshell:** + +``` +cat <deploy/prod.yaml +apiVersion: deploy.cloud.google.com/v1beta1 +kind: Target +metadata: + name: prod + annotations: {} + labels: {} +description: Target for prod environment +requireApproval: true +gke: + cluster: projects/$PROJECT_ID/locations/us-central1-c/clusters/prod +EOF +``` + +Notice the tag requireApproval which is set to true. This will not allow promotion into prod target until the approval is granted. You require `roles/clouddeploy.approver `role to approve a release. + +4. **Create the Deploy Targets** + +``` +gcloud config set deploy/region us-central1 +gcloud beta deploy apply --file deploy/preview.yaml +gcloud beta deploy apply --file deploy/staging.yaml +gcloud beta deploy apply --file deploy/prod.yaml +``` + +## App Creation + +As part of the creation of a new application the CICD pipeline is typically setup to perform automatic builds, integration testing and deployments. The following steps are considered part of the setup process for a new app. Each new application will have a deployment pipeline configured. + +### Defining Cloud Deploy pipeline + +1. **Create a file in the deploy directory named pipeline.yaml with the following command in cloudshell:** + +``` +cat <>deploy/pipeline.yaml +apiVersion: deploy.cloud.google.com/v1beta1 +kind: DeliveryPipeline +metadata: + name: sample-app + labels: + app: sample-app +description: delivery pipeline +serialPipeline: + stages: + - targetId: preview + profiles: + - preview + - targetId: staging + profiles: + - staging + - targetId: prod + profiles: + - prod +EOF +``` + +** ** +As you noticed, the "kind" tag is "DeliveryPipeline". It lets you define the metadata for the pipeline, a description and an order of deployment into various targets via serialPipeline tag. + +`serialPipeline` tag contains a tag named stages which is a list of all targets to which this delivery pipeline is configured to deploy. + +`targetId` identifies the specific target to use for this stage of the delivery pipeline. The value is the metadata.name property from the target definition. + +`profiles` is a list of zero or more Skaffold profile names, from skaffold.yaml. Cloud Deploy uses the profile with skaffold render when creating the release. + +2. **Apply Pipeline** + +``` +gcloud beta deploy apply --file deploy/pipeline.yaml +``` + +## Development Phase + +As the applications are developed automated CICD toolchains will build and store assets. The following commands are executed to build the application using skaffold and store assets for deployment with Cloud Deploy. This step would be performed by your CICD process for every application build. + +1. **Build and store the application with skaffold** + +``` +skaffold build \ +--file-output=artifacts.json \ +--default-repo gcr.io/$PROJECT_ID \ +--push=true +``` + +## Release Phase + +At the end of your CICD process, typically when the code is Tagged for production, you will initiate the release process by calling the `cloud deploy release` command. Later, once the deployment has been validated and approved you'll move the release through the various target environments by promoting and approving the action through automated processes or manual approvals. + + + +### Creating a release + +We created Cloud Deploy files in this tutorial earlier to get an understanding of how Cloud Deploy works. For the purpose of demo, we have created the same Cloud Deploy files and pushed them to a github repo with a sample go application and we will use Cloud Deploy to do the release of that application. + +``` +export REL_TIMESTAMP=$(date '+%Y%m%d-%H%M%S') + +gcloud beta deploy releases create \ +sample-app-release-${REL_TIMESTAMP} \ +--delivery-pipeline=sample-app \ +--description="Release demo" \ +--build-artifacts=artifacts.json \ +--annotations="release-id=rel-${REL_TIMESTAMP}" +``` + + +### Review the release + +When a Cloud Deploy release is created, it automatically rolls it out in the first target which is preview. + +1. Go to [ in Google cloud console](https://console.cloud.google.com/deploy) +2. Click on "sample-app" + + On this screen you will see a graphic representation of your pipeline. + +3. Confirm a green outline on the left side of the preview box which means that the release has been deployed to that environment. +4. Optionally review additional details about the release by clicking on the release name under Release Details in the lower section of the screen + +5. Verify that the release successfully deployed the application, run the following command it cloushell + +``` +gcloud container clusters get-credentials preview --zone us-central1-a && kubectl port-forward --namespace default $(kubectl get pod --namespace default --selector="app=cloud-deploy-tutorial" --output jsonpath='{.items[0].metadata.name}') 8080:8080 +``` + +6. Click on the web preview icon in the upper right of the screen. +7. Select Preview on port 8080 + +This will take you to a new page which shows the message "Hello World!" + +8. Use `ctrl+c` in the terminal to end the port-forward. + +### Promoting a release + +Now that your release is deployed to the first target (preview) in the pipeline, you can promote it to the next target (staging). Run the following command to begin the process. + +``` +gcloud beta deploy releases promote \ +--release=sample-app-release-${REL_TIMESTAMP} \ +--delivery-pipeline=sample-app \ +--quiet +``` + +### Review the release promotion + +1. Go to the [sample-app pipeline in the Google Cloud console](https://console.cloud.google.com/deploy/delivery-pipelines/us-central1/sample-app) +2. Confirm a green outline on the left side of the staging box which means that the release has been deployed to that environment. + +3. Verify the application is deployed correctly by creating a tunnel to it + +``` +gcloud container clusters get-credentials staging --zone us-central1-b && kubectl port-forward --namespace default $(kubectl get pod --namespace default --selector="app=cloud-deploy-tutorial" --output jsonpath='{.items[0].metadata.name}') 8080:8080 +``` + +4. Click on the web preview icon in the upper right of the screen. +5. Select Preview on port 8080 + +This will take you to a new page which shows the message "Hello World!" + +6. Use `ctrl+c` in the terminal to end the port-forward. + +### Approving a production release + +Remember when we created prod target via prod.yaml, we specified the tag requireApproval as true. This will force a requirement of approval for promotion in prod. + +1. Promote the staging release to production with the following command + +``` +gcloud beta deploy releases promote \ +--release=sample-app-release-${REL_TIMESTAMP} \ +--delivery-pipeline=sample-app \ +--quiet +``` + +2. Go to the [sample-app pipeline in the Google Cloud console](https://console.cloud.google.com/deploy/delivery-pipelines/us-central1/sample-app) + +3. Notice the yellow indicator noting "1 pending". + + This message indicates there is a release queued for deployment to production but requires review and approval. + +4. Click on the "Review" button just below the yellow notice. +5. In the next screen click "Review" again to access the approval screen for production +6. Optionally review the Manifest Diff to review the changes. In this case a whole new file. +7. Click on the "Approve" button +8. Return to the [sample-app pipeline page](https://console.cloud.google.com/deploy/delivery-pipelines/us-central1/sample-app) where you will see the release to prod in progress. + +### Review the production release + +As with the other environments you can review the deployment when it completes using the steps below. + +1. Run the following command it cloudshell to create the port-forward + +``` +gcloud container clusters get-credentials prod --zone us-central1-c && kubectl port-forward --namespace default $(kubectl get pod --namespace default --selector="app=cloud-deploy-tutorial" --output jsonpath='{.items[0].metadata.name}') 8080:8080 +``` + +2. Click on the web preview icon in the upper right of the screen. +3. Select Preview on port 8080 + +This will take you to a new page which shows the message "Hello World!" + +4. Use `ctrl+c` in the terminal to end the port-forward. \ No newline at end of file diff --git a/labs/cloud-deploy/go.mod b/labs/cloud-deploy/go.mod new file mode 100644 index 0000000..343c10a --- /dev/null +++ b/labs/cloud-deploy/go.mod @@ -0,0 +1,3 @@ +module example.com/golang + +go 1.16 diff --git a/labs/cloud-deploy/k8s/preview/deployment.yaml b/labs/cloud-deploy/k8s/preview/deployment.yaml new file mode 100644 index 0000000..29b7eaa --- /dev/null +++ b/labs/cloud-deploy/k8s/preview/deployment.yaml @@ -0,0 +1,34 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: app # Must match base value + image: app # Overwrites values from base - Needs to match skaffold artifact + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + cpu: 100m + memory: 256Mi + env: + - name: ENVIRONMENT + value: preview diff --git a/labs/cloud-deploy/k8s/preview/kustomization.yaml b/labs/cloud-deploy/k8s/preview/kustomization.yaml new file mode 100644 index 0000000..898b96a --- /dev/null +++ b/labs/cloud-deploy/k8s/preview/kustomization.yaml @@ -0,0 +1,22 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bases: +- ../../kustomize-base/golang +patches: +- deployment.yaml +namePrefix: cloud-deploy-sample- # App name (dash) +commonLabels: + app: cloud-deploy-tutorial # App name for selectors + role: backend diff --git a/labs/cloud-deploy/k8s/prod/deployment.yaml b/labs/cloud-deploy/k8s/prod/deployment.yaml new file mode 100644 index 0000000..f32c239 --- /dev/null +++ b/labs/cloud-deploy/k8s/prod/deployment.yaml @@ -0,0 +1,34 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: app + image: app + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + cpu: 100m + memory: 256Mi + env: + - name: ENVIRONMENT + value: prod diff --git a/labs/cloud-deploy/k8s/prod/kustomization.yaml b/labs/cloud-deploy/k8s/prod/kustomization.yaml new file mode 100644 index 0000000..9143d26 --- /dev/null +++ b/labs/cloud-deploy/k8s/prod/kustomization.yaml @@ -0,0 +1,22 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bases: +- ../../kustomize-base/golang +patches: +- deployment.yaml +namePrefix: cloud-deploy-sample- +commonLabels: + app: cloud-deploy-tutorial + role: backend diff --git a/labs/cloud-deploy/k8s/staging/deployment.yaml b/labs/cloud-deploy/k8s/staging/deployment.yaml new file mode 100644 index 0000000..7aa6ad7 --- /dev/null +++ b/labs/cloud-deploy/k8s/staging/deployment.yaml @@ -0,0 +1,35 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + replicas: 1 + template: + spec: + containers: + - name: app + image: app + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + cpu: 100m + memory: 256Mi + env: + - name: ENVIRONMENT + value: staging diff --git a/labs/cloud-deploy/k8s/staging/kustomization.yaml b/labs/cloud-deploy/k8s/staging/kustomization.yaml new file mode 100644 index 0000000..8c422d6 --- /dev/null +++ b/labs/cloud-deploy/k8s/staging/kustomization.yaml @@ -0,0 +1,22 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +bases: +- ../../kustomize-base/golang +patches: +- deployment.yaml +commonLabels: + app: cloud-deploy-tutorial + role: backend +namePrefix: cloud-deploy-sample- diff --git a/kubernetes/deployments/canary/backend-canary.yaml b/labs/cloud-deploy/kustomize-base/golang/deployment.yaml similarity index 58% rename from kubernetes/deployments/canary/backend-canary.yaml rename to labs/cloud-deploy/kustomize-base/golang/deployment.yaml index 8c52f27..080de4c 100644 --- a/kubernetes/deployments/canary/backend-canary.yaml +++ b/labs/cloud-deploy/kustomize-base/golang/deployment.yaml @@ -1,10 +1,10 @@ -# Copyright 2015 Google Inc. All rights reserved. +# Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,35 +15,34 @@ kind: Deployment apiVersion: apps/v1 metadata: - name: gceme-backend-canary + name: app spec: - selector: - matchLabels: - app: gceme - role: backend - env: canary - replicas: 1 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 template: metadata: - name: backend - labels: - app: gceme - role: backend - env: canary + name: app spec: containers: - - name: backend - image: gcr.io/cloud-solutions-images/gceme:1.0.0 + - name: app + image: app resources: limits: - memory: "500Mi" - cpu: "100m" - imagePullPolicy: Always + memory: "512Mi" + cpu: "500m" + env: + - name: ENVIRONMENT + value: base + - name: LOG_LEVEL + value: info readinessProbe: + initialDelaySeconds: 1 + periodSeconds: 1 httpGet: path: /healthz port: 8080 - command: ["sh", "-c", "app -port=8080"] ports: - - name: backend + - name: http containerPort: 8080 diff --git a/labs/cloud-deploy/kustomize-base/golang/kustomization.yaml b/labs/cloud-deploy/kustomize-base/golang/kustomization.yaml new file mode 100644 index 0000000..8c06dd4 --- /dev/null +++ b/labs/cloud-deploy/kustomize-base/golang/kustomization.yaml @@ -0,0 +1,17 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +resources: + - service.yaml + - deployment.yaml diff --git a/kubernetes/deployments/dev/default.yml b/labs/cloud-deploy/kustomize-base/golang/service.yaml similarity index 76% rename from kubernetes/deployments/dev/default.yml rename to labs/cloud-deploy/kustomize-base/golang/service.yaml index ddd44d6..5807847 100644 --- a/kubernetes/deployments/dev/default.yml +++ b/labs/cloud-deploy/kustomize-base/golang/service.yaml @@ -1,4 +1,4 @@ -# Copyright 2015 Google Inc. All rights reserved. +# Copyright 2021 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,15 +11,15 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# -kind: ResourceQuota + +kind: Service apiVersion: v1 metadata: - name: default + name: app spec: - hard: - memory: 2Gi - cpu: 2 - pods: 4 - services: 2 - resourcequotas: 1 + type: ClusterIP + ports: + - name: http + port: 8080 + targetPort: 8080 + protocol: TCP diff --git a/labs/cloud-deploy/main.go b/labs/cloud-deploy/main.go new file mode 100644 index 0000000..4b896c1 --- /dev/null +++ b/labs/cloud-deploy/main.go @@ -0,0 +1,40 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "log" + "net/http" + "os" +) + +func main() { + env := os.Getenv("ENVIRONMENT") + port := 8080 + log.Printf("Running in environment: %s\n", env) + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + log.Printf("Received request from %s at %s", r.RemoteAddr, r.URL.EscapedPath()) + fmt.Fprint(w, "Hello World!") + }) + http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + log.Printf("Received health check from %s", r.RemoteAddr) + w.WriteHeader(http.StatusOK) + }) + log.Printf("Starting server on port: %v", port) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) + +} diff --git a/labs/cloud-deploy/skaffold.yaml b/labs/cloud-deploy/skaffold.yaml new file mode 100644 index 0000000..43314ba --- /dev/null +++ b/labs/cloud-deploy/skaffold.yaml @@ -0,0 +1,41 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: skaffold/v1beta14 +kind: Config +build: + artifacts: + - image: app # Match name in deployment yaml + context: ./ +#deploy: +# kustomize: +# path: k8s/dev + +profiles: +- name: preview + activation: + - command: preview + deploy: + kustomize: + path: k8s/preview + +- name: staging + deploy: + kustomize: + path: k8s/staging + +- name: prod + deploy: + kustomize: + path: k8s/prod diff --git a/labs/cloudbuild-scheduled-jobs/README.md b/labs/cloudbuild-scheduled-jobs/README.md new file mode 100644 index 0000000..c0577ab --- /dev/null +++ b/labs/cloudbuild-scheduled-jobs/README.md @@ -0,0 +1,137 @@ +# Cloud Build Scheduled Jobs + +This lab demonstrates how to automate the execution of Cloud Build jobs based on a scheduled frequency. + +This technique is often used to ensure the latest upstream changes are always included in your containers. + +In this example a custom image has been built on top of a base image. To ensure security updates and patches from the base image are always applied to the custom image as well, Cloud Scheduler is used to trigger the build on a regular basis. + + +What you will accomplish +- Create Artifact Registry to Store and Scan your custom image +- Configure Github with Google cloud to store your image configurations +- Create a Cloud Build trigger to automate creation of custom image +- Configure Cloud Scheduler to initiate builds on a regular basis +- Review the results of the processes + +## Setup and Requirements + +### Enable APIs + +```sh +gcloud services enable \ + container.googleapis.com \ + cloudbuild.googleapis.com \ + containerregistry.googleapis.com \ + containerscanning.googleapis.com \ + artifactregistry.googleapis.com \ + cloudscheduler.googleapis.com +``` + + +### Set Environment Variables +The following variables will be used multiple times throughout the tutorial. + + + +```sh +PROJECT_ID=[your project id] +``` + +```sh +GITHUB_USER=[your github name] +``` + + +```sh +PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)') +REGION=us-central1 + +``` + +### Artifact Registry Repository +In this lab you will be using Artifact Registry to store and scan your images. Create the repository with the following command. + +```sh +gcloud artifacts repositories create custom-images \ + --repository-format=docker \ + --location=$REGION \ + --description="Docker repository" +``` + +Configure docker to utilize your gcloud credentials when accessing Artifact Registry. + +```sh +gcloud auth configure-docker $REGION-docker.pkg.dev +``` + + +## Git Repository +In practice you will keep the Dockerfile for your custom images in a git repo. The automated process will access that repo during the build process to pull the relevant configs and Dockerfile. + +### Fork the sample repository + +For this tutorial you will fork a sample repo that provides the container definitions used in this lab. + +- [Click this link to fork the repo](https://github.com/GoogleCloudPlatform/software-delivery-workshop/fork) + + +### Connect Cloud Build to GitHub + +Next you will connect that repository to Cloud Build using the built in Github connection capability. Follow the link below to the instructions describing how to complete the process. + +- [Connect the github repo](https://cloud.google.com/build/docs/automating-builds/github/connect-repo-github) + + + +## Cloud Build + +```sh +TRIGGER_NAME=custom-image-trigger + +gcloud beta builds triggers create manual \ + --region=us-central1 \ + --name=${TRIGGER_NAME} \ + --repo=${GITHUB_USER}/software-delivery-workshop \ + --repo-type=GITHUB \ + --branch=main \ + --build-config=labs/cloudbuild-scheduled-jobs/code-oss-java/cloudbuild.yaml \ + --substitutions=_REGION=us-central1,_AR_REPO_NAME=custom-images,_AR_IMAGE_NAME=code-oss-java,_IMAGE_DIR=labs/cloudbuild-scheduled-jobs/code-oss-java + +TRIGGER_ID=$(gcloud beta builds triggers list --region=us-central1 \ + --filter=name="${TRIGGER_NAME}" --format="value(id)") + +``` + +## Cloud Scheduler + + +```sh +gcloud scheduler jobs create http run-build \ + --schedule='3 * * * *' \ + --uri=https://cloudbuild.googleapis.com/v1/projects/${PROJECT_ID}/locations/us-central1/triggers/${TRIGGER_ID}:run \ + --location=us-central1 \ + --oauth-service-account-email=${PROJECT_NUMBER}-compute@developer.gserviceaccount.com \ + --oauth-token-scope=https://www.googleapis.com/auth/cloud-platform +``` + + +Test the initial functionality by running the job [manually from the console](https://console.cloud.google.com/cloudscheduler) +- On the Cloud Scheduler page find the entry you just created called run-build +- Click the three dots for that row under the Actions column +- Click Force a job run to test the system manually + + +## Review The Results + +### Vulnerabilities +To see the vulnerabilities in an image: +- Open the [Repositories page](https://console.cloud.google.com/artifacts) +- In the repositories list, click a repository. +- Click an image name. +- Vulnerability totals for each image digest are displayed in the Vulnerabilities column. + +To view the list of vulnerabilities for an image: +- click the link in the Vulnerabilities column +- The vulnerability list shows the severity, availability of a fix, and the name of the package that contains the vulnerability. + diff --git a/labs/cloudbuild-scheduled-jobs/code-oss-java/Dockerfile b/labs/cloudbuild-scheduled-jobs/code-oss-java/Dockerfile new file mode 100644 index 0000000..87f0fce --- /dev/null +++ b/labs/cloudbuild-scheduled-jobs/code-oss-java/Dockerfile @@ -0,0 +1,22 @@ + +FROM us-central1-docker.pkg.dev/cloud-workstations-images/predefined/code-oss:latest + +RUN wget https://open-vsx.org/api/vscjava/vscode-java-debug/0.40.1/file/vscjava.vscode-java-debug-0.40.1.vsix && \ +unzip vscjava.vscode-java-debug-0.40.1.vsix "extension/*" &&\ +mv extension /opt/code-oss/extensions/java-debug + +RUN wget https://open-vsx.org/api/vscjava/vscode-java-dependency/0.19.1/file/vscjava.vscode-java-dependency-0.19.1.vsix && \ +unzip vscjava.vscode-java-dependency-0.19.1.vsix "extension/*" &&\ +mv extension /opt/code-oss/extensions/java-dependency + +RUN wget https://open-vsx.org/api/redhat/java/1.6.0/file/redhat.java-1.6.0.vsix && \ +unzip redhat.java-1.6.0.vsix "extension/*" &&\ +mv extension /opt/code-oss/extensions/redhat-java + +RUN wget https://open-vsx.org/api/vscjava/vscode-maven/0.35.2/file/vscjava.vscode-maven-0.35.2.vsix && \ +unzip vscjava.vscode-maven-0.35.2.vsix "extension/*" &&\ +mv extension /opt/code-oss/extensions/java-maven + +RUN wget https://open-vsx.org/api/vscjava/vscode-java-test/0.35.0/file/vscjava.vscode-java-test-0.35.0.vsix && \ +unzip vscjava.vscode-java-test-0.35.0.vsix "extension/*" &&\ +mv extension /opt/code-oss/extensions/java-test \ No newline at end of file diff --git a/labs/cloudbuild-scheduled-jobs/code-oss-java/cloudbuild.yaml b/labs/cloudbuild-scheduled-jobs/code-oss-java/cloudbuild.yaml new file mode 100644 index 0000000..503ff0c --- /dev/null +++ b/labs/cloudbuild-scheduled-jobs/code-oss-java/cloudbuild.yaml @@ -0,0 +1,9 @@ +steps: + +# build +- id: "build" + name: 'gcr.io/cloud-builders/docker' + args: ['build', '-t', '${_REGION}-docker.pkg.dev/${PROJECT_ID}/${_AR_REPO_NAME}/${_AR_IMAGE_NAME}', './${_IMAGE_DIR}'] + waitFor: ['-'] +images: +- '${_REGION}-docker.pkg.dev/${PROJECT_ID}/${_AR_REPO_NAME}/${_AR_IMAGE_NAME}' diff --git a/labs/cloudrun-progression/Dockerfile b/labs/cloudrun-progression/Dockerfile new file mode 100644 index 0000000..5344650 --- /dev/null +++ b/labs/cloudrun-progression/Dockerfile @@ -0,0 +1,32 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Use the official lightweight Python image. +# https://hub.docker.com/_/python +FROM python:3.7-slim + +# Copy local code to the container image. +ENV APP_HOME /app +WORKDIR $APP_HOME +COPY . ./ + +# Install production dependencies. +RUN pip install Flask gunicorn + +# Run the web service on container startup. Here we use the gunicorn +# webserver, with one worker process and 8 threads. +# For environments with multiple CPU cores, increase the number of workers +# to be equal to the cores available. +CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 app:app \ No newline at end of file diff --git a/labs/cloudrun-progression/README.md b/labs/cloudrun-progression/README.md new file mode 100644 index 0000000..40cbf57 --- /dev/null +++ b/labs/cloudrun-progression/README.md @@ -0,0 +1,462 @@ +# Canary Deployments with Cloud Run and Cloud Build + +## Overview + +This document shows you how to implement a deployment pipeline for Cloud Run that implements progression of code from developer branches to production with automated canary testing and percentage based traffic management. It is intended for platform administrators who are responsible for creating and managing CI/CD pipelines. This document assumes that you have a basic understanding of Git, Cloud Run, and CI/CD pipeline concepts.  + +Cloud Run lets you deploy and run your applications with little overhead or effort. Many organizations use robust release pipelines to move code into production. Cloud Run provides unique traffic management capabilities that let you implement advanced release management techniques with little effort. + +### Objectives  + +- Create your Cloud Run service +- Enable developer branch +- Implement canary testing +- Rollout safely to production + +### Costs + +This tutorial uses billable components of Google Cloud, including: + +- [Cloud Run](https://cloud.google.com/run/pricing) +- [Cloud Build](https://cloud.google.com/build/pricing) + +Use the [Pricing Calculator](https://cloud.google.com/products/calculator) to generate a cost estimate based on your projected usage. + +### Before you begin + +1. Create a Google Cloud project. + +[GO TO THE MANAGE RESOURCES PAGE](https://console.cloud.google.com/cloud-resource-manager) + +2. Enable billing for your project. + +[ENABLE BILLING](https://support.google.com/cloud/answer/6293499#enable-billing) + +3. In Cloud Console, go to [Cloud Shell](https://cloud.google.com/shell/docs/how-cloud-shell-works) to execute the commands listed in this tutorial. + +[GO TO CLOUD](https://console.cloud.google.com/?cloudshell=true&_ga=2.130021388.-1258454239.1533315939&_gac=1.201996835.1554234100.CKPetZmVsuECFUfNDQodJ-UGUQ) SHELL + +At the bottom of the Cloud Console, a [Cloud Shell](https://cloud.google.com/shell/docs/how-cloud-shell-works) session opens and displays a command-line prompt. Cloud Shell is a shell environment with the Cloud SDK already installed, including the [gcloud](https://cloud.google.com/sdk/gcloud) command-line tool, and with values already set for your current project. It can take a few seconds for the session to initialize. + +When you finish this tutorial, you can avoid continued billing by deleting the resources you created. See [Cleaning up](https://docs.google.com/document/d/1yCbt_zPaWJ7u59xWgd1KuxMlNr1juBjjYo7WASIQeL0/edit?resourcekey=0-a--Q0cYLuCdecVW6PKWmJg#heading=h.mlrdlgcohh7k) for more detail. + + +## Preparing Your Environment + +### Set Project Variables +1. In Cloud Shell, create environment variables to use throughout this tutorial: + +```sh +export PROJECT_ID=$(gcloud config get-value project) +export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)') +``` + + +### Enable APIs + +Enable the following APIs on your project + +- Cloud Resource Manager +- Cloud Build +- Container Registry +- Cloud Run + +```sh +gcloud services enable \ +cloudresourcemanager.googleapis.com \ +container.googleapis.com \ +secretmanager.googleapis.com \ +cloudbuild.googleapis.com \ +containerregistry.googleapis.com \ +run.googleapis.com +``` + + +### Grant Rights +Grant the Cloud Run Admin role (roles/run.admin) to the Cloud Build service account: + +```sh +gcloud projects add-iam-policy-binding $PROJECT_ID \ +--member=serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \ +--role=roles/run.admin +``` + +Grant the IAM Service Account User role (`roles/iam.serviceAccountUser`) to the Cloud Build service account for the Cloud Run runtime service account: + +```sh +gcloud iam service-accounts add-iam-policy-binding \ +$PROJECT_NUMBER-compute@developer.gserviceaccount.com \ +--member=serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com \ +--role=roles/iam.serviceAccountUser +``` + + + + +### Set Git values + +If you haven't used Git in Cloud Shell previously, set the `user.name` and `user.email` values that you want to use: + +```sh +git config --global user.email "[YOUR_EMAIL_ADDRESS]" +git config --global user.name "[YOUR_USERNAME]" +git config --global credential.helper store +``` + +If you're using MFA with GitHub, create a personal access token and use it as your password when interacting with GitHub through the command-line +- Follow [this link to create an access token](https://github.com/settings/tokens/new?scopes=repo%2Cread%3Auser%2Cread%3Aorg%2Cuser%3Aemail%2Cwrite&description=Cloud%20Run%20Tutorial) +- Leave the tab open + +Store your GitHub ID in an environment variable for easier access + +```sh +export GH_USER= +``` + +### Fork the project repo + +First fork the sample repo into your GitHub account [through the GitHub UI](https://github.com/GoogleCloudPlatform/software-delivery-workshop/fork). + +### Clone The Project Repo + +Clone and prepare the sample repository: + +```sh +git clone https://github.com/$GH_USER/software-delivery-workshop.git cloudrun-progression + +cd cloudrun-progression/labs/cloudrun-progression +``` + + + + +## Connecting Your Git Repo + +Cloud Build enables you to create and manage connections to source code repositories using the Google Cloud console. You can [create and manage connections](https://cloud.google.com/build/docs/repositories) using Cloud Build repositories (1st gen) or Cloud Build repositories (2nd gen). For this tutorial you will utilize Cloud Build repositories (2nd gen) to connect your GitHub repo and access a the sample source repo. + +### Grant Required Permissions + +To connect your GitHub host, grant the Cloud Build Connection Admin (`roles/cloudbuild.connectionAdmin`) role to your user account +```sh +PN=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)") +CLOUD_BUILD_SERVICE_AGENT="service-${PN}@gcp-sa-cloudbuild.iam.gserviceaccount.com" +gcloud projects add-iam-policy-binding ${PROJECT_ID} \ + --member="serviceAccount:${CLOUD_BUILD_SERVICE_AGENT}" \ + --role="roles/secretmanager.admin" +``` + +### Create the Host connection + +Configure the Cloud Build Repository connection by running the command below. + +```sh +gcloud alpha builds connections create github $GH_USER --region=us-central1 +``` + +Click the link provided in the output and follow the onscreen instructions to complete the connection. + + +Verify the installation of your GitHub connection by running the following command: + +``` +gcloud alpha builds connections describe $GH_USER --region=us-central1 +``` + +### Link the specific repository + +Using the host connection you just configured, link in the sample repo you forked + +```sh + gcloud alpha builds repositories create cloudrun-progression \ + --remote-uri=https://github.com/$GH_USER/software-delivery-workshop.git \ + --connection=$GH_USER \ + --region=us-central1 +``` + +### Set Repository Name variable + +Store the Repository name for later use + +```sh +export REPO_NAME=projects/$PROJECT_ID/locations/us-central1/connections/$GH_USER/repositories/cloudrun-progression +``` + + +## Deploying Your Cloud Run Service + +In this section, you build and deploy the initial production application that you use throughout this tutorial. + +### Deploy the service +1. In Cloud Shell, build and deploy the application, including a service that requires authentication. To make a public service use the --allow-unauthenticated flag as [noted in the documentation](https://cloud.google.com/run/docs/authenticating/public). + +```sh +gcloud builds submit --tag gcr.io/$PROJECT_ID/hello-cloudrun + +gcloud run deploy hello-cloudrun \ +--image gcr.io/$PROJECT_ID/hello-cloudrun \ +--platform managed \ +--region us-central1 \ +--tag=prod -q +``` + + + +The output looks like the following: + + ``` + Deploying container to Cloud Run service [hello-cloudrun] in project [sdw-mvp6] region [us-central1] +✓ Deploying new service... Done.                                                            +  ✓ Creating Revision... +  ✓ Routing traffic... +Done. +Service [hello-cloudrun] revision [hello-cloudrun-00001-tar] has been deployed and is serving 100 percent of traffic. +Service URL: https://hello-cloudrun-apwaaxltma-uc.a.run.app +The revision can be reached directly at https://prod---hello-cloudrun-apwaaxltma-uc.a.run.app +``` + +The output includes the service URL and a unique URL for the revision. Your values will differ slightly from what's indicated here.  + +### Validate the deploy +2. After the deployment is complete, view the newly deployed service on the [Revisions page](https://console.cloud.google.com/run/detail/us-central1/hello-cloudrun/revisions) in the Cloud Console. + +4. In Cloud Shell, view the authenticated service response: + + ```sh +PROD_URL=$(gcloud run services describe hello-cloudrun --platform managed --region us-central1 --format=json | jq --raw-output ".status.url") + +echo $PROD_URL + +curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" $PROD_URL +``` + + +## Enabling Branch Deployments + +### Setup Branch Trigger + +In this section, you enable developers with a unique URL for development branches in Git. Each branch is represented by a URL identified by the branch name. Commits to the branch trigger a deployment, and the updates are accessible at that same URL. + +1. In Cloud Shell, set up the trigger: +```sh +gcloud alpha builds triggers create github \ +--name=branchtrigger \ +--repository=$REPO_NAME \ +--branch-pattern='[^(?!.*main)].*' \ +--build-config=labs/cloudrun-progression/branch-cloudbuild.yaml \ +--region=us-central1 +``` + +2. To review the trigger, go to the [Cloud Build Triggers page](https://console.cloud.google.com/cloud-build/triggers;region=us-central1) in the Cloud Console: + +### Create changes on a branch +1. In Cloud Shell, create a new branch: +```sh +git checkout -b new-feature-1 +``` + +4. Open the sample application using your favorite editor or using the Cloud Shell IDE: +```sh +edit app.py +``` + +5. In the sample application, modify line 24 to indicate v1.1 instead of v1.0: +```python +@app.route('/') + +def hello_world(): +    return 'Hello World v1.1' + +``` + +6. To return to your terminal, click Open Terminal. + +### Execute the branch trigger +1. In Cloud Shell, commit the change and push to the remote repository: + +```sh +git add . && git commit -m "updated" && git push origin new-feature-1 +``` + +> NOTE: Use the personal access token you created earlier for the password if you have 2FA enabled on GitHub + +1. To review the build in progress, go to the [Cloud Build Builds page](https://console.cloud.google.com/cloud-build/builds) in the Cloud Console. +2. After the build completes, to review the new revision, go to the [Cloud Run Revisions page](https://console.cloud.google.com/run/detail/us-central1/hello-cloudrun/revisions) in the Cloud Console: +3. In Cloud Shell, get the unique URL for this branch: +```sh +BRANCH_URL=$(gcloud run services describe hello-cloudrun --platform managed --region us-central1 --format=json | jq --raw-output ".status.traffic[] | select (.tag==\"new-feature-1\")|.url") + +echo $BRANCH_URL +``` + + +11. Access the authenticated URL: +```sh +curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" $BRANCH_URL +``` + +The updated response output looks like the following: + +``` +Hello World v1.1 +``` + + + +# Automating Canary Testing + +When code is released to production, it's common to release code to a small subset of live traffic before migrating all traffic to the new code base.  + +In this section, you implement a trigger that is activated when code is committed to the main branch. The trigger deploys the code to a unique canary URL and it routes 10% of all live traffic to the new revision.  + +1. In Cloud Shell, set up the branch trigger: +```sh +gcloud alpha builds triggers create github \ + --name=maintrigger \ + --repository=$REPO_NAME \ + --branch-pattern=main \ + --build-config=labs/cloudrun-progression/main-cloudbuild.yaml \ + --region=us-central1 +``` + +2. To review the new trigger, go to the [Cloud Build Triggers page](https://console.cloud.google.com/cloud-build/triggers) in the Cloud Console. +3. In Cloud Shell, merge the branch to the main line and push to the remote repository: +```sh +git checkout main +git merge new-feature-1 +git push origin main +``` + + +4. To review the build in progress, go to the [Cloud Build Builds page](https://console.cloud.google.com/cloud-build/builds) in the Cloud Console. +5. After the build is complete, to review the new revision, go to the [Cloud Run Revisions page](https://console.cloud.google.com/run/detail/us-central1/hello-cloudrun/revisions) in the Cloud Console. Note that 90% of the traffic is routed to prod, 10% to canary, and 0% to the branch revisions. + + + +Review the key lines of `main-cloudbuild.yaml` that implement the logic for the canary deploy. + +Lines 39-45 deploy the new revision and use the tag flag to route traffic from the unique canary URL: +``` +gcloud run deploy ${_SERVICE_NAME} \ +--platform managed \ +--region ${_REGION} \ +--image gcr.io/${PROJECT_ID}/${_SERVICE_NAME} \ +--tag=canary \ +--no-traffic +``` + + +Line 61 adds a static tag to the revision that notes the Git short SHA of the deployment: +``` +gcloud beta run services update-traffic  ${_SERVICE_NAME} --update-tags=sha-$SHORT_SHA=$${CANARY}  --platform managed  --region ${_REGION} +``` + + Line 62 updates the traffic to route 90% to production and 10% to canary: +``` +gcloud run services update-traffic  ${_SERVICE_NAME} --to-revisions=$${PROD}=90,$${CANARY}=10  --platform managed  --region ${_REGION} +``` + + +1. In Cloud Shell, get the unique URL for the canary revision: + +```sh +CANARY_URL=$(gcloud run services describe hello-cloudrun --platform managed --region us-central1 --format=json | jq --raw-output ".status.traffic[] | select (.tag==\"canary\")|.url") + +echo $CANARY_URL +``` + +8. Review the canary endpoint directly: + +```sh +curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" $CANARY_URL +``` + +9. To see percentage-based responses, make a series of requests: +```sh +LIVE_URL=$(gcloud run services describe hello-cloudrun --platform managed --region us-central1 --format=json | jq --raw-output ".status.url") +for i in {0..20};do + curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" $LIVE_URL; echo \n +done +``` + + +# Releasing to Production + +After the canary deployment is validated with a small subset of traffic, you release the deployment to the remainder of the live traffic.  + +In this section, you set up a trigger that is activated when you create a tag in the repository. The trigger migrates 100% of traffic to the already deployed revision based on the commit SHA of the tag. Using the commit sha ensures the revision validated with canary traffic is the revision utilized for the remainder of production traffic.  + +1. In Cloud Shell, set up the tag trigger: +```sh +gcloud alpha builds triggers create github \ + --name=tagtrigger \ + --repository=$REPO_NAME \ + --tag-pattern=. \ + --build-config=labs/cloudrun-progression/tag-cloudbuild.yaml \ + --region=us-central1 +``` + +2. To review the new trigger, go to the [Cloud Build Triggers page](https://console.cloud.google.com/cloud-build/triggers) in the Cloud Console. +3. In Cloud Shell, create a new tag and push to the remote repository: +```sh +git tag 1.1 +git push origin 1.1 +``` + +4. To review the build in progress, go to the [Cloud Build Builds page](https://console.cloud.google.com/cloud-build/builds) in the Cloud Console. + +5. After the build is complete, to review the new revision, go to the [Cloud Run Revisions page](https://console.cloud.google.com/run/detail/us-central1/hello-cloudrun/revisions) in the Cloud Console. Note that the revision is updated to indicate the prod tag and it is serving 100% of live traffic. + + + +6. In Cloud Shell, to see percentage-based responses, make a series of requests: + +```sh +LIVE_URL=$(gcloud run services describe hello-cloudrun --platform managed --region us-central1 --format=json | jq --raw-output ".status.url") + +for i in {0..20};do + curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" $LIVE_URL; echo \n +done +``` + + +7. Review the key lines of `tag-cloudbuild.yaml` that implement the production deployment logic.  + +Line 37 updates the canary revision adding the prod tag. The deployed revision is now tagged for both prod and canary: + +``` +gcloud beta run services update-traffic  ${_SERVICE_NAME} --update-tags=prod=$${CANARY}  --platform managed  --region ${_REGION} +``` + +Line 39 updates the traffic for the base service URL to route 100% of traffic to the revision tagged as prod: + +``` +gcloud run services update-traffic  ${_SERVICE_NAME} --to-revisions=$${NEW_PROD}=100  --platform managed  --region ${_REGION} +``` + + +# Cleaning up + +To avoid incurring charges to your Google Cloud Platform account for the resources used in this tutorial: + +### Delete the project + +The easiest way to eliminate billing is to delete the project you created for the tutorial. + +Caution: Deleting a project has the following effects: + +- Everything in the project is deleted. If you used an existing project for this tutorial, when you delete it, you also delete any other work you've done in the project. +- Custom project IDs are lost. When you created this project, you might have created a custom project ID that you want to use in the future. To preserve the URLs that use the project ID, such as an appspot.com URL, delete selected resources inside the project instead of deleting the whole project. + +If you plan to explore multiple tutorials and quickstarts, reusing projects can help you avoid exceeding project quota limits. + +1. In the Cloud Console, go to the Manage resources page. + [Go to the Manage resources page](https://console.cloud.google.com/iam-admin/projects) +2. In the project list, select the project that you want to delete and then click Delete ![](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAABGCAYAAACDkrchAAAAAXNSR0IArs4c6QAAAe9JREFUaEPtWctKQlEUXdcH+BnVT1SD1LRxeiUoatCwbvgbEQVmFASJEUFEX1GNKkPKhg6tT3DiAzSuEGgonu095yKyznjt11p7b6/nWN1ut4sZOhYLmnI1qdCUCwQqRIV8ZoAt5zPh4nBUSEyZzwZUyGfCxeGokJgynw2okM+Ei8MZUajZbMJxsnh8eh6bUCQSQeHqEtHoylisCmCigtx/7a1Wa6j/RqOB/YMsXl/eVOL3MOFwGOcXZ0jEY0NtgsEgQqGQkr+JCqpUvpC2N5QC6ADF4lHc3lwruWJBLk1USKlZRoOMt5zH/IyaTzRDfxl1Oh20221jCQYCgd4GlBxPBZVK79jc2pHEE2GXlhfxcH8nsmFB/XRRIVHzAGw5wNvNKVuOLce1zRkamAIuBS4FLgUuBS4F4R4YgPPjlB+n//qHP6zCgeIMcYZmfYZqtRpi8TXhZKjD7fQ68vmcuoHXlnPfiWKrSXzXfkRBVcGnuRNkMrYqvIfzdNHoOiiXy9je2R35ACbKpg+cTMRRLBZgWZbIheeC3GjVahWHR8f4/KigXq+LEugHu3fZ8wtzsFMpOM4e3Jc76dFSkDSoSTwLMsmuDt9USAeLJn1QIZPs6vBNhXSwaNIHFTLJrg7fVEgHiyZ9UCGT7OrwPXMK/QL+cgFNd5b6egAAAABJRU5ErkJggg==). +3. In the dialog, type the project ID and then click Shut down to delete the project. + +# What's next + +- Review [Managing Revisions with Cloud Run](https://cloud.google.com/run/docs/managing/revisions). +- Review Cloud Run [Rollbacks, gradual rollouts, and traffic migration](https://cloud.google.com/run/docs/rollouts-rollbacks-traffic-migration) +- Review [Using tags for accessing revisions](https://cloud.google.com/run/docs/rollouts-rollbacks-traffic-migration#tags). +- Review [Creating and managing build triggers](https://cloud.google.com/build/docs/automating-builds/create-manage-triggers) in Cloud Build. \ No newline at end of file diff --git a/labs/cloudrun-progression/app.py b/labs/cloudrun-progression/app.py new file mode 100644 index 0000000..c3cf55d --- /dev/null +++ b/labs/cloudrun-progression/app.py @@ -0,0 +1,27 @@ +#!/usr/bin/python +# +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os + +from flask import Flask + +app = Flask(__name__) + +@app.route('/') +def hello_world(): + return 'Hello World v1.0' + +if __name__ == "__main__": + app.run(debug=True,host='0.0.0.0',port=int(os.environ.get('PORT', 8080))) \ No newline at end of file diff --git a/labs/cloudrun-progression/branch-cloudbuild.yaml b/labs/cloudrun-progression/branch-cloudbuild.yaml new file mode 100644 index 0000000..d79ec9c --- /dev/null +++ b/labs/cloudrun-progression/branch-cloudbuild.yaml @@ -0,0 +1,47 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Default Values +substitutions: + _SERVICE_NAME: hello-cloudrun + _REGION: us-central1 + +steps: + +### Build + - id: "build image" + name: "gcr.io/cloud-builders/docker" + args: ["build", "-t", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}", "labs/cloudrun-progression"] + +### Push + - id: "push image" + name: "gcr.io/cloud-builders/docker" + args: ["push", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"] + +### Deploy + - id: "deploy prod service" + name: "gcr.io/google.com/cloudsdktool/cloud-sdk" + entrypoint: "bash" + args: + - '-c' + - | + gcloud run deploy ${_SERVICE_NAME} \ + --platform managed \ + --region ${_REGION} \ + --image gcr.io/${PROJECT_ID}/${_SERVICE_NAME} \ + --tag=${BRANCH_NAME} \ + --no-traffic + + + diff --git a/labs/cloudrun-progression/branch-trigger.json-tmpl b/labs/cloudrun-progression/branch-trigger.json-tmpl new file mode 100644 index 0000000..2a6ee51 --- /dev/null +++ b/labs/cloudrun-progression/branch-trigger.json-tmpl @@ -0,0 +1,11 @@ +{ + "triggerTemplate": { + "projectId": "PROJECT", + "repoName": "cloudrun-progression", + "branchName": "[^(?!.*master)].*" + }, + "name": "branch", + "description": "Trigger dev build/deploy for any branch other than master", + + "filename": "branch-cloudbuild.yaml" + } \ No newline at end of file diff --git a/labs/cloudrun-progression/main-cloudbuild.yaml b/labs/cloudrun-progression/main-cloudbuild.yaml new file mode 100644 index 0000000..7a281cf --- /dev/null +++ b/labs/cloudrun-progression/main-cloudbuild.yaml @@ -0,0 +1,62 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Default Values +substitutions: + _SERVICE_NAME: hello-cloudrun + _REGION: us-central1 + +steps: + +### Build + - id: "build image" + name: "gcr.io/cloud-builders/docker" + args: ["build", "-t", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}", "."] + +### Push + - id: "push image" + name: "gcr.io/cloud-builders/docker" + args: ["push", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"] + +### Deploy + - id: "deploy canary service" + name: "gcr.io/google.com/cloudsdktool/cloud-sdk" + entrypoint: "bash" + args: + - '-c' + - | + gcloud run deploy ${_SERVICE_NAME} \ + --platform managed \ + --region ${_REGION} \ + --image gcr.io/${PROJECT_ID}/${_SERVICE_NAME} \ + --tag=canary \ + --no-traffic + + + # Route Traffic + - id: "route prod traffic" + name: "gcr.io/google.com/cloudsdktool/cloud-sdk" + entrypoint: "bash" + args: + - '-c' + - | + apt-get install -y jq + export CANARY=$$(gcloud run services describe hello-cloudrun --platform managed --region ${_REGION} --format=json | jq --raw-output ".spec.traffic[] | select (.tag==\"canary\")|.revisionName") + export PROD=$$(gcloud run services describe hello-cloudrun --platform managed --region ${_REGION} --format=json | jq --raw-output ".spec.traffic[] | select (.tag==\"prod\")|.revisionName") + + echo SHORT_SHA is $SHORT_SHA + echo Canary is $${CANARY} + echo gcloud beta run services update-traffic ${_SERVICE_NAME} --update-tags=sha-$SHORT_SHA=$${CANARY} --platform managed --region ${_REGION} + gcloud beta run services update-traffic ${_SERVICE_NAME} --update-tags=sha-$SHORT_SHA=$${CANARY} --platform managed --region ${_REGION} + gcloud run services update-traffic ${_SERVICE_NAME} --to-revisions=$${PROD}=90,$${CANARY}=10 --platform managed --region ${_REGION} \ No newline at end of file diff --git a/labs/cloudrun-progression/master-cloudbuild.yaml b/labs/cloudrun-progression/master-cloudbuild.yaml new file mode 100644 index 0000000..edee628 --- /dev/null +++ b/labs/cloudrun-progression/master-cloudbuild.yaml @@ -0,0 +1,61 @@ +# Copyright 2020 Google LLC + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # https://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + + # Default Values + substitutions: + _SERVICE_NAME: hello-cloudrun + _REGION: us-central1 + + steps: + + ### Build + - id: "build image" + name: "gcr.io/cloud-builders/docker" + args: ["build", "-t", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}", "."] + + ### Push + - id: "push image" + name: "gcr.io/cloud-builders/docker" + args: ["push", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"] + + ### Deploy + - id: "deploy canary service" + name: "gcr.io/google.com/cloudsdktool/cloud-sdk" + entrypoint: "bash" + args: + - '-c' + - | + gcloud run deploy ${_SERVICE_NAME} \ + --platform managed \ + --region ${_REGION} \ + --image gcr.io/${PROJECT_ID}/${_SERVICE_NAME} \ + --tag=canary \ + --no-traffic + + + # Route Traffic + - id: "route prod traffic" + name: "gcr.io/google.com/cloudsdktool/cloud-sdk" + entrypoint: "bash" + args: + - '-c' + - | + apt-get install -y jq + export CANARY=$$(gcloud run services describe hello-cloudrun --platform managed --region ${_REGION} --format=json | jq --raw-output ".spec.traffic[] | select (.tag==\"canary\")|.revisionName") + export PROD=$$(gcloud run services describe hello-cloudrun --platform managed --region ${_REGION} --format=json | jq --raw-output ".spec.traffic[] | select (.tag==\"prod\")|.revisionName") + echo SHORT_SHA is $SHORT_SHA + echo Canary is $${CANARY} + echo gcloud beta run services update-traffic ${_SERVICE_NAME} --update-tags=sha-$SHORT_SHA=$${CANARY} --platform managed --region ${_REGION} + gcloud beta run services update-traffic ${_SERVICE_NAME} --update-tags=sha-$SHORT_SHA=$${CANARY} --platform managed --region ${_REGION} + gcloud run services update-traffic ${_SERVICE_NAME} --to-revisions=$${PROD}=90,$${CANARY}=10 --platform managed --region ${_REGION} \ No newline at end of file diff --git a/labs/cloudrun-progression/master-trigger.json-tmpl b/labs/cloudrun-progression/master-trigger.json-tmpl new file mode 100644 index 0000000..84ad5ba --- /dev/null +++ b/labs/cloudrun-progression/master-trigger.json-tmpl @@ -0,0 +1,11 @@ +{ + "triggerTemplate": { + "projectId": "PROJECT", + "repoName": "cloudrun-progression", + "branchName": "master" + }, + "name": "master", + "description": "Trigger canary build/deploy for any commit to the master branch", + + "filename": "master-cloudbuild.yaml" + } \ No newline at end of file diff --git a/labs/cloudrun-progression/tag-cloudbuild.yaml b/labs/cloudrun-progression/tag-cloudbuild.yaml new file mode 100644 index 0000000..009ca02 --- /dev/null +++ b/labs/cloudrun-progression/tag-cloudbuild.yaml @@ -0,0 +1,41 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Default Values +substitutions: + _SERVICE_NAME: hello-cloudrun + _REGION: us-central1 + +steps: + + # Route Traffic + - id: "route prod traffic" + name: "gcr.io/google.com/cloudsdktool/cloud-sdk" + entrypoint: "bash" + args: + - '-c' + - | + apt-get install -y jq + export CANARY=$$(gcloud run services describe hello-cloudrun --platform managed --region ${_REGION} --format=json | jq --raw-output ".spec.traffic[] | select (.tag==\"canary\")|.revisionName") + export PROD=$$(gcloud run services describe hello-cloudrun --platform managed --region ${_REGION} --format=json | jq --raw-output ".spec.traffic[] | select (.tag==\"prod\")|.revisionName") + + + echo gcloud beta run services update-traffic ${_SERVICE_NAME} --update-tags=prod=$${CANARY} --platform managed --region ${_REGION} + echo gcloud run services update-traffic ${_SERVICE_NAME} --to-revisions=$${PROD}=100 --platform managed --region ${_REGION} + + gcloud beta run services update-traffic ${_SERVICE_NAME} --update-tags=prod=$${CANARY} --platform managed --region ${_REGION} + export NEW_PROD=$$(gcloud run services describe hello-cloudrun --platform managed --region ${_REGION} --format=json | jq --raw-output ".spec.traffic[] | select (.tag==\"prod\")|.revisionName") + gcloud run services update-traffic ${_SERVICE_NAME} --to-revisions=$${NEW_PROD}=100 --platform managed --region ${_REGION} + + diff --git a/labs/cloudrun-progression/tag-trigger.json-tmpl b/labs/cloudrun-progression/tag-trigger.json-tmpl new file mode 100644 index 0000000..d8daac2 --- /dev/null +++ b/labs/cloudrun-progression/tag-trigger.json-tmpl @@ -0,0 +1,11 @@ +{ + "triggerTemplate": { + "projectId": "PROJECT", + "repoName": "cloudrun-progression", + "tagName": ".*" + }, + "name": "tag", + "description": "Migrate from canary to prod triggered by creation of any tag", + + "filename": "tag-cloudbuild.yaml" + } \ No newline at end of file diff --git a/labs/developing-with-cloud-code/4dwlgh6avps.png b/labs/developing-with-cloud-code/4dwlgh6avps.png new file mode 100644 index 0000000..f2275cb Binary files /dev/null and b/labs/developing-with-cloud-code/4dwlgh6avps.png differ diff --git a/labs/developing-with-cloud-code/61jmwjqwlhk.png b/labs/developing-with-cloud-code/61jmwjqwlhk.png new file mode 100644 index 0000000..5e811a6 Binary files /dev/null and b/labs/developing-with-cloud-code/61jmwjqwlhk.png differ diff --git a/labs/developing-with-cloud-code/README.md b/labs/developing-with-cloud-code/README.md new file mode 100644 index 0000000..a7c5aff --- /dev/null +++ b/labs/developing-with-cloud-code/README.md @@ -0,0 +1,161 @@ +# Developing with Cloud Code + +# Objectives + +In this lab you will: + +- Explore Cloud Code Plugins +- Deploy to Kubernetes cluster +- Stream kubernetes Logs +- Utilize hot reloading of changes +- Debug live Kubernetes apps + +# Preparing your workspace + +## Clone the app + +To clone the repository and open it in your development environment: + +1. Open Cloud Shell editor by visiting the following url + +``` +https://ide.cloud.google.com +``` + +1. In the terminal window clone the application source with the following command: + +``` +git clone https://github.com/viglesiasce/sample-app.git -b golden-path +``` + +1. Change into the directory and set the IDE workspace to the repo root + +``` +cd sample-app && cloudshell workspace . +``` + +## Start Minikube + +In this section, you build, test, deploy, and access your application using a local version of kubernetes called Minikube. + +1. In the terminal, start minikube by running: + +``` +minikube start +``` + +> Minikube sets up a local Kubernetes cluster in your Cloud Shell, this setup will take a few minutes. While it is starting, take a moment to review the various interfaces provided by Cloud Code in the next step. + +# Exploring Cloud Code Plugin + +Cloud Code provides IDE support for the full development cycle of Kubernetes and Cloud Run applications, from creating an application from an existing template to monitoring your deployed app's resources. You'll be using different commands and views provided by Cloud Code. In this step you will get acquainted with the basic interfaces. + +1. Review the various explorer views from the activity bar + + Multiple user interface panels are accessible from the Activity bar. To briefly get acquainted with the various view click on the icons for each view + + API Explorer: + + - Click the Cloud Code - Cloud APIs icon Cloud Code from the Activity bar. Additional details for working in this view are available in the [documentation](https://cloud.google.com/code/docs/vscode/client-libraries). + + + ![image](oepub6vk28.png) + + Secret Manager Explorer: + + - Click on the Secret Manager view  in the Activity bar. Additional details for for working in this view are available in the [documentation](https://cloud.google.com/code/docs/vscode/secret-manager) + + ![image](imias3q32ah.png) + + Cloud Run Explorer: + + - Navigate to the Cloud Run Explorer using the Cloud Run icon in the Activity bar on the left . Additional details for for working in this view are available in the [documentation](https://cloud.google.com/code/docs/vscode/cloud-run-overview) + + ![image](4dwlgh6avps.png) + + Kubernetes explorer: + + - Navigate to the Kubernetes Explorer using the icon in the Activity bar on the left . Additional details for for working in this view are available in the [documentation](https://cloud.google.com/code/docs/vscode/k8s-overview) + + ![image](mz5gbbsj9lq.png) + +2. Review common commands available from the status bar + + Frequently used commands can be accessed quickly through the indicator in the status bar. + + - Locate the Cloud Code extension indicator in the status bar and click on it. + ![image](zygset0j2tn.png) + - Review the various commands available to run and debug on CloudRun and Kubernetes + - Click on Open Welcome Page for additional details and sample activities + +3. Review Commands available in the command pallet + + Additional commands are available from the command pallet. Review the list of commands you can access. + + - Open the Command Palette (press Ctrl/Cmd+Shift+P) and then type Cloud Code to filter the available commands. + - Use your arrow keys to cycle through the list of commands. + +# Deploying to Kubernetes cluster + +In this section, you build, test, deploy, and access your application. + +1. In the pane at the bottom of Cloud Shell Editor, select Cloud Code  +![image](sq5ck29lduc.png) +2. In the panel that appears at the top, select Run on Kubernetes. If prompted, select Yes to use the minikube Kubernetes context. + +This command starts a build of the source code and then runs the tests. The build and tests will take a few minutes to run. These tests include unit tests and a validation step that checks the rules that are set for the deployment environment. This validation step is already configured, and it ensures that you get warning of deployment issues even while you're still working in your development environment. + +3. Select the Output tab in the lower pane to view progress and notifications + ![image](wvofl4zlg6a.png) +4. Select "Kubernetes: Run/Debug - Detailed" in the channel drop down to the right to view additional details and logs streaming live from the containers +![image](x30hwes9pu.png) + +When the build and tests are done, the Output tab says: `Resource deployment/gceme-backend-dev status completed successfully`, and two URLs are listed. + +5. In the Cloud Code terminal, hover over the first URL in the output (http://localhost:8080), and then in the tool tip that appears select Open Web Preview. + +The local version of the application opens in your browser. This version of the app is running in minikube's Kubernetes cluster. + +6. In your browser, refresh the page. The number next to Counter increases, showing that the app is responding to your refresh. + +In your browser, keep this page open so that you can view the application as you make any changes in your local environment. + +# Utilize hot reloading of changes + +In this section, you make a change to the application, and view the change as the app runs in the local Kubernetes cluster. In the output tab for the Kubernetes: Run/Debug channel, in addition to the application urls, the output also says `Watching for changes.` This means that watch mode is enabled. While Cloud Code is in watch mode, Cloud Code will detect any saved changes in your repo and will automatically rebuild and redeploy the app with the latest changes. + +1. In Cloud Shell Editor, go to the main.go file. +2. In this main.go file, in line 23, change the color from green to blue. +3. Save the file. + +Cloud Code detects that the change to the app is saved, and it redeploys the change automatically. The Output tab shows Update initiated. This redeployment will take a few minutes to run. + +This automatic rebuild is similar to hot code reloading, which is a feature available for some application types and frameworks. + +4. When the build is done, go to your browser where you have the app open and refresh the page. + +When you refresh, the color at the top of the table changes from blue to green. + +This setup gives you this automatic reloading for any architecture, with any components. When using Cloud Code and minikube, anything that is running in Kubernetes has this hot code reloading functionality. + +# Debugging live Kubernetes apps + +You have run the application, made a change, and viewed the running app. In this section, you debug the application to be confident that it's ready to commit back into the main repo. + +For this debug example, we'll focus on the section of the code for the page counter. + +1. In Cloud Shell Editor, open the file main.go +2. Set a breakpoint in the application by clicking to the left number of line 82 (if err != nil {) +3. In the blue pane at the bottom of Cloud Shell Editor, select Cloud Code ![image](y1nu68cr85h.png) +4. In the panel that appears at the top, select Debug on Kubernetes. + +Cloud Code runs and attaches debuggers so that you'll be able to access the in-memory state of the application, not just the user-facing behavior of the application. + +5. At the end of the deploy process a prompt will appear at the top of your window asking to confirm the directory in the container where the application is deployed. ![image](tw2ka8wmpl.png) + + Verify the value is set to /go/src/app and hit enter to accept the value +6. Wait for the debugger to finish deploying. You'll know it's complete when the status bar turns orange and the output reports `"Attached debugger to container "sample-app-dev-..." successfully`." +7. In the Cloud Code terminal, hover over the first URL in the output (http://localhost:8081), and then in the tool tip that appears select Open Web Preview. The page won't finish loading which is expected. +8. Switch back to the IDE where the debugger now appears. Code comes up in the tab, and you'll see the call stack, what variables are available at that part of the code. You can expand the Variables - Local to see here the current counter variable value. +9. To allow the page to continue to load, select the "Continue" icon in the debugging window ![image](fyg1yamuy0d.png) +10. When you're finished debugging click the stop button to terminate each of the running threads. ![image](61jmwjqwlhk.png) \ No newline at end of file diff --git a/labs/developing-with-cloud-code/fyg1yamuy0d.png b/labs/developing-with-cloud-code/fyg1yamuy0d.png new file mode 100644 index 0000000..a69b795 Binary files /dev/null and b/labs/developing-with-cloud-code/fyg1yamuy0d.png differ diff --git a/labs/developing-with-cloud-code/imias3q32ah.png b/labs/developing-with-cloud-code/imias3q32ah.png new file mode 100644 index 0000000..9717a61 Binary files /dev/null and b/labs/developing-with-cloud-code/imias3q32ah.png differ diff --git a/labs/developing-with-cloud-code/mz5gbbsj9lq.png b/labs/developing-with-cloud-code/mz5gbbsj9lq.png new file mode 100644 index 0000000..f7c23bc Binary files /dev/null and b/labs/developing-with-cloud-code/mz5gbbsj9lq.png differ diff --git a/labs/developing-with-cloud-code/oepub6vk28.png b/labs/developing-with-cloud-code/oepub6vk28.png new file mode 100644 index 0000000..504c200 Binary files /dev/null and b/labs/developing-with-cloud-code/oepub6vk28.png differ diff --git a/labs/developing-with-cloud-code/sq5ck29lduc.png b/labs/developing-with-cloud-code/sq5ck29lduc.png new file mode 100644 index 0000000..28d59eb Binary files /dev/null and b/labs/developing-with-cloud-code/sq5ck29lduc.png differ diff --git a/labs/developing-with-cloud-code/tw2ka8wmpl.png b/labs/developing-with-cloud-code/tw2ka8wmpl.png new file mode 100644 index 0000000..6733a63 Binary files /dev/null and b/labs/developing-with-cloud-code/tw2ka8wmpl.png differ diff --git a/labs/developing-with-cloud-code/wvofl4zlg6a.png b/labs/developing-with-cloud-code/wvofl4zlg6a.png new file mode 100644 index 0000000..53ccc68 Binary files /dev/null and b/labs/developing-with-cloud-code/wvofl4zlg6a.png differ diff --git a/labs/developing-with-cloud-code/x30hwes9pu.png b/labs/developing-with-cloud-code/x30hwes9pu.png new file mode 100644 index 0000000..56df6dd Binary files /dev/null and b/labs/developing-with-cloud-code/x30hwes9pu.png differ diff --git a/labs/developing-with-cloud-code/y1nu68cr85h.png b/labs/developing-with-cloud-code/y1nu68cr85h.png new file mode 100644 index 0000000..28d59eb Binary files /dev/null and b/labs/developing-with-cloud-code/y1nu68cr85h.png differ diff --git a/labs/developing-with-cloud-code/zygset0j2tn.png b/labs/developing-with-cloud-code/zygset0j2tn.png new file mode 100644 index 0000000..28d59eb Binary files /dev/null and b/labs/developing-with-cloud-code/zygset0j2tn.png differ diff --git a/labs/gke-progression/README.md b/labs/gke-progression/README.md new file mode 100644 index 0000000..f5c121c --- /dev/null +++ b/labs/gke-progression/README.md @@ -0,0 +1,345 @@ +# Continuous deployment to Google Kubernetes Engine (GKE) with Cloud Build + + +## Overview + + +In this lab, you'll learn to set up a continuous delivery pipeline for GKE with Cloud Build. This lab highlights how to trigger Cloud Build jobs for different git events as well as a simple pattern for automated canary releases in GKE. + +You'll complete the following steps: + +* Create the GKE Application +* Automate deployments for git branches +* Automate deployments for git main branch +* Automating deployments for git tags + + +## Preparing your environment + +1. Create environment variables to use throughout this tutorial: + + ```sh + export PROJECT_ID=$(gcloud config get-value project) + export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)') + + export ZONE=us-central1-b + export CLUSTER=gke-progression-cluster + export APP_NAME=myapp + ``` + +2. Enable the following APIs: + + - Resource Manager + - GKE + - Cloud Source Repositories + - Cloud Build + - Container Registry + + ```sh + gcloud services enable \ + cloudresourcemanager.googleapis.com \ + container.googleapis.com \ + sourcerepo.googleapis.com \ + cloudbuild.googleapis.com \ + containerregistry.googleapis.com \ + --async + ``` + +3. Clone the sample source and switch to the lab directory: + + ```sh + git clone https://github.com/GoogleCloudPlatform/software-delivery-workshop.git gke-progression + + cd gke-progression/labs/gke-progression + rm -rf ../../.git + ``` + +4. Replace placeholder values in the sample repository with your `PROJECT_ID`: + + This command creates instances of the various config files unique to your current environment. + + ```sh + for template in $(find . -name '*.tmpl'); do envsubst '${PROJECT_ID} ${ZONE} ${CLUSTER} ${APP_NAME}' < ${template} > ${template%.*}; done + ``` + +5. Store the code from the sample repository in CSR: + + ```sh + gcloud source repos create gke-progression + git init + git config credential.helper gcloud.sh + git remote add gcp https://source.developers.google.com/p/$PROJECT_ID/r/gke-progression + git branch -m main + git add . && git commit -m "initial commit" + git push gcp main + ``` + +6. Create your GKE cluster. + + ```sh + gcloud container clusters create ${CLUSTER} \ + --project=${PROJECT_ID} \ + --zone=${ZONE} + ``` + +7. Give Cloud Build rights to your cluster. + + Cloud Build will be deploying the application to your GKE Cluster and will need rights to do so. + + ```sh + gcloud projects add-iam-policy-binding ${PROJECT_ID} \ + --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \ + --role=roles/container.developer + ``` + +Your environment is ready! + + +## Creating your GKE Application + +In this section, you build and deploy the initial production application that you use throughout this tutorial. + +1. Build the application with Cloud Build: + + ```sh + gcloud builds submit --tag gcr.io/$PROJECT_ID/$APP_NAME:1.0.0 src/ + ``` + +2. Manually deploy to Canary and Production environments: + + Create the production and canary deployments and services using the `kubectl apply` commands. + + ```sh + kubectl create ns production + kubectl apply -f k8s/deployments/prod -n production + kubectl apply -f k8s/deployments/canary -n production + kubectl apply -f k8s/services -n production + ``` + + The service deployed here will route traffic to both the canary and prod deployments. + + +3. Review number of running pods + + Confirm that you have four Pods running for the frontend, including three for production traffic and one for canary releases. That means that changes to your canary release will only affect 1 out of 4 (25%) of users. + + + ```sh + kubectl get pods -n production -l app=$APP_NAME -l role=frontend + ``` + + +4. Retrieve the external IP address for the production services. + + > **Note:** It can take several minutes before you see the load balancer external IP address. + + ```sh + kubectl get service $APP_NAME -n production + ``` + + Once the load balancer returns the IP address continue to the next step + +5. Store the external IP for later use. + + ```sh + export PRODUCTION_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=production services $APP_NAME) + ``` + +6. Review the application + + Check the version output of the service. It should read Hello World v1.0 + + ```sh + curl http://$PRODUCTION_IP + ``` + +Congratulations! You deployed the sample app! Next, you'll set up a pipeline for continuously and deploying your changes. + + +## Automating deployments for git branches + +In this section you will set up a trigger that will execute a Cloudbuild job on commit of any branch other than `main`. The Cloud Build file used here will automatically create a namespace and deployment for any existing or new branches, allowing developers to preview their code before integration with the main branch. + +1. Set up the trigger: + + The key component of this trigger is the use of the `branchName` parameter to match `main` and the `invertRegex` parameter which is set to true and alters the `branchName` pattern to match anything that is not `main`. For your reference you can find the following lines in `build/branch-trigger.json`. + + + + + ```console + "branchName": "main", + "invertRegex": true + ``` + + Additionally the last few lines of the Cloud Build file used with this trigger create a namespace named after the branch that triggered the job, then deploys the application and service within the new namespace. For your reference you can find the following lines in `build/branch-cloudbuild.yaml` + + + + + + ```console + kubectl get ns ${BRANCH_NAME} || kubectl create ns ${BRANCH_NAME} + kubectl apply --namespace ${BRANCH_NAME} --recursive -f k8s/deployments/dev + kubectl apply --namespace ${BRANCH_NAME} --recursive -f k8s/services + ``` + + + Now that you understand the mechanisms in use, create the trigger with the gcloud command below. + ```sh + gcloud beta builds triggers create cloud-source-repositories \ + --trigger-config build/branch-trigger.json + ``` + +2. To review the trigger, go to the + [Cloud Build Triggers page](https://console.cloud.google.com/cloud-build/triggers) + in the Console. + + [Go to Triggers](https://console.cloud.google.com/cloud-build/triggers) + +3. Create a new branch: + + ```sh + git checkout -b new-feature-1 + ``` + +4. Modify the code to indicate v1.1 + + Edit `src/app.py` and change the response from 1.0 to 1.1 + + ```py + @app.route('/') + def hello_world(): + return 'Hello World v1.1' + ``` + +5. Commit the change and push to the remote repository: + + ```sh + git add . && git commit -m "updated" && git push gcp new-feature-1 + ``` + + +6. To review the build in progress, go to the + [Cloud Build Builds page](https://console.cloud.google.com/cloud-build/builds) + in the Console. + + [Go to Builds](https://console.cloud.google.com/cloud-build/builds) + + Once the build completes continue to the next step + +7. Retrieve the external IP address for the newly deployed branch service. + + > **Note:** It can take several minutes before you see the load balancer external IP address. + + ```sh + kubectl get service $APP_NAME -n new-feature-1 + ``` + + Once the load balancer returns the IP address continue to the next step + +5. Store the external IP for later use. + + ```sh + export BRANCH_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=new-feature-1 services $APP_NAME) + ``` + +6. Review the application + + Check the version output of the service. It should read Hello World v1.0 + + ```sh + curl http://$BRANCH_IP + ``` + +## Automate deployments for git main branch + +Before code is released to production, it's common to release code to a small subset of live traffic before migrating all traffic to the new code base. + +In this section, you implement a trigger that is activated when code is committed to the main branch. The trigger deploys the canary deployment which receives 25% of all live traffic to the new revision. + +1. Set up the trigger for the main branch: + + ```sh + gcloud beta builds triggers create cloud-source-repositories \ + --trigger-config build/main-trigger.json + ``` + +2. To review the new trigger, go to the + [Cloud Build Triggers page](https://console.cloud.google.com/cloud-build/triggers) + in the Console. + + [Go to Triggers](https://console.cloud.google.com/cloud-build/triggers) + + +3. Merge the branch to the main line and push to + the remote repository: + + ```sh + git checkout main + git merge new-feature-1 + git push gcp main + ``` + +4. To review the build in progress, go to the + [Cloud Build Builds page](https://console.cloud.google.com/cloud-build/builds) + in the Console. + + [Go to Builds](https://console.cloud.google.com/cloud-build/builds) + + Once the build has completed continue to the next step + +5. Review multiple responses from the server + + Run the following command and note that approximately 25% of the responses are showing the new response of Hello World v1.1 + + ```sh + while true; do curl -w "\n" http://$PRODUCTION_IP; sleep 1; done + ``` + + When you're ready to continue press `Ctrl+c` to exit out of the loop. + +## Automating deployments for git tags + +After the canary deployment is validated with a small subset of traffic, you release the deployment to the remainder of the live traffic. + +In this section, you set up a trigger that is activated when you create a tag in the repository. The trigger labels the image with the appropriate tag then deploys the updates to prod ensuring 100% of traffic is accessing the tagged image. + +1. Set up the tag trigger: + + ```sh + gcloud beta builds triggers create cloud-source-repositories \ + --trigger-config build/tag-trigger.json + ``` + +2. To review the new trigger, go to the + [Cloud Build Triggers page](https://console.cloud.google.com/cloud-build/triggers) + in the Console. + + [Go to Triggers](https://console.cloud.google.com/cloud-build/triggers) + +3. Create a new tag and push to the remote repository: + + ```sh + git tag 1.1 + git push gcp 1.1 + ``` +4. To review the build in progress, go to the + [Cloud Build Builds page](https://console.cloud.google.com/cloud-build/builds) + in the Console. + + [Go to Builds](https://console.cloud.google.com/cloud-build/builds) + +5. Review multiple responses from the server + + Run the following command and note that 100% of the responses are showing the new response of Hello World v1.1 + + This may take a moment as the new pods are deployed and health checked within GKE + + ```sh + while true; do curl -w "\n" http://$PRODUCTION_IP; sleep 1; done + ``` + + When you're ready to continue press `Ctrl+c` to exit out of the loop. + + Congratulations! You created CI/CD triggers in Cloud Build for branches and tags to deploy your apps to GKE. \ No newline at end of file diff --git a/builder/cloudbuild-dev.yaml b/labs/gke-progression/build/branch-cloudbuild.yaml.tmpl similarity index 65% rename from builder/cloudbuild-dev.yaml rename to labs/gke-progression/build/branch-cloudbuild.yaml.tmpl index 27d4b3f..5c84a22 100644 --- a/builder/cloudbuild-dev.yaml +++ b/labs/gke-progression/build/branch-cloudbuild.yaml.tmpl @@ -22,7 +22,7 @@ steps: args: - '-c' - | - docker build -t gcr.io/$PROJECT_ID/gcme:${BRANCH_NAME}-${SHORT_SHA} . + docker build -t gcr.io/$PROJECT_ID/$APP_NAME:${BRANCH_NAME}-${SHORT_SHA} ./src @@ -36,7 +36,7 @@ steps: args: - '-c' - | - docker push gcr.io/$PROJECT_ID/gcme:${BRANCH_NAME}-${SHORT_SHA} + docker push gcr.io/$PROJECT_ID/$APP_NAME:${BRANCH_NAME}-${SHORT_SHA} @@ -44,28 +44,22 @@ steps: - id: 'deploy' name: 'gcr.io/cloud-builders/gcloud' env: - - 'CLOUDSDK_COMPUTE_ZONE=${_CLOUDSDK_COMPUTE_ZONE}' - - 'CLOUDSDK_CONTAINER_CLUSTER=${_CLOUDSDK_CONTAINER_CLUSTER}' - 'KUBECONFIG=/kube/config' entrypoint: 'bash' args: - '-c' - | - CLUSTER=$$(gcloud config get-value container/cluster) + PROJECT=$$(gcloud config get-value core/project) - ZONE=$$(gcloud config get-value compute/zone) + - gcloud container clusters get-credentials "$${CLUSTER}" \ + gcloud container clusters get-credentials "${_CLUSTER}" \ --project "$${PROJECT}" \ - --zone "$${ZONE}" + --zone "${_ZONE}" - - - - - sed -i 's|gcr.io/cloud-solutions-images/gceme:.*|gcr.io/$PROJECT_ID/gcme:${BRANCH_NAME}-${SHORT_SHA}|' ./kubernetes/deployments/dev/*.yaml + sed -i 's|gcr.io/$PROJECT_ID/$APP_NAME:.*|gcr.io/$PROJECT_ID/$APP_NAME:${BRANCH_NAME}-${SHORT_SHA}|' ./k8s/deployments/dev/*.yaml kubectl get ns ${BRANCH_NAME} || kubectl create ns ${BRANCH_NAME} - kubectl apply --namespace ${BRANCH_NAME} --recursive -f kubernetes/deployments/dev - kubectl apply --namespace ${BRANCH_NAME} --recursive -f kubernetes/services + kubectl apply --namespace ${BRANCH_NAME} --recursive -f k8s/deployments/dev + kubectl apply --namespace ${BRANCH_NAME} --recursive -f k8s/services diff --git a/labs/gke-progression/build/branch-trigger.json.tmpl b/labs/gke-progression/build/branch-trigger.json.tmpl new file mode 100644 index 0000000..6e496e0 --- /dev/null +++ b/labs/gke-progression/build/branch-trigger.json.tmpl @@ -0,0 +1,16 @@ +{ + "name": "branch", + "description": "Trigger dev build/deploy for any branch other than master", + "filename": "build/branch-cloudbuild.yaml", + + "triggerTemplate": { + "projectId": "$PROJECT_ID", + "repoName": "gke-progression", + "branchName": "main", + "invertRegex": true + }, + "substitutions": { + "_ZONE": "${ZONE}", + "_CLUSTER": "${CLUSTER}" + } +} \ No newline at end of file diff --git a/builder/cloudbuild-prod.yaml b/labs/gke-progression/build/main-cloudbuild.yaml.tmpl similarity index 57% rename from builder/cloudbuild-prod.yaml rename to labs/gke-progression/build/main-cloudbuild.yaml.tmpl index d806995..666ceb1 100644 --- a/builder/cloudbuild-prod.yaml +++ b/labs/gke-progression/build/main-cloudbuild.yaml.tmpl @@ -22,8 +22,7 @@ steps: args: - '-c' - | - docker build -t gcr.io/$PROJECT_ID/gceme:$TAG_NAME . - + docker build -t gcr.io/$PROJECT_ID/$APP_NAME:${SHORT_SHA} ./src ### Test @@ -36,7 +35,7 @@ steps: args: - '-c' - | - docker push gcr.io/$PROJECT_ID/gceme:$TAG_NAME + docker push gcr.io/$PROJECT_ID/$APP_NAME:${SHORT_SHA} @@ -44,27 +43,21 @@ steps: - id: 'deploy' name: 'gcr.io/cloud-builders/gcloud' env: - - 'CLOUDSDK_COMPUTE_ZONE=${_CLOUDSDK_COMPUTE_ZONE}' - - 'CLOUDSDK_CONTAINER_CLUSTER=${_CLOUDSDK_CONTAINER_CLUSTER}' - 'KUBECONFIG=/kube/config' entrypoint: 'bash' args: - '-c' - | - CLUSTER=$$(gcloud config get-value container/cluster) - PROJECT=$$(gcloud config get-value core/project) - ZONE=$$(gcloud config get-value compute/zone) - + PROJECT=$$(gcloud config get-value core/project) - gcloud container clusters get-credentials "$${CLUSTER}" \ + gcloud container clusters get-credentials "${_CLUSTER}" \ --project "$${PROJECT}" \ - --zone "$${ZONE}" - + --zone "${_ZONE}" - sed -i 's|gcr.io/cloud-solutions-images/gceme:.*|gcr.io/$PROJECT_ID/gceme:$TAG_NAME|' ./kubernetes/deployments/prod/*.yaml + sed -i 's|gcr.io/$PROJECT_ID/$APP_NAME:.*|gcr.io/$PROJECT_ID/$APP_NAME:${SHORT_SHA}|' ./k8s/deployments/canary/*.yaml kubectl get ns production || kubectl create ns production - kubectl apply --namespace production --recursive -f kubernetes/deployments/prod - kubectl apply --namespace production --recursive -f kubernetes/services + kubectl apply --namespace production --recursive -f k8s/deployments/canary + kubectl apply --namespace production --recursive -f k8s/services diff --git a/labs/gke-progression/build/main-trigger.json.tmpl b/labs/gke-progression/build/main-trigger.json.tmpl new file mode 100644 index 0000000..fbfde6a --- /dev/null +++ b/labs/gke-progression/build/main-trigger.json.tmpl @@ -0,0 +1,16 @@ +{ + "name": "main", + "description": "Trigger canary build/deploy for any commit to the main branch", + + "filename": "build/main-cloudbuild.yaml", + "triggerTemplate": { + "projectId": "$PROJECT_ID", + "repoName": "gke-progression", + "branchName": "main" + }, + "substitutions": { + "_ZONE": "${ZONE}", + "_CLUSTER": "${CLUSTER}" + } + +} \ No newline at end of file diff --git a/labs/gke-progression/build/tag-cloudbuild.yaml.tmpl b/labs/gke-progression/build/tag-cloudbuild.yaml.tmpl new file mode 100644 index 0000000..b4e19f0 --- /dev/null +++ b/labs/gke-progression/build/tag-cloudbuild.yaml.tmpl @@ -0,0 +1,52 @@ +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +steps: + +### Add Tag + + - id: 'add-tag' + name: 'gcr.io/cloud-builders/gcloud' + entrypoint: 'bash' + args: + - '-c' + - | + gcloud container images add-tag gcr.io/$PROJECT_ID/$APP_NAME:${SHORT_SHA} \ + gcr.io/$PROJECT_ID/$APP_NAME:$TAG_NAME \ + --quiet + + + +### Deploy + - id: 'deploy' + name: 'gcr.io/cloud-builders/gcloud' + env: + - 'KUBECONFIG=/kube/config' + entrypoint: 'bash' + args: + - '-c' + - | + PROJECT=$$(gcloud config get-value core/project) + + gcloud container clusters get-credentials "${_CLUSTER}" \ + --project "$${PROJECT}" \ + --zone "${_ZONE}" + + + sed -i 's|gcr.io/$PROJECT_ID/$APP_NAME:.*|gcr.io/$PROJECT_ID/$APP_NAME:$TAG_NAME|' ./k8s/deployments/prod/*.yaml + + kubectl apply --namespace production --recursive -f k8s/deployments/prod + kubectl apply --namespace production --recursive -f k8s/services + diff --git a/labs/gke-progression/build/tag-trigger.json.tmpl b/labs/gke-progression/build/tag-trigger.json.tmpl new file mode 100644 index 0000000..b1bdb7f --- /dev/null +++ b/labs/gke-progression/build/tag-trigger.json.tmpl @@ -0,0 +1,16 @@ +{ + "name": "tag", + "description": "Migrate from canary to prod triggered by creation of any tag", + "filename": "build/tag-cloudbuild.yaml", + + "triggerTemplate": { + "projectId": "$PROJECT_ID", + "repoName": "gke-progression", + "tagName": ".*" + }, + "substitutions": { + "_ZONE": "${ZONE}", + "_CLUSTER": "${CLUSTER}" + } + +} \ No newline at end of file diff --git a/kubernetes/deployments/canary/frontend-canary.yaml b/labs/gke-progression/k8s/deployments/canary/frontend-canary.yaml.tmpl similarity index 70% rename from kubernetes/deployments/canary/frontend-canary.yaml rename to labs/gke-progression/k8s/deployments/canary/frontend-canary.yaml.tmpl index 7b3b432..dcdae5a 100644 --- a/kubernetes/deployments/canary/frontend-canary.yaml +++ b/labs/gke-progression/k8s/deployments/canary/frontend-canary.yaml.tmpl @@ -1,4 +1,4 @@ -# Copyright 2015 Google Inc. All rights reserved. +# Copyright 2021 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,35 +15,32 @@ kind: Deployment apiVersion: apps/v1 metadata: - name: gceme-frontend-canary + name: $APP_NAME-canary spec: selector: matchLabels: - app: gceme + app: $APP_NAME role: frontend env: canary - replicas: template: metadata: name: frontend labels: - app: gceme + app: $APP_NAME role: frontend env: canary spec: containers: - name: frontend - image: gcr.io/cloud-solutions-images/gceme:1.0.0 + image: gcr.io/$PROJECT_ID/$APP_NAME:1.0.0 + imagePullPolicy: Always resources: limits: memory: "500Mi" cpu: "100m" - imagePullPolicy: Always - readinessProbe: - httpGet: - path: /healthz - port: 80 - command: ["sh", "-c", "app -frontend=true -backend-service=http://gceme-backend:8080 -port=80"] ports: - name: frontend - containerPort: 80 + containerPort: 8080 + env: + - name: PORT + value: "8080" \ No newline at end of file diff --git a/kubernetes/deployments/dev/frontend-dev.yaml b/labs/gke-progression/k8s/deployments/dev/frontend-dev.yaml.tmpl similarity index 68% rename from kubernetes/deployments/dev/frontend-dev.yaml rename to labs/gke-progression/k8s/deployments/dev/frontend-dev.yaml.tmpl index bc78f40..294ceac 100644 --- a/kubernetes/deployments/dev/frontend-dev.yaml +++ b/labs/gke-progression/k8s/deployments/dev/frontend-dev.yaml.tmpl @@ -1,4 +1,4 @@ -# Copyright 2015 Google Inc. All rights reserved. +# Copyright 2021 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,35 +15,32 @@ kind: Deployment apiVersion: apps/v1 metadata: - name: gceme-frontend-dev + name: $APP_NAME-dev spec: selector: matchLabels: - app: gceme + app: $APP_NAME role: frontend env: dev - replicas: template: metadata: name: frontend labels: - app: gceme + app: $APP_NAME role: frontend env: dev spec: containers: - name: frontend - image: gcr.io/cloud-solutions-images/gceme:1.0.0 + image: gcr.io/$PROJECT_ID/$APP_NAME:1.0.0 + imagePullPolicy: Always resources: limits: memory: "500Mi" cpu: "100m" - imagePullPolicy: Always - readinessProbe: - httpGet: - path: /healthz - port: 80 - command: ["sh", "-c", "app -frontend=true -backend-service=http://${GCEME_BACKEND_SERVICE_HOST}:${GCEME_BACKEND_SERVICE_PORT} -port=80"] ports: - name: frontend - containerPort: 80 + containerPort: 8080 + env: + - name: PORT + value: "8080" \ No newline at end of file diff --git a/kubernetes/deployments/prod/frontend-production.yaml b/labs/gke-progression/k8s/deployments/prod/frontend-production.yaml.tmpl similarity index 70% rename from kubernetes/deployments/prod/frontend-production.yaml rename to labs/gke-progression/k8s/deployments/prod/frontend-production.yaml.tmpl index d76106d..0ea0d17 100644 --- a/kubernetes/deployments/prod/frontend-production.yaml +++ b/labs/gke-progression/k8s/deployments/prod/frontend-production.yaml.tmpl @@ -1,4 +1,4 @@ -# Copyright 2015 Google Inc. All rights reserved. +# Copyright 2021 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,35 +15,33 @@ kind: Deployment apiVersion: apps/v1 metadata: - name: gceme-frontend-production + name: $APP_NAME-production spec: - replicas: + replicas: 3 selector: matchLabels: - app: gceme + app: $APP_NAME role: frontend env: production template: metadata: name: frontend labels: - app: gceme + app: $APP_NAME role: frontend env: production spec: containers: - name: frontend - image: gcr.io/cloud-solutions-images/gceme:1.0.0 + image: gcr.io/$PROJECT_ID/$APP_NAME:1.0.0 + imagePullPolicy: Always resources: limits: memory: "500Mi" cpu: "100m" - imagePullPolicy: Always - readinessProbe: - httpGet: - path: /healthz - port: 80 - command: ["sh", "-c", "app -frontend=true -backend-service=http://gceme-backend:8080 -port=80"] ports: - name: frontend - containerPort: 80 + containerPort: 8080 + env: + - name: PORT + value: "8080" \ No newline at end of file diff --git a/kubernetes/services/frontend.yaml b/labs/gke-progression/k8s/services/frontend.yaml.tmpl similarity index 92% rename from kubernetes/services/frontend.yaml rename to labs/gke-progression/k8s/services/frontend.yaml.tmpl index dbf9360..67db9c2 100644 --- a/kubernetes/services/frontend.yaml +++ b/labs/gke-progression/k8s/services/frontend.yaml.tmpl @@ -15,14 +15,14 @@ kind: Service apiVersion: v1 metadata: - name: gceme-frontend + name: $APP_NAME spec: type: LoadBalancer ports: - name: http port: 80 - targetPort: 80 + targetPort: 8080 protocol: TCP selector: - app: gceme + app: $APP_NAME role: frontend diff --git a/labs/gke-progression/src/Dockerfile b/labs/gke-progression/src/Dockerfile new file mode 100644 index 0000000..5344650 --- /dev/null +++ b/labs/gke-progression/src/Dockerfile @@ -0,0 +1,32 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Use the official lightweight Python image. +# https://hub.docker.com/_/python +FROM python:3.7-slim + +# Copy local code to the container image. +ENV APP_HOME /app +WORKDIR $APP_HOME +COPY . ./ + +# Install production dependencies. +RUN pip install Flask gunicorn + +# Run the web service on container startup. Here we use the gunicorn +# webserver, with one worker process and 8 threads. +# For environments with multiple CPU cores, increase the number of workers +# to be equal to the cores available. +CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 app:app \ No newline at end of file diff --git a/labs/gke-progression/src/app.py b/labs/gke-progression/src/app.py new file mode 100644 index 0000000..7c2aae3 --- /dev/null +++ b/labs/gke-progression/src/app.py @@ -0,0 +1,27 @@ +#!/usr/bin/python +# +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os + +from flask import Flask + +app = Flask(__name__) + +@app.route('/') +def hello_world(): + return 'Hello World v1.0' + +if __name__ == "__main__": + app.run(debug=True,host='0.0.0.0',port=8080) \ No newline at end of file diff --git a/labs/hydrating-with-kustomize/README.md b/labs/hydrating-with-kustomize/README.md new file mode 100644 index 0000000..2b785bd --- /dev/null +++ b/labs/hydrating-with-kustomize/README.md @@ -0,0 +1,353 @@ +# Hydrating with Kustomize + +Kustomize is a tool that introduces a template-free way to customize application configuration, simplifying the use of off-the-shelf applications. It's available as a stand alone utility and is built into kubectl through `kubectl apply -k` of can be used as a stand alone CLI. For additional details read more at [kustomize.io](https://kustomize.io/). +## Objectives + +In this tutorial you work through some of the core concepts of Kustomize and use it to manage variations in the applications and environments. + +You will: + +- Utilize kustomize command line client +- Override common elements +- Patch larger yaml structures +- Utilize multiple layers of overlays + +## Preparing your workspace + +1. Open Cloud Shell editor by visiting the following url + +``` +https://ide.cloud.google.com +``` + +2. In the terminal window create a working directory for this tutorial + +``` +mkdir kustomize-lab +``` + +3. Change into the directory and set the IDE workspace + +``` +cd kustomize-lab && cloudshell workspace . +``` + +## Utilizing kustomize command line client + +The power of kustomize comes from the ability to overlay and modify base Kubernetes yamls with custom values. In order to do this kustomize requires a base file with instructions on where the files are and what to override. Kustomize is included in the Kubernetes ecosystem and can be executed through various methods. + +In this section you will create a base kustomize configuration and process the files with the stand alone kustomize command line client. + +1. To start, you will create a folder to hold your base configuration files + +``` +mkdir -p chat-app/base +``` + +2. Create a simple kubernetes ``deployment.yaml`` in the base folder + +``` +cat < chat-app/base/deployment.yaml +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + metadata: + name: chat-app + spec: + containers: + - name: chat-app + image: chat-app-image +EOF +``` + +3. Create the base `kustomization.yaml` + + Kustomize looks for a file called kustomization.yaml as an entry point. This file contains references to the various base and override files as well as specific override values. + + Create a `kustomization.yaml` file that references the `deployment.yaml` as the base resources. +``` +cat < chat-app/base/kustomization.yaml +bases: + - deployment.yaml +EOF +``` + +4. Run the kustomize command on the base folder. Doing so outputs the deployment YAML files with no changes, which is expected since you haven't included any variations yet. + +``` +kustomize build chat-app/base +``` + +This standalone client can be combined with the kubectl client to apply the output directly as in the following example. Doing so streams the output of the build command directly into the kubectl apply command. + +(Do Not Execute - Included for reference only) + +
+kustomize build chat-app/base | kubectl apply -f -
+
+ +This technique is useful if a specific version of the kustomize client is needed. + +Alternatively kustomize can be executed with the tooling integrated within kubectl itself. As in the following example. + +(Do Not Execute - Included for reference only) + +
+kubectl apply -k chat-app/base
+
+ +## Overriding common elements + +Now that your workspace is configured and you verified kustomize is working, it's time to override some of the base values. + +Images, namespaces and labels are very commonly customized for each application and environment. Since they are commonly changed, Kustomize lets you declare them directly in the `kustomize.yaml`, eliminating the need to create many patches for these common scenarios. + +This technique is often used to create a specific instance of a template. One base set of resources can now be used for multiple implementations by simply changing the name and its namespace. + +In this example, you will add a namespace, name prefix and add some labels to your `kustomization.yaml`. + +1. Update the `kustomization.yaml` file to include common labels and namespaces. + + Copy and execute the following commands in your terminal + +``` +cat < chat-app/base/kustomization.yaml +bases: + - deployment.yaml + +namespace: my-namespace +nameprefix: my- +commonLabels: + app: my-app + +EOF +``` + +2. Execute the build command + + Executing the build at this point shows that the resulting YAML file now contains the namespace, labels and prefixed names in both the service and deployment definitions. + +``` +kustomize build chat-app/base +``` + +Note how the output contains labels and namespaces that are not in the deployment YAML file. Note also how the name was changed from `chat-app` to `my-chat-app` + +(Output do not copy) +
+kind: Deployment
+metadata:
+  labels:
+    app: my-app
+  name: my-chat-app
+  namespace: my-namespace
+
+ +## Patching larger yaml structures + +Kustomize also provides the ability to apply patches that overlay the base resources. This technique is often used to provide variability between applications and environments. + +In this step, you will create environment variations for a single application that use the same base resources. + +1. Start by creating folders for the different environments + +``` +mkdir -p chat-app/dev +mkdir -p chat-app/prod +``` + + + +2. Write the stage patch with the following command + +``` +cat < chat-app/dev/deployment.yaml +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: chat-app + env: + - name: ENVIRONMENT + value: dev +EOF +``` + +3. Now Write the prod patch with the following command + +``` +cat < chat-app/prod/deployment.yaml +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: chat-app + env: + - name: ENVIRONMENT + value: prod +EOF +``` + +Notice that the patches above do not contain the container image name. That value is provided in the base/deployment.yaml you created in the previous step. These patches do however contain unique environment variables for dev and prod. + +4. Implement the kustomize YAML files for the base directory + +Rewrite the base kustomization.yaml, remove the namespace and name prefix as this is just the base config with no variation. Those fields will be moved to the environment files in just a moment. + +``` +cat < chat-app/base/kustomization.yaml +bases: + - deployment.yaml + +commonLabels: + app: chat-app + +EOF +``` +5. Implement the kustomize YAML files for the dev directory + +Now implement the variations for dev and prod by executing the following commands in your terminal. + +``` +cat < chat-app/dev/kustomization.yaml +bases: +- ../base + +namespace: dev +nameprefix: dev- +commonLabels: + env: dev + +patches: +- deployment.yaml +EOF +``` + +Note the addition of the `patches`: section of the file. This indicates that kustomize should overlay those files on top of the base resources. + +6. Implement the kustomize YAML files for the prod directory + +``` +cat < chat-app/prod/kustomization.yaml +bases: +- ../base + +namespace: prod +nameprefix: prod- +commonLabels: + env: prod + +patches: +- deployment.yaml +EOF +``` + +7. Run kustomize to merge the files +With the base and environment files created, you can execute the kustomize process to patch the base files. + + Run the following command for dev to see the merged result. + +``` +kustomize build chat-app/dev +``` + +Note the output contains merged results such as labels from base and dev configurations as well as the container image name from the base and the environment variable from the dev folders. + + +## Utilizing multiple layers of overlays + +Many organizations have a team that helps support the app teams and manage the platform. Frequently these teams will want to include specific details that are to be included in all apps across all environments, such as a logging agent. + +In this example, you will create a `shared-kustomize` folder and resources which will be included by all applications and regardless of which environment they're deployed. + +1. Create the shared-kustomize folder + +``` +mkdir shared-kustomize +``` + +2. Create a simple `deployment.yaml` in the shared folder + + +``` +cat < shared-kustomize/deployment.yaml +kind: Deployment +apiVersion: apps/v1 +metadata: + name: app +spec: + template: + spec: + containers: + - name: logging-agent + image: logging-agent-image +EOF +``` + +3. Create a kustomization.yaml in the shared folder + +``` +cat < shared-kustomize/kustomization.yaml +bases: + - deployment.yaml +EOF +``` + +4. Reference the shared-kustomize folder from your application + +Since you want the `shared-kustomize` folder to be the base for all your applications, you will need to update your `chat-app/base/kustomization.yaml` to use `shared-kustomize` as the base. Then patch its own deployment.yaml on top. The environment folders will then patch again on top of that. + +Copy and execute the following commands in your terminal + +``` +cat < chat-app/base/kustomization.yaml +bases: + - ../../shared-kustomize + +commonLabels: + app: chat-app + +patches: +- deployment.yaml + +EOF +``` + +5. Run kustomize and view the merged results for dev + + +``` +kustomize build chat-app/dev +``` + +Note the output contains merged results from the app base, the app environment, and the `shared-kustomize` folders. Specifically, you can see in the containers section values from all three locations. + +(output do not copy) +
+
+```
+    containers:
+          - env:
+            - name: ENVIRONMENT
+              value: dev
+            name: chat-app
+          - image: image
+            name: app
+          - image: logging-agent-image
+            name: logging-agent
+```
+
+
+ + diff --git a/labs/understanding-skaffold/README.md b/labs/understanding-skaffold/README.md new file mode 100644 index 0000000..994e21e --- /dev/null +++ b/labs/understanding-skaffold/README.md @@ -0,0 +1,370 @@ +# Understanding Skaffold + +[Skaffold](https://skaffold.dev/) is a tool that handles the workflow for +building, pushing and deploying your application. You can use Skaffold to +easily configure a local development workspace, streamline your inner +development loop, and integrate with other tools such as +[Kustomize](kustomize.dev) and [Helm](https://helm.sh/) to help manage your +Kubernetes manifests. + +## Objectives + +In this tutorial you work through some of the core concepts of Skaffold, use it +to automate your inner development loop, then deploy an application. + +You will: + +- Configure and enable Skaffold for local development +- Build and run a simple golang application +- Manage local application deployment with Skaffold +- Render manifests and deploy your application + +## Preparing your workspace + +1. Open the Cloud Shell editor by visiting the following url: + +``` +https://shell.cloud.google.com +``` + +2. If you have not done so already, in the terminal window clone the application source with the following command: + +``` +git clone https://github.com/GoogleCloudPlatform/software-delivery-workshop.git +``` + +3. Change into the cloned repository directory: + +``` +cd software-delivery-workshop/labs/understanding-skaffold/getting-started +``` + +4. Set your Cloud Shell workspace to the current directory by running the following command: + +``` +cloudshell workspace . +``` + +## Preparing your project + +1. Ensure your Google Cloud project is set correctly by running the following command: + +``` +gcloud config set project {{project-id}} +``` + +## Getting started with Skaffold + +1. Run the following command to create the top-level Skaffold configuration file, `skaffold.yaml`: + +``` +cat < skaffold.yaml +apiVersion: skaffold/v2beta21 +kind: Config +metadata: + name: getting-started-kustomize +build: + tagPolicy: + gitCommit: + ignoreChanges: true + artifacts: + - image: skaffold-kustomize + context: app + docker: + dockerfile: Dockerfile +deploy: + kustomize: + paths: + - overlays/dev +profiles: +- name: staging + deploy: + kustomize: + paths: + - overlays/staging +- name: prod + deploy: + kustomize: + paths: + - overlays/prod +EOF +``` + +2. Open the file `skaffold.yaml` in the IDE pane. This is the top-level configuration file that defines the tSkaffold pipeline. + +Notice the Kubernetes-like YAML format and the following sections in the YAML: + + - `build` + - `deploy` + - `profiles` + +These sections define how the application should be built and deployed, as well as profiles for each deployment target. + +You can read more about the full list of Skaffold stages in the Skaffold Pipeline Stages [documentation](https://skaffold.dev/docs/pipeline-stages). + +# Build + +The `build` section contains configuration that defines how the application +should be built. In this case you can see configuration for how `git` tags +should be handled, as well as an `artifacts` section that defines the container +images, that comprise the application. + +As well as this, in this section you can see the reference to the `Dockerfile` +to be used to build the images. Skaffold additionally supports other build +tools such as `Jib`, `Maven`, `Gradle`, Cloud-native `Buildpacks`, `Bazel` and +custom scripts. You can read more about this configuration in the [Skaffold +Build documentation](). + +# Deploy + +The `deploy` section contains configuration that defines how the application +should be deployed. In this case you can see an example for a default +deployment that configures Skaffold to use the the +[`Kustomize`](https://kustomize.io/) tool. + +The `Kustomize` tool provides functionality for generating Kubernetes manifests +by combining a set of common component YAML files (under the `base` directory) +with a one or more "overlays" that typically correspond to one or more +deployment targets -- typically *dev*, *test*, *staging* and *production* or +similar. + +In this example you can see two overlays for three targets, *dev*, *staging* +and *prod*. The *dev* overlay will be used during local development and the +*staging* and *prod* overlays for when deploying using Skaffold. + +# Profiles + +The `profiles` section contains configuration that defines build, test and +deployment configurations for different contexts. Different contexts are +typically different environments in your application deployment pipeline, like +`staging` or `prod` in this example. This means that you can easily manage +manifests whose contents need to differ for different target environments, +without repeating boilerplate configuration. + +Configuration in the `profiles` sectionm can replace or patch any items from +the main configuration (i.e. the `build`, `test` or `deploy` sections, for +example). + +As an example of this, open the file `overlays > prod > deployment.yaml`. +Notice that the number of replicas for the application is configured here to be +three, overriding the base configuration. + +# Navigating the application source code. + +1. Open the following file `app > main.go` in the IDE pane. This is a simple + golang application that writes a string to `stdout` every second. + +2. Notice that the application also outputs the name of the Kubernetes pod in which it it running. + +# Viewing the Dockerfile + +1. Open the file `app > Dockerfile` in the IDE pane. This file contains a + sequence of directives to build the application container image for the + `main.go` file, and is referenced in the top-level `skaffold.yaml` file. + +## Configuring your Kubernetes environment + +1. Run the following command to ensure your local Kubernetes cluster is running and configured: + +``` +minikube start +``` + +The may take several minutes. You should see the following output if the cluster has started successfully: +``` +Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default +``` + +2. Run the following command to create Kubernetes namespaces for `dev`, `staging` and `prod`: + +``` +kubectl apply -f namespaces.yaml +``` + +You should see the following output: + +``` +namespace/dev created +namespace/staging created +namespace/prod created +``` + +## Using Skaffold for local development + +1. Run the following command to build the application and deploy it to a local Kubernetes cluster running in Cloud Shell: + +``` +skaffold dev +``` + +You should see the application container build process run, which may take a +minute, and then the application output repeating every second: + +``` +[skaffold-kustomize] Hello world from pod skaffold-kustomize-dev-xxxxxxxxx-xxxxx +``` + +Note that the exact pod name will vary from the generic output given above. + +## Making changes to the application + +Now that the application is running in your local Kubernetes cluster, you can +make changes to the code, and Skaffold will automatically rebuild and redeploy +the application to the cluster. + +1. Open the file `app > main.go` in the IDE pane, and change the output string: + +``` +"Hello world from pod %s!\n" +``` + + to: + +``` +"Hello Skaffold world from pod %s!\n" +``` + +When you have made the change you should see Skaffold rebuild the image and redeploy it to the cluster, with the change in output visibile in the terminal window. + +2. Now, also in the file "app > main.go" in the IDE pane, change the line: + +``` +time.Sleep(time.Second * 1) +``` + + to + +``` +time.Sleep(time.Second * 10) +``` + +Again you should see the application rebuilt and redeployed, with the output line appearing once every 10 seconds. + +## Making changes to the Kubernetes config + +Next you will make a change to the Kubernetes config, and once more Skaffold will automatically redeploy. + +1. Open the file `base > deployment.yaml` in the IDE and change the line: + +``` +replicas: 1 +``` + +to + +``` +replicas: 2 +``` + +Once the application has been redeployed, you should see two pods running -- each will have a different name. + +2. Now, change the same line in the file `base > deployment.yaml` back to: + +``` +replicas: 1 +``` + +You should see one of the pods removed from service so that only one is remaining. + +3. Finally, press `Ctrl-C` in the terminal window to stop Skaffold local development. + +## Cutting a release + +Next, you will create a release by building a release image, and deploying it to a cluster. + +1. Run the following command to build the release: + +``` +skaffold build --file-output artifacts.json +``` + +This command will build the final image (if necessary) and output the release details to the `artifacts.json` file. + +If you wanted to use a tool like Cloud Deploy to deploy to your clusters, this +file contains the release information. This means that the artifact(s) are +immutable on the route to live. + +2. Run the following command to view the contents of the `artifacts.json` file: + +``` +cat artifacts.json | jq +``` + +Notice that the file contains the reference to the image that will be used in the final deployment. + +## Deploying to staging + +1. Run the following command to deploy the release using the `staging` profile: + +``` +skaffold deploy --profile staging --build-artifacts artifacts.json --tail +``` + +Once deployment is complete you should see output from three pods similar to the following: + +``` +[skaffold-kustomize] Hello world from pod skaffold-kustomize-staging-xxxxxxxxxx-xxxxx! +``` + +2. Press Ctrl-C in the terminal window to stop Skaffold output. + +3. Run the following command to observe your application up and running in the cluster: + +``` +kubectl get all --namespace staging +``` + +You should see two distinct pod names, because the `staging` profile for the application specifies there should be two replicas in the deployment. + +## Deploying to production + +1. Now run the following command to deploy the release using the `prod` profile: + +``` +skaffold deploy --profile prod --build-artifacts artifacts.json --tail +``` + +Once deployment is complete you should see output from three pods similar to the following: + +``` +[skaffold-kustomize] Hello world from pod skaffold-kustomize-prod-xxxxxxxxxx-xxxxx! +``` + +2. Press Ctrl-C in the terminal window to stop Skaffold output. + +You should see three distinct pod names, because the `prod` profile for the application specifies there should be three replicas in the deployment. + +3. Run the following command to observe your application up and running in the cluster: + +``` +kubectl get all --namespace prod +``` + +You should see output that contains lines similar to the following that show the prod deployment: + +``` +NAME READY UP-TO-DATE AVAILABLE AGE +deployment.apps/skaffold-kustomize-prod 3/3 3 3 16m +``` + +You should also see three application pods running. + +``` +NAME READY STATUS RESTARTS AGE +pod/skaffold-kustomize-prod-xxxxxxxxxx-xxxxx 1/1 Running 0 10m +pod/skaffold-kustomize-prod-xxxxxxxxxx-xxxxx 1/1 Running 0 10m +pod/skaffold-kustomize-prod-xxxxxxxxxx-xxxxx 1/1 Running 0 10m +``` + +## Cleaning up + +1. Run the following command to shut down the local cluster: + +``` +minikube delete +``` + +## Finishing up + +Congratulations! You have completed the `Understanding Skaffold` lab and have learned how to configure and use Skaffold for local development and application deployment. + diff --git a/labs/understanding-skaffold/getting-started/.gitignore b/labs/understanding-skaffold/getting-started/.gitignore new file mode 100644 index 0000000..e92a2c6 --- /dev/null +++ b/labs/understanding-skaffold/getting-started/.gitignore @@ -0,0 +1,2 @@ +skaffold.yaml +artifacts.json diff --git a/labs/understanding-skaffold/getting-started/app/Dockerfile b/labs/understanding-skaffold/getting-started/app/Dockerfile new file mode 100644 index 0000000..fd31c95 --- /dev/null +++ b/labs/understanding-skaffold/getting-started/app/Dockerfile @@ -0,0 +1,26 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM golang:1.15 as builder +COPY main.go . +# `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations +ARG SKAFFOLD_GO_GCFLAGS +RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o /app main.go + +FROM alpine:3.10 +# Define GOTRACEBACK to mark this container as using the Go language runtime +# for `skaffold debug` (https://skaffold.dev/docs/workflows/debug/). +ENV GOTRACEBACK=single +CMD ["./app"] +COPY --from=builder /app . diff --git a/labs/understanding-skaffold/getting-started/app/main.go b/labs/understanding-skaffold/getting-started/app/main.go new file mode 100644 index 0000000..205d305 --- /dev/null +++ b/labs/understanding-skaffold/getting-started/app/main.go @@ -0,0 +1,28 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "os" + "time" +) + +func main() { + for { + fmt.Printf("Hello world from pod %s!\n", os.Getenv("POD_NAME")) + time.Sleep(time.Second * 1) + } +} diff --git a/labs/understanding-skaffold/getting-started/base/deployment.yaml b/labs/understanding-skaffold/getting-started/base/deployment.yaml new file mode 100644 index 0000000..3f69808 --- /dev/null +++ b/labs/understanding-skaffold/getting-started/base/deployment.yaml @@ -0,0 +1,38 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: skaffold-kustomize + labels: + app: skaffold-kustomize +spec: + replicas: 1 + selector: + matchLabels: + app: skaffold-kustomize + template: + metadata: + labels: + app: skaffold-kustomize + spec: + containers: + - name: skaffold-kustomize + image: skaffold-kustomize + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name diff --git a/labs/understanding-skaffold/getting-started/base/kustomization.yaml b/labs/understanding-skaffold/getting-started/base/kustomization.yaml new file mode 100644 index 0000000..13240d8 --- /dev/null +++ b/labs/understanding-skaffold/getting-started/base/kustomization.yaml @@ -0,0 +1,19 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - deployment.yaml diff --git a/labs/understanding-skaffold/getting-started/namespaces.yaml b/labs/understanding-skaffold/getting-started/namespaces.yaml new file mode 100644 index 0000000..0fec0c4 --- /dev/null +++ b/labs/understanding-skaffold/getting-started/namespaces.yaml @@ -0,0 +1,29 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: v1 +kind: List +items: +- apiVersion: v1 + kind: Namespace + metadata: + name: dev +- apiVersion: v1 + kind: Namespace + metadata: + name: staging +- apiVersion: v1 + kind: Namespace + metadata: + name: prod diff --git a/labs/understanding-skaffold/getting-started/overlays/dev/deployment.yaml b/labs/understanding-skaffold/getting-started/overlays/dev/deployment.yaml new file mode 100644 index 0000000..476da37 --- /dev/null +++ b/labs/understanding-skaffold/getting-started/overlays/dev/deployment.yaml @@ -0,0 +1,20 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: skaffold-kustomize + labels: + env: dev diff --git a/labs/understanding-skaffold/getting-started/overlays/dev/kustomization.yaml b/labs/understanding-skaffold/getting-started/overlays/dev/kustomization.yaml new file mode 100644 index 0000000..1cd4a92 --- /dev/null +++ b/labs/understanding-skaffold/getting-started/overlays/dev/kustomization.yaml @@ -0,0 +1,25 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: dev +nameSuffix: -dev + +patchesStrategicMerge: +- deployment.yaml + +resources: +- ../../base diff --git a/labs/understanding-skaffold/getting-started/overlays/prod/deployment.yaml b/labs/understanding-skaffold/getting-started/overlays/prod/deployment.yaml new file mode 100644 index 0000000..2711ad8 --- /dev/null +++ b/labs/understanding-skaffold/getting-started/overlays/prod/deployment.yaml @@ -0,0 +1,22 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: skaffold-kustomize + labels: + env: prod +spec: + replicas: 3 diff --git a/labs/understanding-skaffold/getting-started/overlays/prod/kustomization.yaml b/labs/understanding-skaffold/getting-started/overlays/prod/kustomization.yaml new file mode 100644 index 0000000..8b22d3e --- /dev/null +++ b/labs/understanding-skaffold/getting-started/overlays/prod/kustomization.yaml @@ -0,0 +1,25 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: prod +nameSuffix: -prod + +patchesStrategicMerge: +- deployment.yaml + +resources: +- ../../base diff --git a/labs/understanding-skaffold/getting-started/overlays/staging/deployment.yaml b/labs/understanding-skaffold/getting-started/overlays/staging/deployment.yaml new file mode 100644 index 0000000..8b4420d --- /dev/null +++ b/labs/understanding-skaffold/getting-started/overlays/staging/deployment.yaml @@ -0,0 +1,22 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: skaffold-kustomize + labels: + env: staging +spec: + replicas: 2 diff --git a/labs/understanding-skaffold/getting-started/overlays/staging/kustomization.yaml b/labs/understanding-skaffold/getting-started/overlays/staging/kustomization.yaml new file mode 100644 index 0000000..28001ac --- /dev/null +++ b/labs/understanding-skaffold/getting-started/overlays/staging/kustomization.yaml @@ -0,0 +1,25 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: staging +nameSuffix: -staging + +patchesStrategicMerge: +- deployment.yaml + +resources: +- ../../base diff --git a/main.go b/main.go deleted file mode 100644 index 06284a9..0000000 --- a/main.go +++ /dev/null @@ -1,175 +0,0 @@ -/** -# Copyright 2015 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -**/ - -package main - -import ( - "encoding/json" - "flag" - "fmt" - "html/template" - "io/ioutil" - "log" - "net/http" - "net/http/httputil" - - "cloud.google.com/go/compute/metadata" -) - -type Instance struct { - Id string - Name string - Version string - Hostname string - Zone string - Project string - InternalIP string - ExternalIP string - LBRequest string - ClientIP string - Error string -} - -const version string = "1.0.0" - -func main() { - showversion := flag.Bool("version", false, "display version") - frontend := flag.Bool("frontend", false, "run in frontend mode") - port := flag.Int("port", 8080, "port to bind") - backend := flag.String("backend-service", "http://127.0.0.1:8081", "hostname of backend server") - flag.Parse() - - if *showversion { - fmt.Printf("Version %s\n", version) - return - } - - http.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "%s\n", version) - }) - - if *frontend { - frontendMode(*port, *backend) - } else { - backendMode(*port) - } - -} - -func backendMode(port int) { - log.Println("Operating in backend mode...") - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - i := newInstance() - raw, _ := httputil.DumpRequest(r, true) - i.LBRequest = string(raw) - resp, _ := json.Marshal(i) - fmt.Fprintf(w, "%s", resp) - }) - http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - }) - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) - -} - -func frontendMode(port int, backendURL string) { - log.Println("Operating in frontend mode...") - tpl := template.Must(template.New("out").Parse(html)) - - transport := http.Transport{DisableKeepAlives: false} - client := &http.Client{Transport: &transport} - req, _ := http.NewRequest( - "GET", - backendURL, - nil, - ) - req.Close = false - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - i := &Instance{} - resp, err := client.Do(req) - if err != nil { - w.WriteHeader(http.StatusServiceUnavailable) - fmt.Fprintf(w, "Error: %s\n", err.Error()) - return - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error: %s\n", err.Error()) - return - } - err = json.Unmarshal([]byte(body), i) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "Error: %s\n", err.Error()) - return - } - tpl.Execute(w, i) - }) - - http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { - resp, err := client.Do(req) - if err != nil { - w.WriteHeader(http.StatusServiceUnavailable) - fmt.Fprintf(w, "Backend could not be connected to: %s", err.Error()) - return - } - defer resp.Body.Close() - ioutil.ReadAll(resp.Body) - w.WriteHeader(http.StatusOK) - }) - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) -} - -type assigner struct { - err error -} - -func (a *assigner) assign(getVal func() (string, error)) string { - if a.err != nil { - return "" - } - s, err := getVal() - if err != nil { - a.err = err - } - return s -} - -func newInstance() *Instance { - var i = new(Instance) - if !metadata.OnGCE() { - i.Error = "Not running on GCE" - return i - } - - a := &assigner{} - i.Id = a.assign(metadata.InstanceID) - i.Zone = a.assign(metadata.Zone) - i.Name = a.assign(metadata.InstanceName) - i.Hostname = a.assign(metadata.Hostname) - i.Project = a.assign(metadata.ProjectID) - i.InternalIP = a.assign(metadata.InternalIP) - i.ExternalIP = a.assign(metadata.ExternalIP) - i.Version = version - - if a.err != nil { - i.Error = a.err.Error() - } - return i -}