From d2fa9ec2b3b86039dfa326187c84e9f058631af3 Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Tue, 19 Jan 2016 10:17:24 -0800 Subject: [PATCH] Adding new Managed VMs samples. Existing samples have been renamed: * django_tutorial -> django_cloudsql * hello_world -> hello_world_compat * hello_world_custom -> (superseded by) hello_world --- managed_vms/.gitignore | 54 ++++++++ managed_vms/README.md | 68 ++++++++++ managed_vms/analytics/.dockerignore | 8 ++ managed_vms/analytics/Dockerfile | 15 +++ managed_vms/analytics/README.md | 20 +++ managed_vms/analytics/app.yaml | 7 ++ managed_vms/analytics/main.py | 64 ++++++++++ managed_vms/analytics/requirements.txt | 3 + managed_vms/cloudsql/.dockerignore | 8 ++ managed_vms/cloudsql/Dockerfile | 15 +++ managed_vms/cloudsql/README.md | 29 +++++ managed_vms/cloudsql/app.yaml | 13 ++ managed_vms/cloudsql/create_tables.py | 25 ++++ managed_vms/cloudsql/main.py | 67 ++++++++++ managed_vms/cloudsql/requirements.txt | 4 + managed_vms/datastore/.dockerignore | 8 ++ managed_vms/datastore/Dockerfile | 15 +++ managed_vms/datastore/README.md | 20 +++ managed_vms/datastore/app.yaml | 7 ++ managed_vms/datastore/main.py | 52 ++++++++ managed_vms/datastore/requirements.txt | 4 + managed_vms/disk/.dockerignore | 8 ++ managed_vms/disk/Dockerfile | 15 +++ .../{hello_world_custom => disk}/app.yaml | 1 - managed_vms/disk/main.py | 47 +++++++ .../requirements.txt | 0 .../.dockerignore | 0 .../Dockerfile | 0 .../README.md | 0 .../__init__.py | 0 .../app.yaml | 0 .../manage.py | 0 .../mysite/__init__.py | 0 .../mysite/settings.py | 0 .../mysite/urls.py | 0 .../mysite/wsgi.py | 0 .../polls/__init__.py | 0 .../polls/admin.py | 0 .../polls/apps.py | 0 .../polls/models.py | 0 .../polls/tests.py | 0 .../polls/urls.py | 0 .../polls/views.py | 0 .../requirements.txt | 0 managed_vms/extending_runtime/.dockerignore | 8 ++ managed_vms/extending_runtime/Dockerfile | 20 +++ managed_vms/extending_runtime/app.yaml | 2 + managed_vms/extending_runtime/main.py | 36 ++++++ .../extending_runtime/requirements.txt | 2 + managed_vms/hello_world/.dockerignore | 8 ++ managed_vms/hello_world/Dockerfile | 15 +++ managed_vms/hello_world/app.yaml | 9 +- managed_vms/hello_world/main.py | 6 + managed_vms/hello_world/requirements.txt | 1 + .../.gitignore | 0 .../README.md | 0 managed_vms/hello_world_compat/app.yaml | 9 ++ .../appengine_config.py | 0 managed_vms/hello_world_compat/main.py | 26 ++++ .../hello_world_compat/requirements.txt | 1 + managed_vms/hello_world_custom/Dockerfile | 3 - managed_vms/hello_world_custom/README.md | 20 --- managed_vms/hello_world_django/.dockerignore | 9 ++ managed_vms/hello_world_django/.gitignore | 1 + managed_vms/hello_world_django/Dockerfile | 15 +++ managed_vms/hello_world_django/README.md | 58 +++++++++ managed_vms/hello_world_django/app.yaml | 2 + .../hello_world_django/helloworld/__init__.py | 0 .../hello_world_django/helloworld/views.py | 22 ++++ managed_vms/hello_world_django/manage.py | 10 ++ .../project_name/__init__.py | 0 .../project_name/settings.py | 117 ++++++++++++++++++ .../hello_world_django/project_name/urls.py | 39 ++++++ .../hello_world_django/project_name/wsgi.py | 16 +++ .../hello_world_django/requirements.txt | 2 + managed_vms/mailgun/.dockerignore | 8 ++ managed_vms/mailgun/Dockerfile | 15 +++ managed_vms/mailgun/README.md | 24 ++++ managed_vms/mailgun/app.yaml | 8 ++ managed_vms/mailgun/example-attachment.txt | 1 + managed_vms/mailgun/main.py | 85 +++++++++++++ managed_vms/mailgun/requirements.txt | 3 + managed_vms/mailgun/templates/index.html | 30 +++++ managed_vms/memcache/.dockerignore | 8 ++ managed_vms/memcache/Dockerfile | 15 +++ managed_vms/memcache/app.yaml | 7 ++ managed_vms/memcache/main.py | 49 ++++++++ managed_vms/memcache/requirements.txt | 3 + managed_vms/pubsub/.dockerignore | 8 ++ managed_vms/pubsub/Dockerfile | 15 +++ managed_vms/pubsub/README.md | 72 +++++++++++ managed_vms/pubsub/app.yaml | 11 ++ managed_vms/pubsub/main.py | 74 +++++++++++ managed_vms/pubsub/requirements.txt | 4 + managed_vms/pubsub/sample_message.json | 5 + managed_vms/pubsub/templates/index.html | 38 ++++++ managed_vms/sendgrid/.dockerignore | 8 ++ managed_vms/sendgrid/Dockerfile | 15 +++ managed_vms/sendgrid/README.md | 24 ++++ managed_vms/sendgrid/app.yaml | 8 ++ managed_vms/sendgrid/main.py | 64 ++++++++++ managed_vms/sendgrid/requirements.txt | 3 + managed_vms/sendgrid/templates/index.html | 29 +++++ managed_vms/static_files/.dockerignore | 8 ++ managed_vms/static_files/Dockerfile | 15 +++ managed_vms/static_files/README.md | 7 ++ managed_vms/static_files/app.yaml | 2 + .../main.py | 7 +- managed_vms/static_files/requirements.txt | 2 + managed_vms/static_files/static/main.css | 21 ++++ managed_vms/static_files/templates/index.html | 29 +++++ managed_vms/storage/.dockerignore | 8 ++ managed_vms/storage/Dockerfile | 15 +++ managed_vms/storage/README.md | 33 +++++ managed_vms/storage/app.yaml | 8 ++ managed_vms/storage/main.py | 75 +++++++++++ managed_vms/storage/requirements.txt | 3 + managed_vms/tests/__init__.py | 0 managed_vms/tests/cloudsql_test.py | 29 +++++ managed_vms/tests/datastore_test.py | 29 +++++ managed_vms/tests/disk_test.py | 30 +++++ managed_vms/tests/extending_runtime_test.py | 35 ++++++ managed_vms/tests/hello_world_test.py | 28 +++++ managed_vms/tests/memcache_test.py | 29 +++++ managed_vms/tests/pubsub_test.py | 63 ++++++++++ managed_vms/tests/runserver.py | 88 +++++++++++++ managed_vms/tests/static_files_test.py | 31 +++++ managed_vms/tests/storage_test.py | 51 ++++++++ managed_vms/twilio/.dockerignore | 8 ++ managed_vms/twilio/Dockerfile | 15 +++ managed_vms/twilio/README.md | 29 +++++ managed_vms/twilio/app.yaml | 9 ++ managed_vms/twilio/main.py | 80 ++++++++++++ managed_vms/twilio/requirements.txt | 3 + managed_vms/websockets/.dockerignore | 8 ++ managed_vms/websockets/Dockerfile | 16 +++ managed_vms/websockets/README.md | 24 ++++ managed_vms/websockets/app.yaml | 9 ++ managed_vms/websockets/main.py | 73 +++++++++++ managed_vms/websockets/requirements.txt | 4 + managed_vms/websockets/templates/index.html | 94 ++++++++++++++ requirements-dev.txt | 3 + 142 files changed, 2653 insertions(+), 36 deletions(-) create mode 100644 managed_vms/.gitignore create mode 100644 managed_vms/README.md create mode 100644 managed_vms/analytics/.dockerignore create mode 100644 managed_vms/analytics/Dockerfile create mode 100644 managed_vms/analytics/README.md create mode 100644 managed_vms/analytics/app.yaml create mode 100644 managed_vms/analytics/main.py create mode 100644 managed_vms/analytics/requirements.txt create mode 100644 managed_vms/cloudsql/.dockerignore create mode 100644 managed_vms/cloudsql/Dockerfile create mode 100644 managed_vms/cloudsql/README.md create mode 100644 managed_vms/cloudsql/app.yaml create mode 100755 managed_vms/cloudsql/create_tables.py create mode 100644 managed_vms/cloudsql/main.py create mode 100644 managed_vms/cloudsql/requirements.txt create mode 100644 managed_vms/datastore/.dockerignore create mode 100644 managed_vms/datastore/Dockerfile create mode 100644 managed_vms/datastore/README.md create mode 100644 managed_vms/datastore/app.yaml create mode 100644 managed_vms/datastore/main.py create mode 100644 managed_vms/datastore/requirements.txt create mode 100644 managed_vms/disk/.dockerignore create mode 100644 managed_vms/disk/Dockerfile rename managed_vms/{hello_world_custom => disk}/app.yaml (62%) create mode 100644 managed_vms/disk/main.py rename managed_vms/{hello_world_custom => disk}/requirements.txt (100%) rename managed_vms/{django_tutorial => django_cloudsql}/.dockerignore (100%) rename managed_vms/{django_tutorial => django_cloudsql}/Dockerfile (100%) rename managed_vms/{django_tutorial => django_cloudsql}/README.md (100%) rename managed_vms/{django_tutorial => django_cloudsql}/__init__.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/app.yaml (100%) rename managed_vms/{django_tutorial => django_cloudsql}/manage.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/mysite/__init__.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/mysite/settings.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/mysite/urls.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/mysite/wsgi.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/polls/__init__.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/polls/admin.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/polls/apps.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/polls/models.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/polls/tests.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/polls/urls.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/polls/views.py (100%) rename managed_vms/{django_tutorial => django_cloudsql}/requirements.txt (100%) create mode 100644 managed_vms/extending_runtime/.dockerignore create mode 100644 managed_vms/extending_runtime/Dockerfile create mode 100644 managed_vms/extending_runtime/app.yaml create mode 100644 managed_vms/extending_runtime/main.py create mode 100644 managed_vms/extending_runtime/requirements.txt create mode 100644 managed_vms/hello_world/.dockerignore create mode 100644 managed_vms/hello_world/Dockerfile rename managed_vms/{hello_world => hello_world_compat}/.gitignore (100%) rename managed_vms/{hello_world => hello_world_compat}/README.md (100%) create mode 100644 managed_vms/hello_world_compat/app.yaml rename managed_vms/{hello_world => hello_world_compat}/appengine_config.py (100%) create mode 100644 managed_vms/hello_world_compat/main.py create mode 100644 managed_vms/hello_world_compat/requirements.txt delete mode 100644 managed_vms/hello_world_custom/Dockerfile delete mode 100644 managed_vms/hello_world_custom/README.md create mode 100644 managed_vms/hello_world_django/.dockerignore create mode 100644 managed_vms/hello_world_django/.gitignore create mode 100644 managed_vms/hello_world_django/Dockerfile create mode 100644 managed_vms/hello_world_django/README.md create mode 100644 managed_vms/hello_world_django/app.yaml create mode 100644 managed_vms/hello_world_django/helloworld/__init__.py create mode 100644 managed_vms/hello_world_django/helloworld/views.py create mode 100755 managed_vms/hello_world_django/manage.py create mode 100644 managed_vms/hello_world_django/project_name/__init__.py create mode 100644 managed_vms/hello_world_django/project_name/settings.py create mode 100644 managed_vms/hello_world_django/project_name/urls.py create mode 100644 managed_vms/hello_world_django/project_name/wsgi.py create mode 100644 managed_vms/hello_world_django/requirements.txt create mode 100644 managed_vms/mailgun/.dockerignore create mode 100644 managed_vms/mailgun/Dockerfile create mode 100644 managed_vms/mailgun/README.md create mode 100644 managed_vms/mailgun/app.yaml create mode 100644 managed_vms/mailgun/example-attachment.txt create mode 100644 managed_vms/mailgun/main.py create mode 100644 managed_vms/mailgun/requirements.txt create mode 100644 managed_vms/mailgun/templates/index.html create mode 100644 managed_vms/memcache/.dockerignore create mode 100644 managed_vms/memcache/Dockerfile create mode 100644 managed_vms/memcache/app.yaml create mode 100644 managed_vms/memcache/main.py create mode 100644 managed_vms/memcache/requirements.txt create mode 100644 managed_vms/pubsub/.dockerignore create mode 100644 managed_vms/pubsub/Dockerfile create mode 100644 managed_vms/pubsub/README.md create mode 100644 managed_vms/pubsub/app.yaml create mode 100644 managed_vms/pubsub/main.py create mode 100644 managed_vms/pubsub/requirements.txt create mode 100644 managed_vms/pubsub/sample_message.json create mode 100644 managed_vms/pubsub/templates/index.html create mode 100644 managed_vms/sendgrid/.dockerignore create mode 100644 managed_vms/sendgrid/Dockerfile create mode 100644 managed_vms/sendgrid/README.md create mode 100644 managed_vms/sendgrid/app.yaml create mode 100644 managed_vms/sendgrid/main.py create mode 100644 managed_vms/sendgrid/requirements.txt create mode 100644 managed_vms/sendgrid/templates/index.html create mode 100644 managed_vms/static_files/.dockerignore create mode 100644 managed_vms/static_files/Dockerfile create mode 100644 managed_vms/static_files/README.md create mode 100644 managed_vms/static_files/app.yaml rename managed_vms/{hello_world_custom => static_files}/main.py (83%) create mode 100644 managed_vms/static_files/requirements.txt create mode 100644 managed_vms/static_files/static/main.css create mode 100644 managed_vms/static_files/templates/index.html create mode 100644 managed_vms/storage/.dockerignore create mode 100644 managed_vms/storage/Dockerfile create mode 100644 managed_vms/storage/README.md create mode 100644 managed_vms/storage/app.yaml create mode 100644 managed_vms/storage/main.py create mode 100644 managed_vms/storage/requirements.txt create mode 100644 managed_vms/tests/__init__.py create mode 100644 managed_vms/tests/cloudsql_test.py create mode 100644 managed_vms/tests/datastore_test.py create mode 100644 managed_vms/tests/disk_test.py create mode 100644 managed_vms/tests/extending_runtime_test.py create mode 100644 managed_vms/tests/hello_world_test.py create mode 100644 managed_vms/tests/memcache_test.py create mode 100644 managed_vms/tests/pubsub_test.py create mode 100644 managed_vms/tests/runserver.py create mode 100644 managed_vms/tests/static_files_test.py create mode 100644 managed_vms/tests/storage_test.py create mode 100644 managed_vms/twilio/.dockerignore create mode 100644 managed_vms/twilio/Dockerfile create mode 100644 managed_vms/twilio/README.md create mode 100644 managed_vms/twilio/app.yaml create mode 100644 managed_vms/twilio/main.py create mode 100644 managed_vms/twilio/requirements.txt create mode 100644 managed_vms/websockets/.dockerignore create mode 100644 managed_vms/websockets/Dockerfile create mode 100644 managed_vms/websockets/README.md create mode 100644 managed_vms/websockets/app.yaml create mode 100644 managed_vms/websockets/main.py create mode 100644 managed_vms/websockets/requirements.txt create mode 100644 managed_vms/websockets/templates/index.html diff --git a/managed_vms/.gitignore b/managed_vms/.gitignore new file mode 100644 index 000000000000..11a5d337ae22 --- /dev/null +++ b/managed_vms/.gitignore @@ -0,0 +1,54 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ diff --git a/managed_vms/README.md b/managed_vms/README.md new file mode 100644 index 000000000000..e9f5947aba27 --- /dev/null +++ b/managed_vms/README.md @@ -0,0 +1,68 @@ +## Google App Engine Managed VMs Python Samples + +These are samples for using Python on Google App Engine Managed VMs. These samples are typically referenced from the [docs](https://cloud.google.com/appengine/docs). + +See our other [Google Cloud Platform github repos](https://github.com/GoogleCloudPlatform) for sample applications and +scaffolding for other frameworks and use cases. + +## Run Locally + +Some samples have specific instructions. If there is a README in the sample folder, pleaese refer to it for any additional steps required to run the sample. + +In general, the samples typically require: + +1. Install the [Google Cloud SDK](https://cloud.google.com/sdk/), including the [gcloud tool](https://cloud.google.com/sdk/gcloud/), and [gcloud app component](https://cloud.google.com/sdk/gcloud-app). + +2. Setup the gcloud tool. This provides authentication to Google Cloud APIs and services. + + ``` + gcloud init + ``` + +3. Clone this repo. + + ``` + git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git + cd python-docs-samples/managed_vms + ``` + +4. Open a sample folder, create a virtualenv, install dependencies, and run the sample: + + ``` + cd hello-world + virtualenv env + source env/bin/activate + pip install -r requirements.txt + python main.py + ``` + +5. Visit the application at [http://localhost:8080](http://localhost:8080). + + +## Deploying + +Some samples in this repositories may have special deployment instructions. Refer to the readme in the sample directory. + +1. Use the [Google Developers Console](https://console.developer.google.com) to create a project/app id. (App id and project id are identical) + +2. Setup the gcloud tool, if you haven't already. + + ``` + gcloud init + ``` + +3. Use gcloud to deploy your app. + + ``` + gcloud preview app deploy app.yaml + ``` + +4. Congratulations! Your application is now live at `your-app-id.appspot.com` + +## Contributing changes + +* See [CONTRIBUTING.md](../CONTRIBUTING.md) + +## Licensing + +* See [LICENSE](../LICENSE) diff --git a/managed_vms/analytics/.dockerignore b/managed_vms/analytics/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/analytics/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/analytics/Dockerfile b/managed_vms/analytics/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/analytics/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/analytics/README.md b/managed_vms/analytics/README.md new file mode 100644 index 000000000000..ee2d73b84492 --- /dev/null +++ b/managed_vms/analytics/README.md @@ -0,0 +1,20 @@ +# Google Analytics Measurement Protocol sample for Google App Engine Managed VMs + +This sample demonstrates how to use the [Google Analytics Measurement Protocol](https://developers.google.com/analytics/devguides/collection/protocol/v1/) (or any other SQL server) on [Google App Engine Managed VMs](https://cloud.google.com/appengine). + +## Setup + +Before you can run or deploy the sample, you will need to do the following: + +1. Create a Google Analytics Property and obtain the Tracking ID. + +2. Update the environment variables in in ``app.yaml`` with your Tracking ID. + +## Running locally + +Refer to the [top-level README](../README.md) for instructions on running and deploying. + +You will need to set the following environment variables via your shell before running the sample: + + $ export GA_TRACKING_ID=[your Tracking ID] + $ python main.py diff --git a/managed_vms/analytics/app.yaml b/managed_vms/analytics/app.yaml new file mode 100644 index 000000000000..9635bd7784b1 --- /dev/null +++ b/managed_vms/analytics/app.yaml @@ -0,0 +1,7 @@ +runtime: custom +vm: true + +#[START env] +env_variables: + GA_TRACKING_ID: your-tracking-id +#[END env] diff --git a/managed_vms/analytics/main.py b/managed_vms/analytics/main.py new file mode 100644 index 000000000000..bdf24ec8164f --- /dev/null +++ b/managed_vms/analytics/main.py @@ -0,0 +1,64 @@ +# 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. + +# [START app] +import os + +from flask import Flask +import requests + + +app = Flask(__name__) + + +# Environment variables are defined in app.yaml. +GA_TRACKING_ID = os.environ['GA_TRACKING_ID'] + + +def track_event(category, action, label=None, value=None): + data = { + 'v': '1', # API Version. + 'tid': GA_TRACKING_ID, # Tracking ID / Property ID. + # Anonymous Client Identifier. Ideally, this should be a UUID that + # is associated with particular user, device, or browser instance. + 'cid': '555', + 't': 'event', # Event hit type. + 'ec': category, # Event category. + 'ea': action, # Event action. + 'el': label, # Event label. + 'ev': value, # Event valueself. + } + + response = requests.post( + 'http://www.google-analytics.com/collect', data=data) + + # If the request fails, this will raise a RequestException. Depending + # on your application's needs, this may be a non-error and can be caught + # by the caller. + response.raise_for_status() + + +@app.route('/') +def track_example(): + track_event( + category='Example', + action='test action') + return 'Event tracked.' + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) +# [END app] diff --git a/managed_vms/analytics/requirements.txt b/managed_vms/analytics/requirements.txt new file mode 100644 index 000000000000..1aa21f0eb352 --- /dev/null +++ b/managed_vms/analytics/requirements.txt @@ -0,0 +1,3 @@ +Flask==0.10.1 +gunicorn==19.4.5 +requests[security]==2.9.1 diff --git a/managed_vms/cloudsql/.dockerignore b/managed_vms/cloudsql/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/cloudsql/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/cloudsql/Dockerfile b/managed_vms/cloudsql/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/cloudsql/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/cloudsql/README.md b/managed_vms/cloudsql/README.md new file mode 100644 index 000000000000..b6e09b31a039 --- /dev/null +++ b/managed_vms/cloudsql/README.md @@ -0,0 +1,29 @@ +# Python Google Cloud SQL sample for Google App Engine Managed VMs + +This sample demonstrates how to use [Google Cloud SQL](https://cloud.google.com/sql/) (or any other SQL server) on [Google App Engine Managed VMs](https://cloud.google.com/appengine). + +## Setup + +Before you can run or deploy the sample, you will need to do the following: + +1. Create a Cloud SQL instance. You can do this from the [Google Developers Console](https://console.developers.google.com) or via the [Cloud SDK](https://cloud.google.com/sdk). To create it via the SDK use the following command: + + $ gcloud sql instances create [your-instance-name] \ + --assign-ip \ + --authorized-networks 0.0.0.0/0 \ + --tier D0 + +2. Create a new user and database for the application. The easiest way to do this is via the [Google Developers Console](https://console.developers.google.com/project/_/sql/instances/example-instance2/access-control/users). Alternatively, you can use MySQL tools such as the command line client or workbench, but you will need to set a root password for your database using `gcloud sql instances set-root-password`. + +3. Update the connection string in ``app.yaml`` with your instance values. + +4. Finally, run ``create_tables.py`` to ensure that the database is properly configured and to create the tables needed for the sample. + +## Running locally + +Refer to the [top-level README](../README.md) for instructions on running and deploying. + +You will need to set the following environment variables via your shell before running the sample: + + $ export SQLALCHEMY_DATABASE_URI=[your connection string] + $ python main.py diff --git a/managed_vms/cloudsql/app.yaml b/managed_vms/cloudsql/app.yaml new file mode 100644 index 000000000000..183dd2eb9260 --- /dev/null +++ b/managed_vms/cloudsql/app.yaml @@ -0,0 +1,13 @@ +runtime: custom +vm: true + +#[START env] +env_variables: + # Replace user, password, and host with the values obtained when + # configuring your Cloud SQL instance. + SQLALCHEMY_DATABASE_URI: mysql+pymysql://user:password@host/db + # If you are connecting over SSL, you can specify your certificates by + # using a connection string such as: + # > mysql+pymysql://user:password@host/db? + # ssl_key=client-key.pem?ssl_cert=client-cert.pem?ssl_ca=server-ca.pem +#[END env] diff --git a/managed_vms/cloudsql/create_tables.py b/managed_vms/cloudsql/create_tables.py new file mode 100755 index 000000000000..2a71c66b0143 --- /dev/null +++ b/managed_vms/cloudsql/create_tables.py @@ -0,0 +1,25 @@ +#! /usr/bin/env python +# 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. + +# [START all] + +from main import db + + +if __name__ == '__main__': + print('Creating all database tables...') + db.create_all() + print('Done!') +# [END all] diff --git a/managed_vms/cloudsql/main.py b/managed_vms/cloudsql/main.py new file mode 100644 index 000000000000..a1e321f58108 --- /dev/null +++ b/managed_vms/cloudsql/main.py @@ -0,0 +1,67 @@ +# 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. + +import datetime +import os + +from flask import Flask, request +from flask.ext.sqlalchemy import SQLAlchemy + + +app = Flask(__name__) + + +# [START example] +# Environment variables are defined in app.yaml. +app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['SQLALCHEMY_DATABASE_URI'] + +db = SQLAlchemy(app) + + +class Visit(db.Model): + id = db.Column(db.Integer, primary_key=True) + timestamp = db.Column(db.DateTime()) + user_ip = db.Column(db.String(46)) + + def __init__(self, timestamp, user_ip): + self.timestamp = timestamp + self.user_ip = user_ip + + +@app.route('/') +def index(): + visit = Visit( + user_ip=request.remote_addr, + timestamp=datetime.datetime.utcnow() + ) + + db.session.add(visit) + db.session.commit() + + visits = Visit.query.order_by(-Visit.timestamp).limit(10) + + results = [ + 'Time: {} Addr: {}'.format(x.timestamp, x.user_ip) + for x in visits] + + output = 'Last 10 visits:\n{}'.format('\n'.join(results)) + + return output, 200, {'Content-Type': 'text/plain; charset=utf-8'} +# [END example] + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) diff --git a/managed_vms/cloudsql/requirements.txt b/managed_vms/cloudsql/requirements.txt new file mode 100644 index 000000000000..07649fe277d6 --- /dev/null +++ b/managed_vms/cloudsql/requirements.txt @@ -0,0 +1,4 @@ +Flask==0.10.1 +Flask-SQLAlchemy==2.1 +gunicorn==19.4.5 +PyMySQL==0.7.1 diff --git a/managed_vms/datastore/.dockerignore b/managed_vms/datastore/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/datastore/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/datastore/Dockerfile b/managed_vms/datastore/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/datastore/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/datastore/README.md b/managed_vms/datastore/README.md new file mode 100644 index 000000000000..62da94e5dbd8 --- /dev/null +++ b/managed_vms/datastore/README.md @@ -0,0 +1,20 @@ +# Python Google Cloud Datastore sample for Google App Engine Managed VMs + +This sample demonstrates how to use [Google Cloud Datastore](https://cloud.google.com/datastore/) on [Google App Engine Managed VMs](https://cloud.google.com/appengine). + +## Setup + +Before you can run or deploy the sample, you will need to enable the Cloud Datastore API in the [Google Developers Console](https://console.developers.google.com/project/_/apiui/apiview/datastore/overview). + +## Running locally + +Refer to the [top-level README](../README.md) for instructions on running and deploying. + +When running locally, you can use the [Google Cloud SDK](https://cloud.google.com/sdk) to provide authentication to use Google Cloud APIs: + + $ gcloud init + +Set the ``GCLOUD_DATASET_ID`` environment variable to your Project ID before starting your application: + + $ export GCLOUD_DATASET_ID=[your-project-id] + $ python main.py diff --git a/managed_vms/datastore/app.yaml b/managed_vms/datastore/app.yaml new file mode 100644 index 000000000000..2ce99560bb9d --- /dev/null +++ b/managed_vms/datastore/app.yaml @@ -0,0 +1,7 @@ +runtime: custom +vm: true + +# [START env_variables] +env_variables: + GCLOUD_DATASET_ID: your-project-id +# [END env_variables] diff --git a/managed_vms/datastore/main.py b/managed_vms/datastore/main.py new file mode 100644 index 000000000000..6041c69747f0 --- /dev/null +++ b/managed_vms/datastore/main.py @@ -0,0 +1,52 @@ +# 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. + +import datetime + +from flask import Flask, request +from gcloud import datastore + + +app = Flask(__name__) + + +# [START example] +@app.route('/') +def index(): + ds = datastore.Client() + + entity = datastore.Entity(key=ds.key('visit')) + entity.update({ + 'user_ip': request.remote_addr, + 'timestamp': datetime.datetime.utcnow() + }) + + ds.put(entity) + + query = ds.query(kind='visit', order=('-timestamp',)) + + results = [ + 'Time: {timestamp} Addr: {user_ip}'.format(**x) + for x in query.fetch(limit=10)] + + output = 'Last 10 visits:\n{}'.format('\n'.join(results)) + + return output, 200, {'Content-Type': 'text/plain; charset=utf-8'} +# [END example] + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) diff --git a/managed_vms/datastore/requirements.txt b/managed_vms/datastore/requirements.txt new file mode 100644 index 000000000000..ee14266131e9 --- /dev/null +++ b/managed_vms/datastore/requirements.txt @@ -0,0 +1,4 @@ +Flask==0.10.1 +gcloud==0.8.0 +gunicorn==19.4.5 +oauth2client==1.5.2 diff --git a/managed_vms/disk/.dockerignore b/managed_vms/disk/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/disk/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/disk/Dockerfile b/managed_vms/disk/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/disk/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/hello_world_custom/app.yaml b/managed_vms/disk/app.yaml similarity index 62% rename from managed_vms/hello_world_custom/app.yaml rename to managed_vms/disk/app.yaml index a55f406cf63b..e8b73970590e 100644 --- a/managed_vms/hello_world_custom/app.yaml +++ b/managed_vms/disk/app.yaml @@ -1,3 +1,2 @@ runtime: custom vm: true -api_version: 1 diff --git a/managed_vms/disk/main.py b/managed_vms/disk/main.py new file mode 100644 index 000000000000..21f2d7ad1f3f --- /dev/null +++ b/managed_vms/disk/main.py @@ -0,0 +1,47 @@ +# 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. + +import os + +from flask import Flask, request + + +app = Flask(__name__) + + +# [START example] +@app.route('/') +def index(): + instance_id = os.environ.get('GAE_MODULE_INSTANCE', '1') + user_ip = request.remote_addr + + with open('/tmp/seen.txt', 'a') as f: + f.write('{}\n'.format(user_ip)) + + with open('/tmp/seen.txt', 'r') as f: + seen = f.read() + + output = """ +Instance: {} +Seen: +{}""".format(instance_id, seen) + + return output, 200, {'Content-Type': 'text/plain; charset=utf-8'} +# [END example] + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) diff --git a/managed_vms/hello_world_custom/requirements.txt b/managed_vms/disk/requirements.txt similarity index 100% rename from managed_vms/hello_world_custom/requirements.txt rename to managed_vms/disk/requirements.txt diff --git a/managed_vms/django_tutorial/.dockerignore b/managed_vms/django_cloudsql/.dockerignore similarity index 100% rename from managed_vms/django_tutorial/.dockerignore rename to managed_vms/django_cloudsql/.dockerignore diff --git a/managed_vms/django_tutorial/Dockerfile b/managed_vms/django_cloudsql/Dockerfile similarity index 100% rename from managed_vms/django_tutorial/Dockerfile rename to managed_vms/django_cloudsql/Dockerfile diff --git a/managed_vms/django_tutorial/README.md b/managed_vms/django_cloudsql/README.md similarity index 100% rename from managed_vms/django_tutorial/README.md rename to managed_vms/django_cloudsql/README.md diff --git a/managed_vms/django_tutorial/__init__.py b/managed_vms/django_cloudsql/__init__.py similarity index 100% rename from managed_vms/django_tutorial/__init__.py rename to managed_vms/django_cloudsql/__init__.py diff --git a/managed_vms/django_tutorial/app.yaml b/managed_vms/django_cloudsql/app.yaml similarity index 100% rename from managed_vms/django_tutorial/app.yaml rename to managed_vms/django_cloudsql/app.yaml diff --git a/managed_vms/django_tutorial/manage.py b/managed_vms/django_cloudsql/manage.py similarity index 100% rename from managed_vms/django_tutorial/manage.py rename to managed_vms/django_cloudsql/manage.py diff --git a/managed_vms/django_tutorial/mysite/__init__.py b/managed_vms/django_cloudsql/mysite/__init__.py similarity index 100% rename from managed_vms/django_tutorial/mysite/__init__.py rename to managed_vms/django_cloudsql/mysite/__init__.py diff --git a/managed_vms/django_tutorial/mysite/settings.py b/managed_vms/django_cloudsql/mysite/settings.py similarity index 100% rename from managed_vms/django_tutorial/mysite/settings.py rename to managed_vms/django_cloudsql/mysite/settings.py diff --git a/managed_vms/django_tutorial/mysite/urls.py b/managed_vms/django_cloudsql/mysite/urls.py similarity index 100% rename from managed_vms/django_tutorial/mysite/urls.py rename to managed_vms/django_cloudsql/mysite/urls.py diff --git a/managed_vms/django_tutorial/mysite/wsgi.py b/managed_vms/django_cloudsql/mysite/wsgi.py similarity index 100% rename from managed_vms/django_tutorial/mysite/wsgi.py rename to managed_vms/django_cloudsql/mysite/wsgi.py diff --git a/managed_vms/django_tutorial/polls/__init__.py b/managed_vms/django_cloudsql/polls/__init__.py similarity index 100% rename from managed_vms/django_tutorial/polls/__init__.py rename to managed_vms/django_cloudsql/polls/__init__.py diff --git a/managed_vms/django_tutorial/polls/admin.py b/managed_vms/django_cloudsql/polls/admin.py similarity index 100% rename from managed_vms/django_tutorial/polls/admin.py rename to managed_vms/django_cloudsql/polls/admin.py diff --git a/managed_vms/django_tutorial/polls/apps.py b/managed_vms/django_cloudsql/polls/apps.py similarity index 100% rename from managed_vms/django_tutorial/polls/apps.py rename to managed_vms/django_cloudsql/polls/apps.py diff --git a/managed_vms/django_tutorial/polls/models.py b/managed_vms/django_cloudsql/polls/models.py similarity index 100% rename from managed_vms/django_tutorial/polls/models.py rename to managed_vms/django_cloudsql/polls/models.py diff --git a/managed_vms/django_tutorial/polls/tests.py b/managed_vms/django_cloudsql/polls/tests.py similarity index 100% rename from managed_vms/django_tutorial/polls/tests.py rename to managed_vms/django_cloudsql/polls/tests.py diff --git a/managed_vms/django_tutorial/polls/urls.py b/managed_vms/django_cloudsql/polls/urls.py similarity index 100% rename from managed_vms/django_tutorial/polls/urls.py rename to managed_vms/django_cloudsql/polls/urls.py diff --git a/managed_vms/django_tutorial/polls/views.py b/managed_vms/django_cloudsql/polls/views.py similarity index 100% rename from managed_vms/django_tutorial/polls/views.py rename to managed_vms/django_cloudsql/polls/views.py diff --git a/managed_vms/django_tutorial/requirements.txt b/managed_vms/django_cloudsql/requirements.txt similarity index 100% rename from managed_vms/django_tutorial/requirements.txt rename to managed_vms/django_cloudsql/requirements.txt diff --git a/managed_vms/extending_runtime/.dockerignore b/managed_vms/extending_runtime/.dockerignore new file mode 100644 index 000000000000..cc6c24ef97f9 --- /dev/null +++ b/managed_vms/extending_runtime/.dockerignore @@ -0,0 +1,8 @@ +env +*.pyc +__pycache__ +.dockerignore +Dockerfile +.git +.hg +.svn diff --git a/managed_vms/extending_runtime/Dockerfile b/managed_vms/extending_runtime/Dockerfile new file mode 100644 index 000000000000..d2b2524b48eb --- /dev/null +++ b/managed_vms/extending_runtime/Dockerfile @@ -0,0 +1,20 @@ +# [START dockerfile] +FROM gcr.io/google_appengine/python + +# Install the fortunes binary from the debian repositories. +RUN apt-get update && apt-get install -y fortunes + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app +# [END dockerfile] diff --git a/managed_vms/extending_runtime/app.yaml b/managed_vms/extending_runtime/app.yaml new file mode 100644 index 000000000000..e8b73970590e --- /dev/null +++ b/managed_vms/extending_runtime/app.yaml @@ -0,0 +1,2 @@ +runtime: custom +vm: true diff --git a/managed_vms/extending_runtime/main.py b/managed_vms/extending_runtime/main.py new file mode 100644 index 000000000000..1c75a7bbff20 --- /dev/null +++ b/managed_vms/extending_runtime/main.py @@ -0,0 +1,36 @@ +# 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. + +# [START app] +import subprocess + +from flask import Flask + + +app = Flask(__name__) + + +# [START example] +@app.route('/') +def fortune(): + output = subprocess.check_output('/usr/games/fortune') + return output, 200, {'Content-Type': 'text/plain; charset=utf-8'} +# [END example] + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) +# [END app] diff --git a/managed_vms/extending_runtime/requirements.txt b/managed_vms/extending_runtime/requirements.txt new file mode 100644 index 000000000000..470ef196fd98 --- /dev/null +++ b/managed_vms/extending_runtime/requirements.txt @@ -0,0 +1,2 @@ +Flask==0.10.1 +gunicorn==19.4.5 diff --git a/managed_vms/hello_world/.dockerignore b/managed_vms/hello_world/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/hello_world/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/hello_world/Dockerfile b/managed_vms/hello_world/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/hello_world/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/hello_world/app.yaml b/managed_vms/hello_world/app.yaml index 70f6bcf43ee1..e8b73970590e 100644 --- a/managed_vms/hello_world/app.yaml +++ b/managed_vms/hello_world/app.yaml @@ -1,9 +1,2 @@ -version: 1 -runtime: python27 +runtime: custom vm: true -api_version: 1 -threadsafe: true - -handlers: -- url: .* # This regex directs all routes to main.app - script: main.app diff --git a/managed_vms/hello_world/main.py b/managed_vms/hello_world/main.py index 28e73368add0..49963e00ffe9 100644 --- a/managed_vms/hello_world/main.py +++ b/managed_vms/hello_world/main.py @@ -23,4 +23,10 @@ def hello(): """Return a friendly HTTP greeting.""" return 'Hello World!' + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) # [END app] diff --git a/managed_vms/hello_world/requirements.txt b/managed_vms/hello_world/requirements.txt index 632a1efabce7..470ef196fd98 100644 --- a/managed_vms/hello_world/requirements.txt +++ b/managed_vms/hello_world/requirements.txt @@ -1 +1,2 @@ Flask==0.10.1 +gunicorn==19.4.5 diff --git a/managed_vms/hello_world/.gitignore b/managed_vms/hello_world_compat/.gitignore similarity index 100% rename from managed_vms/hello_world/.gitignore rename to managed_vms/hello_world_compat/.gitignore diff --git a/managed_vms/hello_world/README.md b/managed_vms/hello_world_compat/README.md similarity index 100% rename from managed_vms/hello_world/README.md rename to managed_vms/hello_world_compat/README.md diff --git a/managed_vms/hello_world_compat/app.yaml b/managed_vms/hello_world_compat/app.yaml new file mode 100644 index 000000000000..70f6bcf43ee1 --- /dev/null +++ b/managed_vms/hello_world_compat/app.yaml @@ -0,0 +1,9 @@ +version: 1 +runtime: python27 +vm: true +api_version: 1 +threadsafe: true + +handlers: +- url: .* # This regex directs all routes to main.app + script: main.app diff --git a/managed_vms/hello_world/appengine_config.py b/managed_vms/hello_world_compat/appengine_config.py similarity index 100% rename from managed_vms/hello_world/appengine_config.py rename to managed_vms/hello_world_compat/appengine_config.py diff --git a/managed_vms/hello_world_compat/main.py b/managed_vms/hello_world_compat/main.py new file mode 100644 index 000000000000..28e73368add0 --- /dev/null +++ b/managed_vms/hello_world_compat/main.py @@ -0,0 +1,26 @@ +# 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. + +# [START app] +from flask import Flask + + +app = Flask(__name__) + + +@app.route('/') +def hello(): + """Return a friendly HTTP greeting.""" + return 'Hello World!' +# [END app] diff --git a/managed_vms/hello_world_compat/requirements.txt b/managed_vms/hello_world_compat/requirements.txt new file mode 100644 index 000000000000..632a1efabce7 --- /dev/null +++ b/managed_vms/hello_world_compat/requirements.txt @@ -0,0 +1 @@ +Flask==0.10.1 diff --git a/managed_vms/hello_world_custom/Dockerfile b/managed_vms/hello_world_custom/Dockerfile deleted file mode 100644 index 3928b108f81c..000000000000 --- a/managed_vms/hello_world_custom/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM google/python-runtime - -ENTRYPOINT /env/bin/gunicorn -b 0.0.0.0:8080 main:app diff --git a/managed_vms/hello_world_custom/README.md b/managed_vms/hello_world_custom/README.md deleted file mode 100644 index aa649c556ea0..000000000000 --- a/managed_vms/hello_world_custom/README.md +++ /dev/null @@ -1,20 +0,0 @@ -## Google App Engine Managed VMs Python Custom Runtime Hello World - -This sample demonstrates using [Python on Google App Engine Managed VMs](https://cloud.google.com/appengine/docs/python/managed-vms/hello-world) with a custom runtime. - -This sample does not use the standard App Engine python runtime, but instead uses -a custom runtime. The custom runtime ensures that any requirements defined -in `requirements.txt` are automatically installed. - -### Running & deploying the sample - -To run the sample locally, use a virtualenv: - - $ virtualenv env - $ source env/bin/activate.sh - $ pip install -r requirements.txt - $ python main.py - -To deploy the sample, use the [Google Cloud SDK](https://cloud.google.com/sdk) - - $ gcloud preview app deploy app.yaml diff --git a/managed_vms/hello_world_django/.dockerignore b/managed_vms/hello_world_django/.dockerignore new file mode 100644 index 000000000000..accaf564035f --- /dev/null +++ b/managed_vms/hello_world_django/.dockerignore @@ -0,0 +1,9 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ +db.sqlite3 diff --git a/managed_vms/hello_world_django/.gitignore b/managed_vms/hello_world_django/.gitignore new file mode 100644 index 000000000000..49ef2557b16e --- /dev/null +++ b/managed_vms/hello_world_django/.gitignore @@ -0,0 +1 @@ +db.sqlite3 diff --git a/managed_vms/hello_world_django/Dockerfile b/managed_vms/hello_world_django/Dockerfile new file mode 100644 index 000000000000..df150068f4ed --- /dev/null +++ b/managed_vms/hello_world_django/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT project_name.wsgi diff --git a/managed_vms/hello_world_django/README.md b/managed_vms/hello_world_django/README.md new file mode 100644 index 000000000000..0249a0b18b7e --- /dev/null +++ b/managed_vms/hello_world_django/README.md @@ -0,0 +1,58 @@ +# Django sample for Google App Engine Managed VMs + +This is a basic hello world [Django](https://www.djangoproject.com/) example +for [Google App Engine Managed VMs](https://cloud.google.com/appengine). + +## Running locally + +You can run locally using django's `manage.py`: + + $ python manage.py runserver + +## Deployment & how the application runs on Google App Engine. + +Follow the standard deployment instructions in +[the top-level README](../README.md). Google App Engine runs the application +using [gunicorn](http://gunicorn.org/) as defined by `CMD` in +[`Dockerfile`](Dockerfile). You can use a different WSGI container if you want, as +long as it listens for web traffic on port `$PORT` and is declared in +[`requirements.txt`](requirements.txt). + +## How this was created + +This project was created using standard Django commands: + + $ virtualenv env + $ source env/bin/activate + $ pip install django gunicorn + $ pip freeze > requirements.txt + $ django-admin startproject project_name + $ python manage.py startapp helloworld + +Then, we added a simple view in `hellworld.views`, added the app to +`project_name.settings.INSTALLED_APPS`, and finally added a URL rule to +`project_name.urls`. + +In order to deploy to Google App Engine, we created a simple +[`app.yaml`](app.yaml) and [`Dockerfile`](Dockerfile). + +## Database notice + +This sample project uses Django's default sqlite database. This isn't suitable +for production as your application can run multiple instances and each will +have a different sqlite database. Additionally, instance disks are emphemmeral, +so data will not survive restarts. + +For production applications running on Google Cloud Platform, you have +the following options: + +* Use [Cloud SQL](https://cloud.google.com/sql), a fully-managed MySQL database. + There is a [Flask CloudSQL](../clousql) sample that should be straightfoward + to adapt to Django. +* Use any database of your choice hosted on + [Google Compute Engine](https://cloud.google.com/compute). The + [Cloud Launcher](https://cloud.google.com/launcher/) can be used to easily + deploy common databases. +* Use third-party database services, or services hosted by other providers, + provided you have configured access. + diff --git a/managed_vms/hello_world_django/app.yaml b/managed_vms/hello_world_django/app.yaml new file mode 100644 index 000000000000..e8b73970590e --- /dev/null +++ b/managed_vms/hello_world_django/app.yaml @@ -0,0 +1,2 @@ +runtime: custom +vm: true diff --git a/managed_vms/hello_world_django/helloworld/__init__.py b/managed_vms/hello_world_django/helloworld/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/managed_vms/hello_world_django/helloworld/views.py b/managed_vms/hello_world_django/helloworld/views.py new file mode 100644 index 000000000000..e6b9069a796d --- /dev/null +++ b/managed_vms/hello_world_django/helloworld/views.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# Copyright 2015 Google Inc. +# +# 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 django.http import HttpResponse + + +def index(request): + return HttpResponse( + 'Hello, World. This is Django running on Google App Engine') diff --git a/managed_vms/hello_world_django/manage.py b/managed_vms/hello_world_django/manage.py new file mode 100755 index 000000000000..0eacf42df3e5 --- /dev/null +++ b/managed_vms/hello_world_django/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_name.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/managed_vms/hello_world_django/project_name/__init__.py b/managed_vms/hello_world_django/project_name/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/managed_vms/hello_world_django/project_name/settings.py b/managed_vms/hello_world_django/project_name/settings.py new file mode 100644 index 000000000000..e96651fa8f7e --- /dev/null +++ b/managed_vms/hello_world_django/project_name/settings.py @@ -0,0 +1,117 @@ +# Copyright 2015 Google Inc. +# +# 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. + +""" +Django settings for project_name project. + +Generated by 'django-admin startproject' using Django 1.8.4. + +For more information on this file, see +https://docs.djangoproject.com/en/1.8/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.8/ref/settings/ +""" + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'qgw!j*bpxo7g&o1ux-(2ph818ojfj(3c#-#*_8r^8&hq5jg$3@' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'helloworld' +) + +MIDDLEWARE_CLASSES = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.security.SecurityMiddleware', +) + +ROOT_URLCONF = 'project_name.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'project_name.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.8/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Internationalization +# https://docs.djangoproject.com/en/1.8/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.8/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/managed_vms/hello_world_django/project_name/urls.py b/managed_vms/hello_world_django/project_name/urls.py new file mode 100644 index 000000000000..883aa60e9394 --- /dev/null +++ b/managed_vms/hello_world_django/project_name/urls.py @@ -0,0 +1,39 @@ +# Copyright 2015 Google Inc. +# +# 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_name URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.8/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Add an import: from blog import urls as blog_urls + 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) +""" +from django.conf.urls import include, url +from django.contrib import admin + +import helloworld.views + + +urlpatterns = [ + url(r'^admin/', include(admin.site.urls)), + url(r'^$', helloworld.views.index), +] diff --git a/managed_vms/hello_world_django/project_name/wsgi.py b/managed_vms/hello_world_django/project_name/wsgi.py new file mode 100644 index 000000000000..242a7ebfb1e2 --- /dev/null +++ b/managed_vms/hello_world_django/project_name/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for project_name project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_name.settings") + +application = get_wsgi_application() diff --git a/managed_vms/hello_world_django/requirements.txt b/managed_vms/hello_world_django/requirements.txt new file mode 100644 index 000000000000..f2cade8894e9 --- /dev/null +++ b/managed_vms/hello_world_django/requirements.txt @@ -0,0 +1,2 @@ +Django==1.9.1 +gunicorn==19.4.5 diff --git a/managed_vms/mailgun/.dockerignore b/managed_vms/mailgun/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/mailgun/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/mailgun/Dockerfile b/managed_vms/mailgun/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/mailgun/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/mailgun/README.md b/managed_vms/mailgun/README.md new file mode 100644 index 000000000000..e1f7704aedc6 --- /dev/null +++ b/managed_vms/mailgun/README.md @@ -0,0 +1,24 @@ +# Python Mailgun email sample for Google App Engine Managed VMs + +This sample demonstrates how to use [Mailgun](https://www.mailgun.com) on [Google App Engine Managed VMs](https://cloud.google.com/appengine). + +For more information about Mail, see their [documentation](https://documentation.mailgun.com/). + +## Setup + +Before you can run or deploy the sample, you will need to do the following: + +1. [Create a Mailgun Account](http://www.mailgun.com/google). As of September 2015, Google users start with 30,000 free emails per month. + +2. Configure your Mailgun settings in the environment variables section in ``app.yaml``. + +## Running locally + +Refer to the [top-level README](../README.md) for instructions on running and deploying. + +You can run the application locally and send emails from your local machine. You +will need to set environment variables before starting your application: + + $ export MAILGUN_API_KEY=[your-mailgun-api-key] + $ export MAILGUN_DOMAIN_NAME=[your-mailgun-domain-name] + $ python main.py diff --git a/managed_vms/mailgun/app.yaml b/managed_vms/mailgun/app.yaml new file mode 100644 index 000000000000..f2616d79394c --- /dev/null +++ b/managed_vms/mailgun/app.yaml @@ -0,0 +1,8 @@ +runtime: custom +vm: true + +# [START env_variables] +env_variables: + MAILGUN_DOMAIN_NAME: your-mailgun-domain-name + MAILGUN_API_KEY: your-mailgun-api-key +# [END env_variables] diff --git a/managed_vms/mailgun/example-attachment.txt b/managed_vms/mailgun/example-attachment.txt new file mode 100644 index 000000000000..96feda20c1c0 --- /dev/null +++ b/managed_vms/mailgun/example-attachment.txt @@ -0,0 +1 @@ +This is a sample attachment. diff --git a/managed_vms/mailgun/main.py b/managed_vms/mailgun/main.py new file mode 100644 index 000000000000..c2abde1df698 --- /dev/null +++ b/managed_vms/mailgun/main.py @@ -0,0 +1,85 @@ +# 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. + +# [START app] +import os + +from flask import Flask, render_template, request +import requests + +# [START config] +MAILGUN_DOMAIN_NAME = os.environ['MAILGUN_DOMAIN_NAME'] +MAILGUN_API_KEY = os.environ['MAILGUN_API_KEY'] +# [END config] + +app = Flask(__name__) + + +# [START simple_message] +def send_simple_message(to): + url = 'https://api.mailgun.net/v3/{}/messages'.format(MAILGUN_DOMAIN_NAME) + auth = ('api', MAILGUN_API_KEY) + data = { + 'from': 'Mailgun User '.format(MAILGUN_DOMAIN_NAME), + 'to': to, + 'subject': 'Simple Mailgun Example', + 'text': 'Plaintext content', + } + + response = requests.post(url, auth=auth, data=data) + response.raise_for_status() +# [END simple_message] + + +# [START complex_message] +def send_complex_message(to): + url = 'https://api.mailgun.net/v3/{}/messages'.format(MAILGUN_DOMAIN_NAME) + auth = ('api', MAILGUN_API_KEY) + data = { + 'from': 'Mailgun User '.format(MAILGUN_DOMAIN_NAME), + 'to': to, + 'subject': 'Complex Mailgun Example', + 'text': 'Plaintext content', + 'html': 'HTML content' + } + files = [("attachment", open("example-attachment.txt"))] + + response = requests.post(url, auth=auth, data=data, files=files) + response.raise_for_status() +# [END complex_message] + + +@app.route('/') +def index(): + return render_template('index.html') + + +@app.route('/send/email', methods=['POST']) +def send_email(): + action = request.form.get('submit') + to = request.form.get('to') + + if action == 'Send simple email': + send_simple_message(to) + else: + send_complex_message(to) + + return 'Email sent.' + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) +# [END app] diff --git a/managed_vms/mailgun/requirements.txt b/managed_vms/mailgun/requirements.txt new file mode 100644 index 000000000000..1aa21f0eb352 --- /dev/null +++ b/managed_vms/mailgun/requirements.txt @@ -0,0 +1,3 @@ +Flask==0.10.1 +gunicorn==19.4.5 +requests[security]==2.9.1 diff --git a/managed_vms/mailgun/templates/index.html b/managed_vms/mailgun/templates/index.html new file mode 100644 index 000000000000..9966bb6fd61a --- /dev/null +++ b/managed_vms/mailgun/templates/index.html @@ -0,0 +1,30 @@ +{# +# 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. +#} + + + + Mailgun on Google App Engine Managed VMs + + + +
+ + + +
+ + + diff --git a/managed_vms/memcache/.dockerignore b/managed_vms/memcache/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/memcache/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/memcache/Dockerfile b/managed_vms/memcache/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/memcache/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/memcache/app.yaml b/managed_vms/memcache/app.yaml new file mode 100644 index 000000000000..b820c326251e --- /dev/null +++ b/managed_vms/memcache/app.yaml @@ -0,0 +1,7 @@ +runtime: custom +vm: true + +# [START config] +beta_settings: + use_memcache_proxy: true +# [END config] diff --git a/managed_vms/memcache/main.py b/managed_vms/memcache/main.py new file mode 100644 index 000000000000..bd2f8f034f28 --- /dev/null +++ b/managed_vms/memcache/main.py @@ -0,0 +1,49 @@ +# 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. + +import os + +from flask import Flask +import memcache + + +app = Flask(__name__) + + +# [START client] +memcache_addr = os.environ.get('MEMCACHE_PORT_11211_TCP_ADDR', 'localhost') +memcache_port = os.environ.get('MEMCACHE_PORT_11211_TCP_PORT', '11211') +memcache_client = memcache.Client([ + '{}:{}'.format(memcache_addr, memcache_port)]) +# [END client] + + +# [START example] +@app.route('/') +def index(): + + # Set initial value if necessary + if not memcache_client.get('counter'): + memcache_client.set('counter', 0) + + value = memcache_client.incr('counter') + + return 'Value is {}'.format(value) +# [END example] + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) diff --git a/managed_vms/memcache/requirements.txt b/managed_vms/memcache/requirements.txt new file mode 100644 index 000000000000..924f9437661d --- /dev/null +++ b/managed_vms/memcache/requirements.txt @@ -0,0 +1,3 @@ +Flask==0.10.1 +gunicorn==19.4.5 +python-memcached==1.57 diff --git a/managed_vms/pubsub/.dockerignore b/managed_vms/pubsub/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/pubsub/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/pubsub/Dockerfile b/managed_vms/pubsub/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/pubsub/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/pubsub/README.md b/managed_vms/pubsub/README.md new file mode 100644 index 000000000000..c69e89af6c67 --- /dev/null +++ b/managed_vms/pubsub/README.md @@ -0,0 +1,72 @@ +# Python Google Cloud Pub/Sub sample for Google App Engine Managed VMs + +This demonstrates how to send and receive messages using [Google Cloud Pub/Sub](https://cloud.google.com/pubsub) on [Google App Engine Managed VMs](https://cloud.google.com/appengine). + +## Setup + +Before you can run or deploy the sample, you will need to do the following: + +1. Enable the Cloud Pub/Sub API in the [Google Developers Console](https://console.developers.google.com/project/_/apiui/apiview/pubsub/overview). + +2. Create a topic and subscription. + + $ gcloud alpha pubsub topics create [your-topic-name] + $ gcloud alpha pubsub subscriptions create [your-subscription-name] \ + --topic [your-topic-name] \ + --push-endpoint \ + https://[your-app-id].appspot.com/pubsub/push?token=[your-token] \ + --ack-deadline 30 + +3. Update the environment variables in ``app.yaml``. + +## Running locally + +Refer to the [top-level README](../README.md) for instructions on running and deploying. + +When running locally, you can use the [Google Cloud SDK](https://cloud.google.com/sdk) to provide authentication to use Google Cloud APIs: + + $ gcloud init + +Install dependencies, preferably with a virtualenv: + + $ virtualenv env + $ source env/bin/activate + $ pip install -r requirements.txt + +Then set environment variables before starting your application: + + $ export GCLOUD_PROJECT=[your-project-id] + $ export PUBSUB_VERIFICATION_TOKEN=[your-verification-token] + $ export PUBSUB_TOPIC=[your-topic] + $ python main.py + +### Simulating push notifications + +The application can send messages locally, but it is not able to receive push messages locally. You can, however, simulate a push message by making an HTTP request to the local push notification endpoint. There is an included ``sample_message.json``. You can use +``curl`` or [httpie](https://github.com/jkbrzt/httpie) to POST this: + + $ curl -i --data @sample_message.json ":8080/pubsub/push?token=[your-token]" + +Or + + $ http POST ":8080/pubsub/push?token=[your-token]" < sample_message.json + +Response: + + HTTP/1.0 200 OK + Content-Length: 2 + Content-Type: text/html; charset=utf-8 + Date: Mon, 10 Aug 2015 17:52:03 GMT + Server: Werkzeug/0.10.4 Python/2.7.10 + + OK + +After the request completes, you can refresh ``localhost:8080`` and see the message in the list of received messages. + +## Running on App Engine + +Deploy using `gcloud`: + + gcloud app deploy app.yaml + +You can now access the application at `https://your-app-id.appspot.com`. You can use the form to submit messages, but it's non-deterministic which instance of your application will receive the notification. You can send multiple messages and refresh the page to see the received message. diff --git a/managed_vms/pubsub/app.yaml b/managed_vms/pubsub/app.yaml new file mode 100644 index 000000000000..af5971a01b50 --- /dev/null +++ b/managed_vms/pubsub/app.yaml @@ -0,0 +1,11 @@ +runtime: custom +vm: true + +#[START env] +env_variables: + GCLOUD_PROJECT: your-project-id + PUBSUB_TOPIC: your-topic + # This token is used to verify that requests originate from your + # application. It can be any sufficiently random string. + PUBSUB_VERIFICATION_TOKEN: 1234abc +#[END env] diff --git a/managed_vms/pubsub/main.py b/managed_vms/pubsub/main.py new file mode 100644 index 000000000000..a301a4b984a2 --- /dev/null +++ b/managed_vms/pubsub/main.py @@ -0,0 +1,74 @@ +# 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. +# [START app] +import base64 +import json +import os + +from flask import current_app, Flask, render_template, request +from gcloud import pubsub + + +app = Flask(__name__) + +# Configure the following environment variables via app.yaml +# This is used in the push request handler to veirfy that the request came from +# pubsub and originated from a trusted source. +app.config['PUBSUB_VERIFICATION_TOKEN'] = \ + os.environ['PUBSUB_VERIFICATION_TOKEN'] +app.config['PUBSUB_TOPIC'] = os.environ['PUBSUB_TOPIC'] + + +# Global list to storage messages received by this instance. +MESSAGES = [] + + +# [START index] +@app.route('/', methods=['GET', 'POST']) +def index(): + if request.method == 'GET': + return render_template('index.html', messages=MESSAGES) + + ps = pubsub.Client() + topic = ps.topic(current_app.config['PUBSUB_TOPIC']) + + topic.publish( + request.form.get('payload', 'Example payload').encode('utf-8')) + + return 'OK', 200 +# [END index] + + +# [START push] +@app.route('/pubsub/push', methods=['POST']) +def pubsub_push(): + if (request.args.get('token', '') != + current_app.config['PUBSUB_VERIFICATION_TOKEN']): + return 'Invalid request', 400 + + envelope = json.loads(request.data.decode('utf-8')) + payload = base64.b64decode(envelope['message']['data']) + + MESSAGES.append(payload) + + # Returning any 2xx status indicates successful receipt of the message. + return 'OK', 200 +# [END push] + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) +# [END app] diff --git a/managed_vms/pubsub/requirements.txt b/managed_vms/pubsub/requirements.txt new file mode 100644 index 000000000000..ee14266131e9 --- /dev/null +++ b/managed_vms/pubsub/requirements.txt @@ -0,0 +1,4 @@ +Flask==0.10.1 +gcloud==0.8.0 +gunicorn==19.4.5 +oauth2client==1.5.2 diff --git a/managed_vms/pubsub/sample_message.json b/managed_vms/pubsub/sample_message.json new file mode 100644 index 000000000000..8fe62d23fb92 --- /dev/null +++ b/managed_vms/pubsub/sample_message.json @@ -0,0 +1,5 @@ +{ + "message": { + "data": "SGVsbG8sIFdvcmxkIQ==" + } +} diff --git a/managed_vms/pubsub/templates/index.html b/managed_vms/pubsub/templates/index.html new file mode 100644 index 000000000000..2d4ff27fe213 --- /dev/null +++ b/managed_vms/pubsub/templates/index.html @@ -0,0 +1,38 @@ +{# +# 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. +#} + + + + Pub/Sub Python on Google App Engine Managed VMs + + +
+

