-
Notifications
You must be signed in to change notification settings - Fork 2k
run: add hello-broken sample #1480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
fb380c9
1df2198
f316b33
3ebb6c9
b85de4d
8bb8199
6a9a8aa
eb5bb2c
09f7f49
a06ed59
d2e195f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # Copyright 2019 Google LLC. All rights reserved. | ||
| # Use of this source code is governed by the Apache 2.0 | ||
| # license that can be found in the LICENSE file. | ||
|
|
||
| # [START run_broken_dockerfile] | ||
|
|
||
| # Use the official lightweight Node.js 10 image. | ||
| # https://hub.docker.com/_/node | ||
| FROM node:10-slim | ||
|
|
||
| # Create and change to the app directory. | ||
| WORKDIR /usr/src/app | ||
|
|
||
| # Copy application dependency manifests to the container image. | ||
| # A wildcard is used to ensure copying both package.json AND package-lock.json (when available). | ||
| # Copying this first prevents re-running npm install on every code change. | ||
| COPY package*.json ./ | ||
|
|
||
| # Install production dependencies. | ||
| # If you add a package-lock.json, speed your build by switching to 'npm ci'. | ||
| # RUN npm ci --only=production | ||
| RUN npm install --only=production | ||
|
|
||
| # Copy local code to the container image. | ||
| COPY . ./ | ||
|
|
||
| # Run the web service on container startup. | ||
| CMD [ "npm", "start" ] | ||
|
|
||
| # [END run_broken_dockerfile] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| # Cloud Run Broken Sample | ||
|
|
||
| This sample presents broken code in need of troubleshooting. An alternate | ||
| resource at `/improved` shows a more stable implementation with more informative | ||
| errors and default values. | ||
|
|
||
| Use it with the [Local Container Troubleshooting tutorial](http://cloud.google.com/run/docs/tutorials/local-troubleshooting). | ||
|
|
||
| For more details on how to work with this sample read the [Google Cloud Run Node.js Samples README](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/run). | ||
|
|
||
| ## Local Development | ||
|
|
||
| ### `npm run e2e-test` | ||
|
|
||
| ``` | ||
| export SERVICE_NAME=broken | ||
| export CONTAINER_IMAGE=gcr.io/${GOOGLE_CLOUD_PROJECT}/broken | ||
| npm run e2e-test | ||
| ``` | ||
|
|
||
| ## Using Testing Scripts | ||
|
|
||
| ### url.sh | ||
|
|
||
| The `url.sh` script derives the automatically provisioned URL of a deployed | ||
| Cloud Run service. | ||
|
|
||
| ```sh | ||
| export SERVICE_NAME=broken | ||
| export REGION=us-central1 | ||
| test/url.sh | ||
| ``` | ||
|
|
||
| ### deploy.sh | ||
|
|
||
| The `deploy.sh` script deploys a Cloud Run service. | ||
|
|
||
| ```sh | ||
| export SERVICE_NAME=broken | ||
| export CONTAINER_IMAGE=gcr.io/${GOOGLE_CLOUD_PROJECT}/broken | ||
| export REGION=us-central1 | ||
| test/deploy.sh | ||
| ``` | ||
|
|
||
| ### runner.sh | ||
|
|
||
| The `runner.sh` script is a helper that facilitates: | ||
|
|
||
| * Deploy the service to Cloud Run based on the `deploy.sh` script. | ||
grayside marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| * Set the `BASE_URL` and `ID_TOKEN` environment variables. | ||
| * Run any arguments passed to the `runner.sh` script. | ||
| * Tear down the Cloud Run service on completion. | ||
|
|
||
| ```sh | ||
| test/runner.sh sleep 20 | ||
| ``` | ||
|
|
||
| ## Environment Variables (Testing) | ||
|
|
||
| * `BASE_URL`: Used to designate the URL of the Cloud Run service under system test. The URL is not deterministic, so the options are to record it at deploy time or use the Cloud Run API to retrieve the value. | ||
grayside marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| * `ID_TOKEN`: Used for authenticated HTTP requests to the Cloud Run service. | ||
| * `REGION`: [`us-central1`] Optional override region for the location of the Cloud Run service. | ||
| * `SERVICE_NAME`: Used in testing to specify the name of the service. The name may be included in API calls and test conditions. | ||
| * `GOOGLE_CLOUD_PROJECT`: Used in production as an override to determination of the GCP Project from the GCP metadata server. Used in testing to configure the Logging client library. | ||
|
|
||
| ## Dependencies | ||
|
|
||
| * **express**: Web server framework. | ||
| * **got**: [Testing] Used to make HTTP requests of the running service in end-to-end testing. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // Copyright 2019 Google LLC. All rights reserved. | ||
| // Use of this source code is governed by the Apache 2.0 | ||
| // license that can be found in the LICENSE file. | ||
|
|
||
| // [START run_broken_service] | ||
| const express = require('express'); | ||
| const app = express(); | ||
|
|
||
| app.get('/', (req, res) => { | ||
| console.log('hello-broken: received request.'); | ||
|
|
||
| // [START run_broken_service_problem] | ||
| const {TARGET} = process.env; | ||
| if (!TARGET) { | ||
| // Plain error logs do not appear in Stackdriver Error Reporting. | ||
| console.error('Environment validation failed.'); | ||
| console.error(new Error('Missing required server parameter')); | ||
| res.status(500).send('Internal Server Error'); | ||
| return; | ||
grayside marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| // [END run_broken_service_problem] | ||
| res.send(`Hello ${TARGET}!`); | ||
| }); | ||
| // [END run_broken_service] | ||
|
|
||
| app.get('/improved', (req, res) => { | ||
| console.log('hello-broken: received request.'); | ||
|
|
||
| // [START run_broken_service_upgrade] | ||
| const TARGET = process.env.TARGET || 'World'; | ||
| if (!process.env.TARGET) { | ||
| console.log(JSON.stringify({ | ||
| severity: 'WARNING', | ||
| message: `TARGET not set, default to '${TARGET}'` | ||
| })); | ||
| } | ||
| // [END run_broken_service_upgrade] | ||
| res.send(`Hello ${TARGET}!`); | ||
| }); | ||
|
|
||
| // [START run_broken_service] | ||
| const port = process.env.PORT || 8080; | ||
| app.listen(port, () => { | ||
| console.log(`hello-broken: listening on port ${port}`); | ||
| }); | ||
| // [END run_broken_service] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| { | ||
| "name": "hello-broken", | ||
| "private": true, | ||
| "description": "Broken Cloud Run service for troubleshooting practice", | ||
| "main": "index.js", | ||
| "scripts": { | ||
| "start": "node index.js", | ||
| "test": "echo \"Error: no test specified\" && exit 0", | ||
| "e2e-test": "TARGET=Cloud test/runner.sh mocha test/system.test.js --timeout=20000" | ||
|
||
| }, | ||
| "author": "Google LLC", | ||
| "license": "Apache-2.0", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" | ||
| }, | ||
| "dependencies": { | ||
| "express": "^4.17.1" | ||
| }, | ||
| "devDependencies": { | ||
| "got": "^9.6.0" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| # Copyright 2019 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 -eo pipefail; | ||
|
|
||
| requireEnv() { | ||
| test "${!1}" || (echo "Environment Variable '$1' not found" && exit 1) | ||
| } | ||
|
|
||
| requireEnv SERVICE_NAME | ||
| requireEnv CONTAINER_IMAGE | ||
|
|
||
| # Deploy the service | ||
| set -x | ||
| gcloud beta --quiet run deploy "${SERVICE_NAME}" \ | ||
grayside marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| --image="${CONTAINER_IMAGE}" \ | ||
| --region="${REGION:-us-central1}" \ | ||
| ${FLAGS} \ | ||
| --platform=managed | ||
|
|
||
| echo 'Cloud Run Links:' | ||
| echo "- Logs: https://console.cloud.google.com/logs/viewer?project=${GOOGLE_CLOUD_PROJECT}&resource=cloud_run_revision%2Fservice_name%2F${SERVICE_NAME}" | ||
| echo "- Console: https://console.cloud.google.com/run/detail/${REGION:-us-central1}/${SERVICE_NAME}/metrics?project=${GOOGLE_CLOUD_PROJECT}" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| # Copyright 2019 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 -eo pipefail; | ||
|
|
||
| requireEnv() { | ||
| test "${!1}" || (echo "Environment Variable '$1' not found" && exit 1) | ||
| } | ||
| requireEnv SERVICE_NAME | ||
|
|
||
| # The hello-broken sample needs to be tested with the TARGET environment variable | ||
| # both set and unset. | ||
| SERVICE_OVERRIDE="${SERVICE_NAME}-override" | ||
|
|
||
| echo '---' | ||
| test/deploy.sh | ||
| FLAGS="--set-env-vars TARGET=$TARGET" SERVICE_NAME=${SERVICE_OVERRIDE} test/deploy.sh | ||
|
|
||
| echo | ||
| echo '---' | ||
| echo | ||
|
|
||
| # Register post-test cleanup. | ||
| # Only needed if deploy completed. | ||
| function cleanup { | ||
| set -x | ||
| gcloud --quiet beta run services delete ${SERVICE_NAME} \ | ||
grayside marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| --platform=managed \ | ||
| --region="${REGION:-us-central1}" | ||
| gcloud --quiet beta run services delete ${SERVICE_OVERRIDE} \ | ||
| --platform=managed \ | ||
| --region="${REGION:-us-central1}" | ||
| } | ||
| trap cleanup EXIT | ||
|
|
||
| # TODO: Perform authentication inside the test. | ||
| export ID_TOKEN=$(gcloud auth print-identity-token) | ||
| export BASE_URL=$(test/url.sh) | ||
| export BASE_URL_OVERRIDE=$(SERVICE_NAME=${SERVICE_OVERRIDE} test/url.sh) | ||
| # Do not use exec to preserve trap behavior. | ||
| "$@" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| // Copyright 2019 Google LLC. All rights reserved. | ||
grayside marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // Use of this source code is governed by the Apache 2.0 | ||
| // license that can be found in the LICENSE file. | ||
|
|
||
| const assert = require('assert'); | ||
| const request = require('got'); | ||
|
|
||
| const get = (route, base_url) => { | ||
| const {ID_TOKEN} = process.env; | ||
| if (!ID_TOKEN) { | ||
| throw Error('"ID_TOKEN" environment variable is required.'); | ||
| } | ||
|
|
||
| return request(route, { | ||
| baseUrl: base_url.trim(), | ||
| headers: { | ||
| Authorization: `Bearer ${ID_TOKEN.trim()}`, | ||
| }, | ||
| throwHttpErrors: false | ||
| }); | ||
| } | ||
|
|
||
| describe('Default Service', () => { | ||
| const {BASE_URL} = process.env; | ||
| if (!BASE_URL) { | ||
| throw Error( | ||
| '"BASE_URL" environment variable is required. For example: https://service-x8xabcdefg-uc.a.run.app' | ||
| ); | ||
| } | ||
|
|
||
| it('Broken resource fails on any request', async () => { | ||
| const response = await get('/', BASE_URL); | ||
| assert.strictEqual(response.statusCode, 500, 'Internal service error not found'); | ||
| }); | ||
|
|
||
| it('Fixed resource successfully falls back to a default value', async () => { | ||
| const response = await get('/improved', BASE_URL); | ||
| assert.strictEqual(response.statusCode, 200, 'Did not fallback to default as expected'); | ||
| assert.strictEqual(response.body, `Hello ${TARGET}!`, `Expected fallback "World" not found`); | ||
| }); | ||
| }); | ||
|
|
||
| describe('Overridden Service', () => { | ||
| const {BASE_URL_OVERRIDE} = process.env; | ||
| if (!BASE_URL_OVERRIDE) { | ||
| throw Error( | ||
| '"BASE_URL_OVERRIDE" environment variable is required. For example: https://service-x8xabcdefg-uc.a.run.app' | ||
| ); | ||
| } | ||
|
|
||
| const {TARGET} = process.env; | ||
| if (!TARGET) { | ||
| throw Error( | ||
| '"TARGET" environment variable is required. For example: Cosmos' | ||
| ); | ||
| } | ||
|
|
||
| it('Broken resource uses the TARGET override', async () => { | ||
| const response = await get('/', BASE_URL); | ||
| assert.strictEqual(response.statusCode, 200, 'Did not use the TARGET override'); | ||
| assert.strictEqual(response.body, `Hello ${TARGET}!`, `Expected override "${TARGET}" not found`); | ||
| }); | ||
|
|
||
| it('Fixed resource uses the TARGET override', async () => { | ||
| const response = await get('/improved', BASE_URL_OVERRIDE); | ||
| assert.strictEqual(response.statusCode, 200, 'Did not fallback to default as expected'); | ||
| assert.strictEqual(response.body, `Hello ${TARGET}!`, `Expected override "${TARGET}" not found`); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need a forward slash here.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While the previous practice was to use
COPY . ., this is a newer practice I think we should use in COPY statements. The issue is COPY supports having multiple source arguments and one destination argument, but if the source arguments are:Then the destination needs to have the trailing slash otherwise it will error.
This is intended to future proof the use of the Dockerfile should a developer use it as a template.