├── launcher ├── .gitignore ├── README.md ├── package.json ├── index.js └── package-lock.json ├── .gitignore ├── SECURITY.md ├── NOTICE ├── outputs.tf ├── CONTRIBUTING.md ├── main.tf ├── atlas.tf ├── google.tf ├── variables.tf ├── ci-cd.tf ├── CODE_OF_CONDUCT.md ├── LICENSE.txt └── README.md /launcher/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.terraform/* 2 | *.tfstate 3 | *.tfstate.* 4 | .terraform.lock.hcl 5 | 6 | *.auto.tfvars 7 | terraform.tfvars 8 | -------------------------------------------------------------------------------- /launcher/README.md: -------------------------------------------------------------------------------- 1 | # tf-launcher 2 | 3 | Loads `config.yaml` if present, or else creates a default configuration. 4 | Queries user with a series of prompts, saves the configuration, and launches 5 | Terraform. 6 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | To report a security issue, please use [https://g.co/vulnz](https://g.co/vulnz). 2 | We use g.co/vulnz for our intake, and do coordination and disclosure here on 3 | GitHub (including using GitHub Security Advisory). The Google Security Team will 4 | respond within 5 working days of your report on g.co/vulnz. 5 | -------------------------------------------------------------------------------- /launcher/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tf-launcher", 3 | "version": "0.0.0", 4 | "description": "Launches Terraform for deploying MEAN stack on Cloud Run with MongoDB Atlas Serverless", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "js-yaml": "^4.1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Google LLC. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | output "app_url" { 16 | value = google_cloud_run_service.app.status[0].url 17 | } 18 | 19 | output "repo_url" { 20 | value = google_sourcerepo_repository.repo.url 21 | } 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement (CLA). You (or your employer) retain the copyright to your 10 | contribution; this simply gives us permission to use and redistribute your 11 | contributions as part of the project. Head over to 12 | to see your current agreements on file or 13 | to sign a new one. 14 | 15 | You generally only need to submit a CLA once, so if you've already submitted one 16 | (even if it was for a different project), you probably don't need to do it 17 | again. 18 | 19 | ## Community guidelines 20 | 21 | This project follows 22 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 23 | 24 | ## Code reviews 25 | 26 | All submissions, including submissions by project members, require review. We 27 | use GitHub pull requests for this purpose. Consult 28 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 29 | information on using pull requests. -------------------------------------------------------------------------------- /launcher/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const process = require("process"); 3 | const yaml = require("js-yaml"); 4 | 5 | 6 | let defaultConfig = { 7 | "version": "0.0.0" 8 | }; 9 | 10 | let configPath = "config.yaml"; 11 | let config; 12 | 13 | 14 | function errorExit(...args) { 15 | console.log("ERROR:", ...args); 16 | process.exit(1); 17 | } 18 | 19 | function info(...args) { 20 | console.log("INFO:", ...args); 21 | } 22 | 23 | function warn(...args) { 24 | console.log("WARN:", ...args); 25 | } 26 | 27 | /** 28 | * @param {YAMLException} e 29 | */ 30 | function yamlParseWarning(e) { 31 | warn(e.message); 32 | } 33 | 34 | function loadConfig() { 35 | let contents; 36 | 37 | try { 38 | info(`Loading configuration: ${configPath}`); 39 | contents = fs.readFileSync(configPath).toString(); 40 | } catch (e) { 41 | info(`No configuration found, generating default configuration`); 42 | config = defaultConfig; 43 | writeConfig(); 44 | return config; 45 | } 46 | 47 | try { 48 | config = yaml.load(contents); 49 | console.log(config); 50 | } catch (e) { 51 | return errorExit(e.message); 52 | } 53 | } 54 | 55 | function writeConfig() { 56 | try { 57 | const doc = yaml.dump(config); 58 | fs.writeFileSync(configPath, doc); 59 | info(`Saved configuration: ${configPath}`); 60 | } catch (e) { 61 | return errorExit(e.message); 62 | } 63 | } 64 | 65 | function main() { 66 | loadConfig(); 67 | console.log(config); 68 | } 69 | 70 | 71 | main(); 72 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ###----------------------------------------------------------------------------- 16 | ### configure terraform 17 | ###----------------------------------------------------------------------------- 18 | 19 | terraform { 20 | required_version = ">= 0.13" 21 | 22 | required_providers { 23 | google = { 24 | version = "~> 4.36" 25 | source = "hashicorp/google" 26 | } 27 | 28 | mongodbatlas = { 29 | version = "~> 1.4.5" 30 | source = "mongodb/mongodbatlas" 31 | } 32 | } 33 | } 34 | 35 | ###----------------------------------------------------------------------------- 36 | ### shared config 37 | ###----------------------------------------------------------------------------- 38 | 39 | resource "random_string" "mongodb_password" { 40 | length = 32 41 | special = false 42 | upper = true 43 | } 44 | 45 | resource "random_string" "suffix" { 46 | length = 8 47 | special = false 48 | upper = false 49 | } 50 | 51 | locals { 52 | project_id = "${var.project_name}-${random_string.suffix.result}" 53 | } 54 | -------------------------------------------------------------------------------- /launcher/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tf-launcher", 3 | "version": "0.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "tf-launcher", 9 | "version": "0.0.0", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "js-yaml": "^4.1.0" 13 | } 14 | }, 15 | "node_modules/argparse": { 16 | "version": "2.0.1", 17 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 18 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 19 | }, 20 | "node_modules/js-yaml": { 21 | "version": "4.1.0", 22 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 23 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 24 | "dependencies": { 25 | "argparse": "^2.0.1" 26 | }, 27 | "bin": { 28 | "js-yaml": "bin/js-yaml.js" 29 | } 30 | } 31 | }, 32 | "dependencies": { 33 | "argparse": { 34 | "version": "2.0.1", 35 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 36 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 37 | }, 38 | "js-yaml": { 39 | "version": "4.1.0", 40 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 41 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 42 | "requires": { 43 | "argparse": "^2.0.1" 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /atlas.tf: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | provider "mongodbatlas" { 16 | public_key = var.atlas_pub_key 17 | private_key = var.atlas_priv_key 18 | } 19 | 20 | resource "mongodbatlas_project" "demo" { 21 | name = local.project_id 22 | org_id = var.atlas_org_id 23 | } 24 | 25 | resource "mongodbatlas_project_ip_access_list" "acl" { 26 | project_id = mongodbatlas_project.demo.id 27 | cidr_block = "0.0.0.0/0" 28 | } 29 | 30 | resource "mongodbatlas_cluster" "cluster" { 31 | project_id = mongodbatlas_project.demo.id 32 | name = local.project_id 33 | 34 | provider_name = "TENANT" 35 | backing_provider_name = "GCP" 36 | provider_region_name = var.atlas_cluster_region 37 | provider_instance_size_name = var.atlas_cluster_tier 38 | } 39 | 40 | resource "mongodbatlas_database_user" "user" { 41 | project_id = mongodbatlas_project.demo.id 42 | auth_database_name = "admin" 43 | 44 | username = var.db_user 45 | password = random_string.mongodb_password.result 46 | 47 | roles { 48 | role_name = "readWrite" 49 | database_name = var.db_name 50 | } 51 | } 52 | 53 | locals { 54 | # the demo app only takes URIs with the credentials embedded and the atlas 55 | # provider doesn't give us a good way to get the hostname without the protocol 56 | # part so we end up doing some slicing and dicing to get the creds into the URI 57 | atlas_uri = replace( 58 | mongodbatlas_cluster.cluster.srv_address, 59 | "://", 60 | "://${var.db_user}:${mongodbatlas_database_user.user.password}@" 61 | ) 62 | } 63 | -------------------------------------------------------------------------------- /google.tf: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | provider "google" {} 16 | 17 | resource "google_project" "prj" { 18 | project_id = local.project_id 19 | name = local.project_id 20 | billing_account = var.google_billing_account 21 | 22 | lifecycle { 23 | # ignoring org_id changes allows the project to be created in whatever org 24 | # the user is part of by default, without having to explicitly include the 25 | # org id in the terraform config. is this a problem waiting to happen? only 26 | # time will tell. 27 | ignore_changes = [org_id] 28 | } 29 | } 30 | 31 | resource "google_project_service" "svc" { 32 | project = google_project.prj.name 33 | service = "${each.value}.googleapis.com" 34 | 35 | for_each = toset([ 36 | "run", 37 | ]) 38 | } 39 | 40 | resource "google_cloud_run_service" "app" { 41 | project = google_project.prj.name 42 | 43 | name = "demo" 44 | location = var.google_cloud_region 45 | 46 | template { 47 | spec { 48 | containers { 49 | image = var.app_image 50 | 51 | env { 52 | name = "ATLAS_URI" 53 | value = local.atlas_uri 54 | } 55 | } 56 | } 57 | } 58 | 59 | lifecycle { 60 | # this stops terraform from trying to revert to the sample app after you've 61 | # pushed new changes through CI 62 | ignore_changes = [template[0].spec[0].containers[0].image] 63 | } 64 | 65 | depends_on = [google_project_service.svc["run"]] 66 | } 67 | 68 | resource "google_cloud_run_service_iam_binding" "app" { 69 | location = google_cloud_run_service.app.location 70 | project = google_cloud_run_service.app.project 71 | service = google_cloud_run_service.app.name 72 | 73 | role = "roles/run.invoker" 74 | members = ["allUsers"] 75 | } 76 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ###----------------------------------------------------------------------------- 16 | ### general config 17 | ###----------------------------------------------------------------------------- 18 | 19 | variable "project_name" { 20 | type = string 21 | description = "the base name to use when creating resources. a randomized suffix will be added." 22 | default = "gcp-meanstack-demo" 23 | } 24 | 25 | ###----------------------------------------------------------------------------- 26 | ### region config 27 | ###----------------------------------------------------------------------------- 28 | 29 | # Please refer to https://www.mongodb.com/docs/atlas/reference/google-gcp/#std-label-google-gcp 30 | # for a mapping of Atlas region names to Google Cloud region names. In most cases 31 | # you should use the same region for both services. 32 | 33 | variable "google_cloud_region" { 34 | type = string 35 | description = "the Google Cloud region in which to create resources" 36 | default = "us-central1" 37 | } 38 | 39 | variable "atlas_cluster_region" { 40 | type = string 41 | description = "the Atlas region in which to create the database cluster" 42 | default = "CENTRAL_US" 43 | } 44 | 45 | variable "app_image" { 46 | type = string 47 | description = "the fully-qualified name of your app image" 48 | default = "us-central1-docker.pkg.dev/next22-mean-stack-demo/demo-app/server:latest" 49 | } 50 | 51 | ###----------------------------------------------------------------------------- 52 | ### Google Cloud 53 | ###----------------------------------------------------------------------------- 54 | 55 | variable "google_billing_account" { 56 | type = string 57 | description = "the ID of your Google Cloud billing account" 58 | } 59 | 60 | ###----------------------------------------------------------------------------- 61 | ### MongoDB Atlas 62 | ###----------------------------------------------------------------------------- 63 | 64 | variable "atlas_cluster_tier" { 65 | type = string 66 | description = "the tier of cluster you want to create. see the Atlas docs for details." 67 | default = "M0" # M0 is the free tier 68 | } 69 | 70 | variable "atlas_org_id" { 71 | type = string 72 | description = "the ID of your MongoDB Atlas organization" 73 | } 74 | 75 | variable "atlas_pub_key" { 76 | type = string 77 | description = "public key for MongoDB Atlas" 78 | } 79 | 80 | variable "atlas_priv_key" { 81 | type = string 82 | description = "private key for MongoDB Atlas" 83 | } 84 | 85 | ###----------------------------------------------------------------------------- 86 | ### MongoDB database 87 | ###----------------------------------------------------------------------------- 88 | 89 | variable "db_name" { 90 | type = string 91 | description = "the name of the database to configure" 92 | default = "meanStackExample" 93 | } 94 | 95 | variable "db_user" { 96 | type = string 97 | description = "the username used to connect to the mongodb cluster" 98 | default = "mongo" 99 | } 100 | -------------------------------------------------------------------------------- /ci-cd.tf: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | resource "google_project_service" "ci_cd" { 16 | project = google_project.prj.name 17 | service = "${each.value}.googleapis.com" 18 | 19 | for_each = toset([ 20 | "artifactregistry", 21 | "cloudbuild", 22 | "iam", 23 | "sourcerepo", 24 | "storage", 25 | ]) 26 | } 27 | 28 | ### this allows us to inject a 30 second delay between enabling a service and 29 | ### trying to use it. a resource that depends on this will not start until the 30 | ### timer is up. see google_artifact_registry_repository.repo for an example. 31 | 32 | resource "time_sleep" "wait_for_services" { 33 | create_duration = "30s" 34 | depends_on = [google_project_service.ci_cd] 35 | } 36 | 37 | ### the cloud build default service account needs to have `roles/run.developer` 38 | ### to create and update cloud run services. it also needs to be able to act 39 | ### as the cloud run service account. see more info in the docs: 40 | ### 41 | ### https://cloud.google.com/build/docs/deploying-builds/deploy-cloud-run#continuous-iam 42 | 43 | resource "google_project_iam_member" "build_run_developer" { 44 | project = google_project.prj.name 45 | role = "roles/run.developer" 46 | member = "serviceAccount:${google_project.prj.number}@cloudbuild.gserviceaccount.com" 47 | 48 | depends_on = [google_project_service.ci_cd["cloudbuild"]] 49 | } 50 | 51 | resource "google_project_iam_member" "build_act_as" { 52 | project = google_project.prj.name 53 | role = "roles/iam.serviceAccountUser" 54 | member = "serviceAccount:${google_project.prj.number}@cloudbuild.gserviceaccount.com" 55 | 56 | depends_on = [google_project_service.ci_cd["cloudbuild"]] 57 | } 58 | 59 | resource "google_artifact_registry_repository" "repo" { 60 | project = google_project.prj.name 61 | location = var.google_cloud_region 62 | 63 | repository_id = "repo" 64 | format = "DOCKER" 65 | 66 | depends_on = [ 67 | google_project_service.ci_cd["artifactregistry"], 68 | 69 | # wait for the service enablement to propagate before creating repo 70 | time_sleep.wait_for_services, 71 | ] 72 | } 73 | 74 | locals { 75 | image_path = "${var.google_cloud_region}-docker.pkg.dev/${google_project.prj.name}/${google_artifact_registry_repository.repo.name}/demo" 76 | } 77 | 78 | resource "google_sourcerepo_repository" "repo" { 79 | project = google_project.prj.name 80 | name = "repo" 81 | 82 | depends_on = [google_project_service.ci_cd["sourcerepo"]] 83 | } 84 | 85 | resource "google_cloudbuild_trigger" "demo" { 86 | project = google_project.prj.name 87 | name = "demo" 88 | location = var.google_cloud_region 89 | 90 | trigger_template { 91 | repo_name = google_sourcerepo_repository.repo.name 92 | branch_name = "^main$" 93 | } 94 | 95 | build { 96 | artifacts { 97 | images = ["${local.image_path}:$COMMIT_SHA"] 98 | } 99 | 100 | step { 101 | name = "node" 102 | entrypoint = "npm" 103 | args = ["install"] 104 | } 105 | 106 | step { 107 | name = "node" 108 | entrypoint = "npm" 109 | args = ["test"] 110 | } 111 | 112 | step { 113 | name = "gcr.io/cloud-builders/docker" 114 | args = ["build", "-t", "${local.image_path}:$COMMIT_SHA", "."] 115 | } 116 | 117 | step { 118 | name = "gcr.io/cloud-builders/docker" 119 | args = ["push", "${local.image_path}:$COMMIT_SHA"] 120 | } 121 | 122 | step { 123 | name = "gcr.io/cloud-builders/gcloud" 124 | args = [ 125 | "run", "deploy", google_cloud_run_service.app.name, 126 | "--image", "${local.image_path}:$COMMIT_SHA", 127 | "--region", var.google_cloud_region 128 | ] 129 | } 130 | } 131 | 132 | depends_on = [ 133 | google_project_service.ci_cd["cloudbuild"], 134 | 135 | # wait for the service enablement to propagate before creating trigger 136 | time_sleep.wait_for_services, 137 | ] 138 | } 139 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, 8 | body size, disability, ethnicity, gender identity and expression, level of 9 | experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment include: 15 | 16 | * Using welcoming and inclusive language 17 | * Being respectful of differing viewpoints and experiences 18 | * Gracefully accepting constructive criticism 19 | * Focusing on what is best for the community 20 | * Showing empathy towards other community members 21 | 22 | Examples of unacceptable behavior by participants include: 23 | 24 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 25 | * Trolling, insulting/derogatory comments, and personal or political attacks 26 | * Public or private harassment 27 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 28 | * Other conduct which could reasonably be considered inappropriate in a professional setting 29 | 30 | ## Our Responsibilities 31 | 32 | Project maintainers are responsible for clarifying the standards of acceptable 33 | behavior and are expected to take appropriate and fair corrective action in 34 | response to any instances of unacceptable behavior. 35 | 36 | Project maintainers have the right and responsibility to remove, edit, or 37 | reject comments, commits, code, wiki edits, issues, and other contributions 38 | that are not aligned to this Code of Conduct, or to ban temporarily or 39 | permanently any contributor for other behaviors that they deem inappropriate, 40 | threatening, offensive, or harmful. 41 | 42 | ## Scope 43 | 44 | This Code of Conduct applies both within project spaces and in public spaces 45 | when an individual is representing the project or its community. Examples of 46 | representing a project or community include using an official project e-mail 47 | address, posting via an official social media account, or acting as an 48 | appointed representative at an online or offline event. Representation of a 49 | project may be further defined and clarified by project maintainers. 50 | 51 | This Code of Conduct also applies outside the project spaces when the Project 52 | Steward has a reasonable belief that an individual's behavior may have a 53 | negative impact on the project or its community. 54 | 55 | ## Conflict Resolution 56 | 57 | We do not believe that all conflict is bad; healthy debate and disagreement 58 | often yield positive results. However, it is never okay to be disrespectful or 59 | to engage in behavior that violates the project’s code of conduct. 60 | 61 | If you see someone violating the code of conduct, you are encouraged to address 62 | the behavior directly with those involved. Many issues can be resolved quickly 63 | and easily, and this gives people more control over the outcome of their 64 | dispute. If you are unable to resolve the matter for any reason, or if the 65 | behavior is threatening or harassing, report it. We are dedicated to providing 66 | an environment where participants feel welcome and safe. 67 | 68 | Reports should be directed to Tony Pujals (tonypujals@google.com) and Abirami 69 | Sukumaran (abisukumaran@google.com), the Project Steward(s) for this project 70 | (terraform-mean-cloudrun-mongodb). It is the Project Steward’s duty to receive 71 | and address reported violations of the code of conduct. They will then work 72 | with a committee consisting of representatives from the Open Source Programs 73 | Office and the Google Open Source Strategy team. If for any reason you are 74 | uncomfortable reaching out to the Project Steward, please email 75 | opensource@google.com. 76 | 77 | We will investigate every complaint, but you may not receive a direct response. 78 | We will use our discretion in determining when and how to follow up on reported 79 | incidents, which may range from not taking action to permanent expulsion from 80 | the project and project-sponsored spaces. We will notify the accused of the 81 | report and provide them an opportunity to discuss it before any action is 82 | taken. The identity of the reporter will be omitted from the details of the 83 | report supplied to the accused. In potentially harmful situations, such as 84 | ongoing harassment or threats to anyone's safety, we may take action without 85 | notice. 86 | 87 | ## Attribution 88 | 89 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 90 | available at 91 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 92 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cloud Run + MongoDB Atlas demo 2 | ================================================================================ 3 | 4 | This repo contains a demo that deploys a [MEAN Stack](https://www.mongodb.com/mean-stack) 5 | application on Google Cloud using [Cloud Run](https://cloud.google.com/run) and 6 | [MongoDB Atlas](https://www.mongodb.com/atlas). It also deploys a CI/CD pipeline 7 | using [Cloud Build](https://cloud.google.com/build) that you can use to deploy 8 | your own changes (or even your own app). 9 | 10 | This demo uses [Terraform](https://www.terraform.io/) to provision and configure 11 | Atlas and Cloud Run and deploy [a sample application](https://github.com/mongodb-developer/mean-stack-example). 12 | You should be able to deploy your own simple application with only minor changes 13 | to this repo. 14 | 15 | Getting Started 16 | -------------------------------------------------------------------------------- 17 | 18 | In order to run this demo, you'll need a Google Cloud account, a MongoDB Atlas 19 | account, credentials for both services, and a working Terraform installation. 20 | 21 | A quick note on security: this demo uses your personal credentials for Google 22 | Cloud and a highly privileged API key for Atlas. It also configures Atlas 23 | to allow connections from any IP address. We do this to get you 24 | up and running as fast as possible, but in the real world you should follow 25 | Google Cloud's [best practices for service accounts](https://cloud.google.com/iam/docs/best-practices-service-accounts) 26 | and Atlas's [documentation](https://www.mongodb.com/docs/atlas/atlas-ui-authorization/) 27 | to properly secure your infrastructure. 28 | 29 | With that out of the way, let's get started! 30 | 31 | ### MongoDB Atlas 32 | 33 | If you don't already have a Atlas account, [sign up here](https://www.mongodb.com/cloud/atlas/register). 34 | If you are prompted to create a database, look for the "I'll do this later" link 35 | in the lower left corner. Once you're logged in, click on "Access Manager" at 36 | the top and select "Organization Access". 37 | 38 | Select the "API Keys" tab and click the "Create API Key" button. Give your new 39 | key a short description and select the "Organization Owner" permission. Click 40 | "Next" and then make a note of your public and private keys. This is your last 41 | chance to see the private key, so be sure you've written it down somewhere safe. 42 | 43 | Next, you'll need your Organization ID. Go to [the projects page](https://cloud.mongodb.com/v2#/org) 44 | and click "Settings" in the list on the left side of the window to get to the 45 | Organization Settings screen. Your organization ID is in a box in the upper-left 46 | corner of the window. Copy your Organization ID and save it with your credentials. 47 | 48 | That's everything for Atlas. Now you're ready to move on to setting up Google Cloud! 49 | 50 | ### Google Cloud 51 | 52 | If you don't already have a Google Cloud account, [sign up here](https://accounts.google.com/SignUp). 53 | You'll also need to [enable billing](https://console.cloud.google.com/billing) 54 | and set up a billing account. This demo is designed to qualify for the 55 | [free tier](https://cloud.google.com/free) but some of the services involved 56 | require billing to be enabled. See [this page](https://cloud.google.com/billing/docs/how-to/manage-billing-account#create_a_new_billing_account) 57 | for more information on setting up a billing account. Make a note of your Billing 58 | Account ID, listed [here](https://console.cloud.google.com/billing). 59 | 60 | We recommend using [Cloud Shell](https://cloud.google.com/shell) but if you prefer 61 | to work locally you can do that too. If you'd like to use Cloud Shell, just click 62 | this convenient button: 63 | 64 | [![Open in Cloud Shell](https://gstatic.com/cloudssh/images/open-btn.svg)](https://shell.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https://github.com/GoogleCloudPlatform/terraform-mean-cloudrun-mongodb) 65 | 66 | This will open a Cloud Shell window and clone a copy of this repository. Cloud 67 | Shell has your credentials built in, and Terraform is already installed, so 68 | you're ready to move on to [Choosing a Region](#choosing-a-region). You can also 69 | [open Cloud Shell the normal way](https://cloud.google.com/shell/docs/using-cloud-shell) 70 | but you'll need to clone this repo yourself. 71 | 72 | If you want to work locally, you'll need to [install the `gcloud` tool](https://cloud.google.com/sdk/docs/install). 73 | If you're on MacOS you can also install `gcloud` [via homebrew](https://formulae.brew.sh/cask/google-cloud-sdk). 74 | Once the install is done, run `gcloud init` to set up your environment. More 75 | information can be found [in the docs](https://cloud.google.com/sdk/docs/initializing). 76 | 77 | ### Installing Terraform 78 | 79 | If you're using Cloud Shell, you've already got Terraform installed so feel free 80 | to move on to the next section. Otherwise, [download Terraform](https://www.terraform.io/downloads) 81 | and install it. A detailed walkthrough can be found [in this tutorial](https://learn.hashicorp.com/tutorials/terraform/install-cli). 82 | 83 | ### Choosing a region 84 | 85 | Next, select a region for your infrastructure. For this demo it's okay to choose 86 | somewhere close to you, but for production use you may want to choose a different 87 | region. If you need some advice, check out [Best Practices for Compute Engine regions selection](https://cloud.google.com/solutions/best-practices-compute-engine-region-selection). 88 | 89 | For a list of available regions, refer Atlas's [Google Cloud provider documentation](https://www.mongodb.com/docs/atlas/reference/google-gcp/). 90 | Choose a region that's close to you and that supports the `M0` cluster tier. Make 91 | a note of both the Google Cloud region name and the Atlas region name, as you'll 92 | need both in the next step. 93 | 94 | ### Configuring the demo 95 | 96 | If you haven't already, clone this repo. Run `terraform init` to make sure 97 | Terraform is working correctly and download the provider plugins. 98 | 99 | Then, create a file in the root of the repository called `terraform.tfvars` with 100 | the following contents, replacing placeholders as necessary: 101 | 102 | atlas_pub_key = "" 103 | atlas_priv_key = "" 104 | atlas_org_id = "" 105 | google_billing_account = "" 106 | 107 | If you used the Open in Cloud Shell button, check to make sure that you're creating 108 | the `terraform.tfvars` file in the root of the repository. The Cloud Shell 109 | terminal will be in the right directory but the Cloud Shell editor may not. 110 | Double-check to be sure you're creating the file in the same directory as this 111 | README. 112 | 113 | If you selected the `us-central1`/`US_CENTRAL` region then you're ready to go. If 114 | you selected a different region, add the following to your `terraform.tfvars` file: 115 | 116 | atlas_cluster_region = "" 117 | google_cloud_region = "" 118 | 119 | Run `terraform init` again to make sure there are no new errors. If you get an 120 | error, check your `terraform.tfvars` file. 121 | 122 | ### Run it! 123 | 124 | You're ready to deploy! You have two options: you can run `terraform plan` to 125 | see a full listing of everything that Terraform wants to do without any risk of 126 | accidentally creating those resources. If everything looks good, you can then 127 | run `terraform apply` to execute the plan. 128 | 129 | Alternately, you can just run `terraform apply` on its own and it will create 130 | a plan and display it before prompting you to continue. You can learn more about 131 | the `plan` and `apply` commands in [this tutorial](https://learn.hashicorp.com/tutorials/terraform/plan). 132 | 133 | For this demo, we're going to just run `terraform apply`: 134 | 135 | $ terraform apply 136 | 137 | Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 138 | + create 139 | 140 | Terraform will perform the following actions: 141 | 142 | # mongodbatlas_cluster.cluster will be created 143 | + resource "mongodbatlas_cluster" "cluster" { 144 | + auto_scaling_compute_enabled = (known after apply) 145 | + auto_scaling_compute_scale_down_enabled = (known after apply) 146 | + auto_scaling_disk_gb_enabled = true 147 | + backing_provider_name = "GCP" 148 | 149 | [ ... snip ... ] 150 | 151 | Plan: 6 to add, 0 to change, 0 to destroy. 152 | 153 | Do you want to perform these actions? 154 | Terraform will perform the actions described above. 155 | Only 'yes' will be accepted to approve. 156 | 157 | Enter a value: 158 | 159 | If everything looks good to you, type `yes` and press enter, then go grab a snack 160 | while Terraform sets everything up for you! When it's done, Terraform will display 161 | the URL of your application and your deployment repo: 162 | 163 | [ ... snip ... ] 164 | Apply complete! Resources: 10 added, 0 changed, 0 destroyed. 165 | 166 | Outputs: 167 | 168 | app_url = "https://" 169 | repo_url = "https://source.developers.google.com/p//r/repo" 170 | 171 | Load the `app_url` up in your browser and you'll see the sample app running! 172 | 173 | ### Making and deploying changes 174 | 175 | If you'd like to experiment with the CI/CD pipeline, now's your chance. First, 176 | clone a copy of [this fork of the sample application](https://github.com/bleything/mean-stack-example/). 177 | Next, add the `repo_url` from the Terraform output as a remote: 178 | 179 | $ git remote add deploy 180 | 181 | Now you can make some changes to the application. For a simple change, try 182 | adding a new level in `[client/src/app/employee-form/employee-form.component.ts]`. 183 | If you want to try something more complex, you could try adding a new field. 184 | 185 | When you've made your changes, commit them and then push to your deploy repo: 186 | 187 | $ git push deploy main 188 | 189 | This will kick off a build that will build the application, run `npm test`, and 190 | deploy the new code. You can monitor the build from the [Build history](https://console.cloud.google.com/cloud-build/builds) 191 | page. 192 | 193 | You can also use this pipeline to deploy your own code. See the [Next Steps](#next-steps) 194 | section below for more details. 195 | 196 | ### Cleaning Up 197 | 198 | When you're done, run `terraform destroy` to clean everything up: 199 | 200 | $ terraform destroy 201 | 202 | [ ... snip ... ] 203 | 204 | Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 205 | - destroy 206 | 207 | Terraform will perform the following actions: 208 | 209 | [ ... snip ... ] 210 | 211 | Plan: 0 to add, 0 to change, 10 to destroy. 212 | 213 | Changes to Outputs: 214 | - app_url = "https://example.com" -> null 215 | 216 | Do you really want to destroy all resources? 217 | Terraform will destroy all your managed infrastructure, as shown above. 218 | There is no undo. Only 'yes' will be accepted to confirm. 219 | 220 | Enter a value: 221 | 222 | If you're sure you want to tear everything down, type `yes` and press enter. This 223 | will take a few minutes so now would be a great time for another break. When 224 | Terraform is done everything it created will have been destroyed and you 225 | will not be billed for any further usage. Note that you may still see the project 226 | listed in the project selector in the Google Cloud console. You can confirm it 227 | has been marked for deletion by going to the [Resources Pending Deletion](https://console.cloud.google.com/cloud-resource-manager?pendingDeletion=true) 228 | page and looking for it there. 229 | 230 | Next Steps 231 | -------------------------------------------------------------------------------- 232 | 233 | You can use the code in this repository to deploy your own applications. Out of 234 | the box, it will run any application that meets the following requirements: 235 | 236 | - runs in a single container 237 | - reads MongoDB connection string from an environment variable called `ATLAS_URI` 238 | 239 | ### Deploying your own container 240 | 241 | If you already have a container that meets those requirements, you can deploy it 242 | by adding a line to your `terraform.tfvars` file pointing to your container 243 | image: 244 | 245 | app_image = '' 246 | 247 | Note that if you do this after you've already run Terraform, you will need to 248 | [taint](https://www.terraform.io/cli/commands/taint) the Cloud Run service and 249 | run `terraform apply` again: 250 | 251 | $ terraform taint google_cloud_run_service.app 252 | Resource instance google_cloud_run_service.app has been marked as tainted. 253 | 254 | $ terraform apply 255 | 256 | Tainting a resource causes it to be deleted and re-created, so don't be surprised 257 | when you see that in the plan. 258 | 259 | ### Deploy via `git push` 260 | 261 | Another option is to use the CI/CD pipeline to build and deploy your own code. 262 | To do so there is one extra requirement: your application must be able to 263 | successfully run `npm install` and `npm test`. 264 | 265 | The process is essentially the same as described in the [Making and deploying changes](#making-and-deploying-changes) 266 | section above, except you'd add the remote to your own repository instead of 267 | the demo application. 268 | 269 | If you've already pushed other code to the deploy repo, you'll need to use 270 | a force push the first time: 271 | 272 | $ git push --force deploy main 273 | 274 | As before, you can see the build status on the [Build history](https://console.cloud.google.com/cloud-build/builds) 275 | page. 276 | 277 | ### Other notes 278 | 279 | If you need to add or change the environment variables that get passed into the 280 | container, take a look at the `google.tf` file. You can add additional `env` blocks 281 | next to the `ATLAS_URI` block, and/or modify that block to fit your needs. 282 | 283 | You can also add more `google_cloud_run_service` blocks to deploy additional 284 | services on Cloud Run, just make sure to also include a `google_cloud_run_service_iam_binding` 285 | block if that service needs to be accessible to the public. For more information, 286 | see the [`google_cloud_run_service`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloud_run_service) 287 | and [`google_cloud_run_service_iam_binding`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloud_run_service_iam) 288 | pages in the Terraform provider documentation, as well as the [Cloud Run IAM documentation](https://cloud.google.com/run/docs/securing/managing-access). 289 | --------------------------------------------------------------------------------