Messages received by this instance:

+
    + {% for message in messages: %} +
  • {{message}}
  • + {% endfor %} +
+

Note: because your application is likely running multiple instances, each instance will have a different list of messages.

+
+ +
+ + +
+ + + diff --git a/managed_vms/sendgrid/.dockerignore b/managed_vms/sendgrid/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/sendgrid/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/sendgrid/Dockerfile b/managed_vms/sendgrid/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/sendgrid/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/sendgrid/README.md b/managed_vms/sendgrid/README.md new file mode 100644 index 000000000000..db6dd1cb91ea --- /dev/null +++ b/managed_vms/sendgrid/README.md @@ -0,0 +1,24 @@ +# Python SendGrid email sample for Google App Engine Managed VMs + +This sample demonstrates how to use [SendGrid](https://www.sendgrid.com) on [Google App Engine Managed VMs](https://cloud.google.com/appengine). + +For more information about SendGrid, see their [documentation](https://sendgrid.com/docs/User_Guide/index.html). + +## Setup + +Before you can run or deploy the sample, you will need to do the following: + +1. [Create a SendGrid Account](http://sendgrid.com/partner/google). As of September 2015, Google users start with 25,000 free emails per month. + +2. Configure your SendGrid settings in the environment variables section in ``app.yaml``. + +## Running locally + +Refer to the [top-level README](../README.md) for instructions on running and deploying. + +You can run the application locally and send emails from your local machine. You +will need to set environment variables before starting your application: + + $ export SENDGRID_API_KEY=[your-sendgrid-api-key] + $ export SENDGRID_SENDER=[your-sendgrid-sender-email-address] + $ python main.py diff --git a/managed_vms/sendgrid/app.yaml b/managed_vms/sendgrid/app.yaml new file mode 100644 index 000000000000..6703c64228a3 --- /dev/null +++ b/managed_vms/sendgrid/app.yaml @@ -0,0 +1,8 @@ +runtime: custom +vm: true + +# [START env_variables] +env_variables: + SENDGRID_API_KEY: your-sendgrid-api-key + SENDGRID_SENDER: your-sendgrid-sender +# [END env_variables] diff --git a/managed_vms/sendgrid/main.py b/managed_vms/sendgrid/main.py new file mode 100644 index 000000000000..02350be53473 --- /dev/null +++ b/managed_vms/sendgrid/main.py @@ -0,0 +1,64 @@ +# 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. + +# [START app] +import os + +from flask import Flask, render_template, request +import sendgrid + +# [START config] +SENDGRID_API_KEY = os.environ['SENDGRID_API_KEY'] +SENDGRID_SENDER = os.environ['SENDGRID_SENDER'] +# [END config] + +app = Flask(__name__) + + +@app.route('/') +def index(): + return render_template('index.html') + + +# [START example] +@app.route('/send/email', methods=['POST']) +def send_email(): + to = request.form.get('to') + if not to: + return ('Please provide an email address in the "to" query string ' + 'parameter.') + + sg = sendgrid.SendGridClient(SENDGRID_API_KEY) + + message = sendgrid.Mail( + to=to, + subject='This is a test email', + html='

