diff --git a/appengine/multitenancy/README.md b/appengine/multitenancy/README.md new file mode 100644 index 000000000000..c8787c136fe9 --- /dev/null +++ b/appengine/multitenancy/README.md @@ -0,0 +1,56 @@ +## Multitenancy Using Namespaces Sample + +This is a sample app for Google App Engine that exercises the [namespace manager Python API](https://cloud.google.com/appengine/docs/python/multitenancy/multitenancy). + +See our other [Google Cloud Platform github +repos](https://github.com/GoogleCloudPlatform) for sample applications and +scaffolding for other python frameworks and use cases. + +## Run Locally +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. + + ``` + gcloud components update app + gcloud auth login + gcloud config set project + ``` + You don't need a valid app-id to run locally, but will need a valid id to deploy below. + +1. Clone this repo. + + ``` + git clone https://github.com/GoogleCloudPlatform/appengine-multitenancy-python.git + ``` +1. Run this project locally from the command line. + + ``` + gcloud preview app run appengine-multitenancy-python/ + ``` + +1. Visit the application at [http://localhost:8080](http://localhost:8080). + +## Deploying + +1. Use the [Cloud Developer Console](https://console.developer.google.com) to create a project/app id. (App id and project id are identical) +2. Configure gcloud with your app id. + + ``` + gcloud config set project + ``` +1. Use the [Admin Console](https://appengine.google.com) to view data, queues, and other App Engine specific administration tasks. +1. Use gcloud to deploy your app. + + ``` + gcloud preview app deploy appengine-multitenancy-python/ + ``` + +1. 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/appengine/multitenancy/app.yaml b/appengine/multitenancy/app.yaml new file mode 100644 index 000000000000..5227472ff0cf --- /dev/null +++ b/appengine/multitenancy/app.yaml @@ -0,0 +1,22 @@ +# This file specifies your Python application's runtime configuration +# including URL routing, versions, static file uploads, etc. See +# https://developers.google.com/appengine/docs/python/config/appconfig +# for details. + +version: 1 +runtime: python27 +api_version: 1 +threadsafe: yes + +# Handlers define how to route requests to your application. +handlers: + +# This handler tells app engine how to route requests to a WSGI application. +# The script value is in the format . +# where is a WSGI application object. +- url: .* # This regex directs all routes to main.app + script: main.app + +libraries: +- name: webapp2 + version: "2.5.2" diff --git a/appengine/multitenancy/favicon.ico b/appengine/multitenancy/favicon.ico new file mode 100644 index 000000000000..23c553a2966c Binary files /dev/null and b/appengine/multitenancy/favicon.ico differ diff --git a/appengine/multitenancy/main.py b/appengine/multitenancy/main.py new file mode 100644 index 000000000000..8d3253b8bf27 --- /dev/null +++ b/appengine/multitenancy/main.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. + +# [START all] +from google.appengine.api import namespace_manager +from google.appengine.ext import ndb +import webapp2 + + +class Counter(ndb.Model): + """Model for containing a count.""" + count = ndb.IntegerProperty() + + +def update_counter(name): + """Increment the named counter by 1.""" + + def _update_counter(inner_name): + counter = Counter.get_by_id(inner_name) + if counter is None: + counter = Counter(id=inner_name) + counter.count = 0 + counter.count += 1 + counter.put() + + # Update counter in a transaction. + ndb.transaction(lambda: _update_counter(name)) + counter = Counter.get_by_id(name) + return counter.count + + +class SomeRequest(webapp2.RequestHandler): + """Perform synchronous requests to update counter.""" + + def get(self): + update_counter('SomeRequest') + # try/finally pattern to temporarily set the namespace. + # Save the current namespace. + namespace = namespace_manager.get_namespace() + try: + namespace_manager.set_namespace('-global-') + x = update_counter('SomeRequest') + finally: + # Restore the saved namespace. + namespace_manager.set_namespace(namespace) + self.response.write('

Updated counters') + self.response.write(' to %s' % x) + self.response.write('

') + + +app = webapp2.WSGIApplication([('/', SomeRequest)], debug=True) +# [END all]