Example HTML body.

', + text='Example text body.', + from_email=SENDGRID_SENDER) + + status, response = sg.send(message) + + if status != 200: + return 'An error occurred: {}'.format(response) + + return 'Email sent.' +# [END example] + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) +# [END app] diff --git a/managed_vms/sendgrid/requirements.txt b/managed_vms/sendgrid/requirements.txt new file mode 100644 index 000000000000..5b9ed6c5f6b5 --- /dev/null +++ b/managed_vms/sendgrid/requirements.txt @@ -0,0 +1,3 @@ +Flask==0.10.1 +sendgrid==1.5.20 +gunicorn==19.4.5 diff --git a/managed_vms/sendgrid/templates/index.html b/managed_vms/sendgrid/templates/index.html new file mode 100644 index 000000000000..b53ff6cdb9ce --- /dev/null +++ b/managed_vms/sendgrid/templates/index.html @@ -0,0 +1,29 @@ +{# +# 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. +#} + + + + SendGrid on Google App Engine Managed VMs + + + +
+ + +
+ + + diff --git a/managed_vms/static_files/.dockerignore b/managed_vms/static_files/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/static_files/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/static_files/Dockerfile b/managed_vms/static_files/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/static_files/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/static_files/README.md b/managed_vms/static_files/README.md new file mode 100644 index 000000000000..6db9e84a74d4 --- /dev/null +++ b/managed_vms/static_files/README.md @@ -0,0 +1,7 @@ +# Python / Flask static files sample for Google App Engine Managed VMs + +This demonstrates how to use [Flask](http://flask.pocoo.org/) to serve static files in your application. + +Flask automatically makes anything in the ``static`` directory available via the ``/static`` URL. If you plan on using a different framework, it may have different conventions for serving static files. + +Refer to the [top-level README](../README.md) for instructions on running and deploying. diff --git a/managed_vms/static_files/app.yaml b/managed_vms/static_files/app.yaml new file mode 100644 index 000000000000..e8b73970590e --- /dev/null +++ b/managed_vms/static_files/app.yaml @@ -0,0 +1,2 @@ +runtime: custom +vm: true diff --git a/managed_vms/hello_world_custom/main.py b/managed_vms/static_files/main.py similarity index 83% rename from managed_vms/hello_world_custom/main.py rename to managed_vms/static_files/main.py index bd4ea38ff5d3..f99e2c956d89 100644 --- a/managed_vms/hello_world_custom/main.py +++ b/managed_vms/static_files/main.py @@ -13,7 +13,7 @@ # limitations under the License. # [START app] -from flask import Flask +from flask import Flask, render_template app = Flask(__name__) @@ -21,12 +21,11 @@ @app.route('/') def hello(): - """Return a friendly HTTP greeting.""" - return 'Hello World!' + return render_template('index.html') if __name__ == '__main__': # This is used when running locally. Gunicorn is used to run the - # application on Google App Engine. See ENTRYPOINT in the Dockerfile. + # application on Google App Engine. See CMD in Dockerfile. app.run(host='127.0.0.1', port=8080, debug=True) # [END app] diff --git a/managed_vms/static_files/requirements.txt b/managed_vms/static_files/requirements.txt new file mode 100644 index 000000000000..470ef196fd98 --- /dev/null +++ b/managed_vms/static_files/requirements.txt @@ -0,0 +1,2 @@ +Flask==0.10.1 +gunicorn==19.4.5 diff --git a/managed_vms/static_files/static/main.css b/managed_vms/static_files/static/main.css new file mode 100644 index 000000000000..fe0aca088c41 --- /dev/null +++ b/managed_vms/static_files/static/main.css @@ -0,0 +1,21 @@ +/* +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. +*/ +/* [START example] */ +body { + font-family: Verdana, Helvetica, sans-serif; + background-color: #CCCCFF; +} +/* [END example] */ diff --git a/managed_vms/static_files/templates/index.html b/managed_vms/static_files/templates/index.html new file mode 100644 index 000000000000..352d00996f83 --- /dev/null +++ b/managed_vms/static_files/templates/index.html @@ -0,0 +1,29 @@ +{# +# 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. +#} + + + + Static Files + + + + +

This is a static file serving example.

+ + diff --git a/managed_vms/storage/.dockerignore b/managed_vms/storage/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/storage/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/storage/Dockerfile b/managed_vms/storage/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/storage/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/storage/README.md b/managed_vms/storage/README.md new file mode 100644 index 000000000000..e182c5f40d32 --- /dev/null +++ b/managed_vms/storage/README.md @@ -0,0 +1,33 @@ +# Python Google Cloud Storage sample for Google App Engine Managed VMs + +This sample demonstrates how to use [Google Cloud Storage](https://cloud.google.com/storage/) on [Google App Engine Managed VMs](https://cloud.google.com/appengine). + +## Setup + +Before you can run or deploy the sample, you will need to do the following: + +1. Enable the Cloud Storage API in the [Google Developers Console](https://console.developers.google.com/project/_/apiui/apiview/storage/overview). + +2. Create a Cloud Storage Bucket. You can do this with the [Google Cloud SDK](https://cloud.google.com/sdk) with the following command: + + $ gsutil mb gs://[your-bucket-name] + +3. Set the default ACL on your bucket to public read in order to serve files directly from Cloud Storage. You can do this with the [Google Cloud SDK](https://cloud.google.com/sdk) with the following command: + + $ gsutil defacl set public-read gs://[your-bucket-name] + +4. Update the environment variables in ``app.yaml``. + +## Running locally + +Refer to the [top-level README](../README.md) for instructions on running and deploying. + +When running locally, you can use the [Google Cloud SDK](https://cloud.google.com/sdk) to provide authentication to use Google Cloud APIs: + + $ gcloud init + +Then set environment variables before starting your application: + + $ export GCLOUD_PROJECT=[your-project-id] + $ export GCLOUD_STORAGE_BUCKET=[your-bucket-name] + $ python main.py diff --git a/managed_vms/storage/app.yaml b/managed_vms/storage/app.yaml new file mode 100644 index 000000000000..efb0c008246d --- /dev/null +++ b/managed_vms/storage/app.yaml @@ -0,0 +1,8 @@ +runtime: custom +vm: true + +#[START env] +env_variables: + GCLOUD_PROJECT: your-project-id + GCLOUD_STORAGE_BUCKET: your-bucket-name +#[END env] diff --git a/managed_vms/storage/main.py b/managed_vms/storage/main.py new file mode 100644 index 000000000000..74120ec4fc0f --- /dev/null +++ b/managed_vms/storage/main.py @@ -0,0 +1,75 @@ +# 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. + +# [START app] +import os + +from flask import current_app, Flask, request +from gcloud import storage + +# [start config] +app = Flask(__name__) + + +# Configure this environment variable via app.yaml +app.config['GCLOUD_STORAGE_BUCKET'] = os.environ['GCLOUD_STORAGE_BUCKET'] +# [end config] + + +# [START form] +@app.route('/') +def index(): + """Present the user with an upload form.""" + return """ +
+ + +
+""" +# [END form] + + +# [START upload] +@app.route('/upload', methods=['POST']) +def upload(): + """Process the uploaded file and upload it to Google Cloud Storage.""" + uploaded_file = request.files.get('file') + + if not uploaded_file: + return 'No file uploaded.', 400 + + # Create a Cloud Storage client. + gcs = storage.Client() + + # Get the bucket that the file will be uploaded to. + bucket = gcs.get_bucket(current_app.config['GCLOUD_STORAGE_BUCKET']) + + # Create a new blob and upload the file's content. + blob = bucket.blob(uploaded_file.filename) + + blob.upload_from_string( + uploaded_file.read(), + content_type=uploaded_file.content_type + ) + + # The public URL can be used to directly access the uploaded file via HTTP. + return blob.public_url +# [END upload] + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) +# [END app] diff --git a/managed_vms/storage/requirements.txt b/managed_vms/storage/requirements.txt new file mode 100644 index 000000000000..f41b909191e8 --- /dev/null +++ b/managed_vms/storage/requirements.txt @@ -0,0 +1,3 @@ +Flask==0.10.1 +gcloud==0.8.0 +gunicorn==19.4.5 diff --git a/managed_vms/tests/__init__.py b/managed_vms/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/managed_vms/tests/cloudsql_test.py b/managed_vms/tests/cloudsql_test.py new file mode 100644 index 000000000000..3714f84a3f5f --- /dev/null +++ b/managed_vms/tests/cloudsql_test.py @@ -0,0 +1,29 @@ +# 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. + +import os + +import requests + +from .runserver import RunServerTestCase + + +class CloudSqlTest(RunServerTestCase): + application_path = os.path.join( + os.path.dirname(__file__), '..', 'cloudsql') + + def test_index(self): + r = requests.get(self.server_url) + self.assertEqual(r.status_code, 200) + self.assertTrue('127.0.0.1' in r.text) diff --git a/managed_vms/tests/datastore_test.py b/managed_vms/tests/datastore_test.py new file mode 100644 index 000000000000..f148ffa386ba --- /dev/null +++ b/managed_vms/tests/datastore_test.py @@ -0,0 +1,29 @@ +# 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. + +import os + +import requests + +from .runserver import RunServerTestCase + + +class DatastoreTest(RunServerTestCase): + application_path = os.path.join( + os.path.dirname(__file__), '..', 'datastore') + + def test_index(self): + r = requests.get(self.server_url) + self.assertEqual(r.status_code, 200) + self.assertTrue('127.0.0.1' in r.text) diff --git a/managed_vms/tests/disk_test.py b/managed_vms/tests/disk_test.py new file mode 100644 index 000000000000..28552e0a523d --- /dev/null +++ b/managed_vms/tests/disk_test.py @@ -0,0 +1,30 @@ +# 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. + +import os + +import requests + +from .runserver import RunServerTestCase + + +class DiskTest(RunServerTestCase): + application_path = os.path.join( + os.path.dirname(__file__), '..', 'disk') + + def test_index(self): + r = requests.get(self.server_url) + self.assertEqual(r.status_code, 200) + self.assertTrue('127.0.0.1' in r.text) + self.assertTrue(os.path.exists('/tmp/seen.txt')) diff --git a/managed_vms/tests/extending_runtime_test.py b/managed_vms/tests/extending_runtime_test.py new file mode 100644 index 000000000000..fdce4886e012 --- /dev/null +++ b/managed_vms/tests/extending_runtime_test.py @@ -0,0 +1,35 @@ +# 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. + +import os +import platform + +from nose.plugins.skip import SkipTest +import requests + +from .runserver import RunServerTestCase + + +if platform.system() != 'Linux': + raise SkipTest("Extending runtime test will only execute on Linux.") + + +class ExtendingRuntimeTest(RunServerTestCase): + application_path = os.path.join( + os.path.dirname(__file__), '..', 'extending_runtime') + + def test_index(self): + r = requests.get(self.server_url) + self.assertEqual(r.status_code, 200) + self.assertTrue(len(r.text)) diff --git a/managed_vms/tests/hello_world_test.py b/managed_vms/tests/hello_world_test.py new file mode 100644 index 000000000000..ca6f0b22581d --- /dev/null +++ b/managed_vms/tests/hello_world_test.py @@ -0,0 +1,28 @@ +# 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. + +import os + +import requests + +from .runserver import RunServerTestCase + + +class HelloWorldTest(RunServerTestCase): + application_path = os.path.join( + os.path.dirname(__file__), '..', 'hello_world') + + def test_index(self): + r = requests.get(self.server_url) + self.assertEqual(r.status_code, 200) diff --git a/managed_vms/tests/memcache_test.py b/managed_vms/tests/memcache_test.py new file mode 100644 index 000000000000..1c69da13ba4f --- /dev/null +++ b/managed_vms/tests/memcache_test.py @@ -0,0 +1,29 @@ +# 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. + +import os + +import requests + +from .runserver import RunServerTestCase + + +class MemcacheTest(RunServerTestCase): + application_path = os.path.join( + os.path.dirname(__file__), '..', 'memcache') + + def test_index(self): + r = requests.get(self.server_url) + self.assertEqual(r.status_code, 200) + self.assertTrue('Value is' in r.text) diff --git a/managed_vms/tests/pubsub_test.py b/managed_vms/tests/pubsub_test.py new file mode 100644 index 000000000000..bb4c22c8d99e --- /dev/null +++ b/managed_vms/tests/pubsub_test.py @@ -0,0 +1,63 @@ +# 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. + +import base64 +import os + +import requests + +from .runserver import RunServerTestCase + + +class PubSubTest(RunServerTestCase): + application_path = os.path.join( + os.path.dirname(__file__), '..', 'pubsub') + + def test_index(self): + r = requests.get(self.server_url) + self.assertEqual(r.status_code, 200) + + def test_post_index(self): + r = requests.post(self.server_url, data={'payload': 'Test payload'}) + self.assertEqual(r.status_code, 200) + + def test_push_endpoint(self): + url = self.server_url + 'pubsub/push?token=' + \ + os.environ['PUBSUB_VERIFICATION_TOKEN'] + r = requests.post( + url, + json={ + "message": { + "data": base64.b64encode( + u'Test message'.encode('utf-8') + ).decode('utf-8') + } + } + ) + + self.assertEqual(r.status_code, 200) + + # Make sure the message is visible on the home page. + r = requests.get(self.server_url) + self.assertEqual(r.status_code, 200) + self.assertTrue('Test message' in r.text) + + def test_push_endpoint_errors(self): + # no token + r = requests.post(self.server_url + 'pubsub/push') + self.assertEqual(r.status_code, 400) + + # invalid token + r = requests.post(self.server_url + 'pubsub/push?token=bad') + self.assertEqual(r.status_code, 400) diff --git a/managed_vms/tests/runserver.py b/managed_vms/tests/runserver.py new file mode 100644 index 000000000000..09089d2ee4e4 --- /dev/null +++ b/managed_vms/tests/runserver.py @@ -0,0 +1,88 @@ +# 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. + +import logging +import multiprocessing +import os +import sys +import time +import unittest + +import requests +from werkzeug.serving import run_simple + + +logging.getLogger("requests").setLevel(logging.WARNING) + + +def run_app(path, port, entrypoint): + path = os.path.realpath(path) + sys.path.insert(1, path) + os.chdir(path) + + module_name, wsgi_app_name = entrypoint.rsplit('.', 1) + + module = __import__(module_name) + wsgi_app = getattr(module, wsgi_app_name) + + wsgi_app.debug = True + run_simple('localhost', port, wsgi_app, use_debugger=True) + + +def run_app_multiprocessing(path, port=43125, entrypoint='main.app'): + process = multiprocessing.Process( + target=run_app, args=(path, port, entrypoint)) + process.start() + + try: + _wait_for_server(port) + except: + process.terminate() + raise + + return process + + +def _wait_for_server(port): + start = time.time() + while True: + try: + requests.get('http://localhost:{}/_ah/health'.format(port)) + # Break on first successful request, regardless of status, as it + # means the server is accepting connections. + break + except requests.ConnectionError: + if time.time() - start > 5: + raise RuntimeError('Server failed to respond to requests.') + + +class RunServerTestCase(unittest.TestCase): + server_host = 'locahost' + server_port = 43125 + server_url = 'http://localhost:43125/' + + application_path = None + application_entrypoint = 'main.app' + _server_process = None + + @classmethod + def setUpClass(cls): + cls._server_process = run_app_multiprocessing( + cls.application_path, + entrypoint=cls.application_entrypoint) + + @classmethod + def tearDownClass(cls): + if cls._server_process: + cls._server_process.terminate() diff --git a/managed_vms/tests/static_files_test.py b/managed_vms/tests/static_files_test.py new file mode 100644 index 000000000000..090bf8d47e3c --- /dev/null +++ b/managed_vms/tests/static_files_test.py @@ -0,0 +1,31 @@ +# 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. + +import os + +import requests + +from .runserver import RunServerTestCase + + +class StaticFilesTest(RunServerTestCase): + application_path = os.path.join( + os.path.dirname(__file__), '..', 'static_files') + + def test_index(self): + r = requests.get(self.server_url) + self.assertEqual(r.status_code, 200) + + r = requests.get(self.server_url + 'static/main.css') + self.assertEqual(r.status_code, 200) diff --git a/managed_vms/tests/storage_test.py b/managed_vms/tests/storage_test.py new file mode 100644 index 000000000000..fc0cccfd4ee6 --- /dev/null +++ b/managed_vms/tests/storage_test.py @@ -0,0 +1,51 @@ +# 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. + +import os + +import requests + +from .runserver import RunServerTestCase + + +class StaticFilesTest(RunServerTestCase): + application_path = os.path.join( + os.path.dirname(__file__), '..', 'storage') + + def test_index(self): + r = requests.get(self.server_url) + self.assertEqual(r.status_code, 200) + + def test_upload(self): + # Upload a simple file + file_content = "This is some test content." + + r = requests.post( + self.server_url + 'upload', + files={ + 'file': ( + 'example.txt', + file_content, + 'text/plain' + ) + } + ) + + self.assertEqual(r.status_code, 200) + + # The app should return the public cloud storage URL for the uploaded + # file. Download and verify it. + cloud_storage_url = r.text + r = requests.get(cloud_storage_url) + self.assertEqual(r.text, file_content) diff --git a/managed_vms/twilio/.dockerignore b/managed_vms/twilio/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/twilio/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/twilio/Dockerfile b/managed_vms/twilio/Dockerfile new file mode 100644 index 000000000000..64d56e4e8a3a --- /dev/null +++ b/managed_vms/twilio/Dockerfile @@ -0,0 +1,15 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +CMD gunicorn -b :$PORT main:app diff --git a/managed_vms/twilio/README.md b/managed_vms/twilio/README.md new file mode 100644 index 000000000000..9aadc1a23f91 --- /dev/null +++ b/managed_vms/twilio/README.md @@ -0,0 +1,29 @@ +# Python Twilio voice and SMS sample for Google App Engine Managed VMs + +This sample demonstrates how to use [Twilio](https://www.twilio.com) on [Google App Engine Managed VMs](https://cloud.google.com/appengine). + +For more information about Twilio, see their [Python quickstart tutorials](https://www.twilio.com/docs/quickstart/python). + +## Setup + +Before you can run or deploy the sample, you will need to do the following: + +1. [Create a Twilio Account](http://ahoy.twilio.com/googlecloudplatform). Google App Engine +customers receive a complimentary credit for SMS messages and inbound messages. + +2. Create a number on twilio, and configure the voice request URL to be ``https://your-app-id.appspot.com/call/receive`` +and the SMS request URL to be ``https://your-app-id.appspot.com/sms/receive``. + +3. Configure your Twilio settings in the environment variables section in ``app.yaml``. + +## Running locally + +Refer to the [top-level README](../README.md) for instructions on running and deploying. + +You can run the application locally to test the callbacks and SMS sending. You +will need to set environment variables before starting your application: + + $ export TWILIO_ACCOUNT_SID=[your-twilio-accoun-sid] + $ export TWILIO_AUTH_TOKEN=[your-twilio-auth-token] + $ export TWILIO_NUMBER=[your-twilio-number] + $ python main.py diff --git a/managed_vms/twilio/app.yaml b/managed_vms/twilio/app.yaml new file mode 100644 index 000000000000..a2d6d551d6b6 --- /dev/null +++ b/managed_vms/twilio/app.yaml @@ -0,0 +1,9 @@ +runtime: custom +vm: true + +# [START env_variables] +env_variables: + TWILIO_ACCOUNT_SID: your-account-sid + TWILIO_AUTH_TOKEN: your-auth-token + TWILIO_NUMBER: your-twilio-number +# [END env_variables] diff --git a/managed_vms/twilio/main.py b/managed_vms/twilio/main.py new file mode 100644 index 000000000000..57ccc40cc81e --- /dev/null +++ b/managed_vms/twilio/main.py @@ -0,0 +1,80 @@ +# 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. + +# [START app] +import os + +from flask import Flask, request +from twilio import twiml +from twilio.rest import TwilioRestClient + + +# [START configuration] +TWILIO_ACCOUNT_SID = os.environ['TWILIO_ACCOUNT_SID'] +TWILIO_AUTH_TOKEN = os.environ['TWILIO_AUTH_TOKEN'] +TWILIO_NUMBER = os.environ['TWILIO_NUMBER'] +# [END configuration] + + +app = Flask(__name__) + + +# [START receive_call] +@app.route('/call/receive', methods=['POST']) +def receive_call(): + """Answers a call and replies with a simple greeting.""" + response = twiml.Response() + response.say('Hello from Twilio!') + return str(response), 200, {'Content-Type': 'application/xml'} +# [END receive_call] + + +# [START send_sms] +@app.route('/sms/send') +def send_sms(): + """Sends a simple SMS message.""" + to = request.args.get('to') + if not to: + return ('Please provide the number to message in the "to" query string' + ' parameter.') + + client = TwilioRestClient(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN) + rv = client.messages.create( + to=to, + from_=TWILIO_NUMBER, + body='Hello from Twilio!') + return str(rv) +# [END send_sms] + + +# [START receive_sms] +@app.route('/sms/receive', methods=['POST']) +def receive_sms(): + """Receives an SMS message and replies with a simple greeting.""" + sender = request.values.get('From') + body = request.values.get('Body') + + message = 'Hello, {}, you said: {}'.format(sender, body) + + response = twiml.Response() + response.message(message) + return str(response), 200, {'Content-Type': 'application/xml'} +# [END receive_sms] + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See CMD in Dockerfile. + app.run(host='127.0.0.1', port=8080, debug=True) +# [END app] diff --git a/managed_vms/twilio/requirements.txt b/managed_vms/twilio/requirements.txt new file mode 100644 index 000000000000..d6049aceacb7 --- /dev/null +++ b/managed_vms/twilio/requirements.txt @@ -0,0 +1,3 @@ +Flask==0.10.1 +gunicorn==19.4.5 +twilio==6.2.dev0 diff --git a/managed_vms/websockets/.dockerignore b/managed_vms/websockets/.dockerignore new file mode 100644 index 000000000000..c346979e48f1 --- /dev/null +++ b/managed_vms/websockets/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +Dockerfile +.git +.hg +.svn +env +*.pyc +__pycache__ diff --git a/managed_vms/websockets/Dockerfile b/managed_vms/websockets/Dockerfile new file mode 100644 index 000000000000..c1f36243cd4d --- /dev/null +++ b/managed_vms/websockets/Dockerfile @@ -0,0 +1,16 @@ +FROM gcr.io/google_appengine/python + +# Change the -p argument to use Python 2.7 if desired. +RUN virtualenv /env -p python3.4 + +# Set virtualenv environment variables. This is equivalent to running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +ADD requirements.txt /app/ +RUN pip install -r requirements.txt +ADD . /app/ + +# Use a special gunicorn worker class to support websockets. +CMD gunicorn -b :$PORT -b :65080 -k flask_sockets.worker main:app diff --git a/managed_vms/websockets/README.md b/managed_vms/websockets/README.md new file mode 100644 index 000000000000..809e96c21e5e --- /dev/null +++ b/managed_vms/websockets/README.md @@ -0,0 +1,24 @@ +# Python websockets sample for Google App Engine Managed VMs + +This sample demonstrates how to use websockets on [Google App Engine Managed VMs](https://cloud.google.com/appengine). + +## Setup + +Before you can run or deploy the sample, you will need to create a new firewall rule to allow traffic on port 65080. This port will be used for websocket connections. You can do this with the [Google Cloud SDK](https://cloud.google.com/sdk) with the following command: + + $ gcloud compute firewall-rules create default-allow-websockets \ + --allow tcp:65080 \ + --target-tags websocket \ + --description "Allow websocket traffic on port 65080" + +## Running locally + +Refer to the [top-level README](../README.md) for instructions on running and deploying. + +To run locally, you need to use gunicorn with the ``flask_socket`` worker: + + $ gunicorn -b 127.0.0.1:8080 -b 127.0.0.1:65080 -k flask_sockets.worker main:app + +## Python 3 Compatibility Notice + +This sample currently doesn't run in Python 3 due to an issue with gevent. Gevent is planning to address the issue in the next release. diff --git a/managed_vms/websockets/app.yaml b/managed_vms/websockets/app.yaml new file mode 100644 index 000000000000..069482c5cce3 --- /dev/null +++ b/managed_vms/websockets/app.yaml @@ -0,0 +1,9 @@ +runtime: custom +vm: true + +# [START network] +network: + forwarded_ports: + - 65080 + instance_tag: websocket +# [END network] diff --git a/managed_vms/websockets/main.py b/managed_vms/websockets/main.py new file mode 100644 index 000000000000..5e6905088407 --- /dev/null +++ b/managed_vms/websockets/main.py @@ -0,0 +1,73 @@ +# Copyright 2015 Google Inc. +# +# 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. + +# [START app] +import logging + +from flask import Flask, render_template +from flask_sockets import Sockets +import requests + + +logging.basicConfig(level=logging.INFO) +app = Flask(__name__) +sockets = Sockets(app) + + +# [START metadata] +METADATA_NETWORK_INTERFACE_URL = \ + ('http://metadata/computeMetadata/v1/instance/network-interfaces/0/' + 'access-configs/0/external-ip') + + +def get_external_ip(): + """Gets the instance's external IP address from the Compute Engine metadata + server. If the metadata server is unavailable, it assumes that the + application is running locally. + """ + try: + r = requests.get( + METADATA_NETWORK_INTERFACE_URL, + headers={'Metadata-Flavor': 'Google'}, + timeout=2) + return r.text + except requests.RequestException: + logging.info('Metadata server could not be reached, assuming local.') + return 'localhost' +# [END metadata] + + +@sockets.route('/echo') +def echo_socket(ws): + while True: + message = ws.receive() + ws.send(message) + + +@app.route('/') +def index(): + # Websocket connections must be made directly to this instance, so the + # external IP address of this instance is needed. + external_ip = get_external_ip() + return render_template('index.html', external_ip=external_ip) +# [END app] + +if __name__ == '__main__': + print(""" +This can not be run directly because the Flask development server does not +support web sockets. Instead, use gunicorn: + +gunicorn -b 127.0.0.1:8080 -b 127.0.0.1:65080 -k flask_sockets.worker main:app + +""") diff --git a/managed_vms/websockets/requirements.txt b/managed_vms/websockets/requirements.txt new file mode 100644 index 000000000000..c88e50c63e2e --- /dev/null +++ b/managed_vms/websockets/requirements.txt @@ -0,0 +1,4 @@ +Flask==0.10.1 +Flask-Sockets==0.1 +gunicorn==19.4.5 +requests[security]==2.9.1 diff --git a/managed_vms/websockets/templates/index.html b/managed_vms/websockets/templates/index.html new file mode 100644 index 000000000000..50fea246f004 --- /dev/null +++ b/managed_vms/websockets/templates/index.html @@ -0,0 +1,94 @@ +{# +# Copyright 2015 Google Inc. +# +# 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. +#} +{# [START index] #} + + + + Google App Engine Managed VMs - Python Websockets Echo + + + + + +

Echo demo

+
+ + +
+ +
+

Response:

+

+  
+ +
+

Status:

+

+  
+ + + + + + +{# [END index] #} diff --git a/requirements-dev.txt b/requirements-dev.txt index 77a7629315bf..bec32771f4c5 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -16,3 +16,6 @@ WebOb==1.5.1 WebTest==2.0.20 Werkzeug==0.11.2 nose-timer==0.5.0 +Flask-SQLAlchemy==2.0 +PyMySQL==0.6.6 +python-memcached==1.57