-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Samples for migrating from Python 2.7 runtime to Python 3.7 #3656
Merged
Changes from 19 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
cd7aa91
Initial commit for storage sample
engelke 1eaf0c3
Added Python 3.7 version
engelke 4767c57
Starting urlfetch samples
engelke 77355ae
requests samples work in 2.7 and 3.7 (gcloud deploy app app3.yaml for…
engelke 3cdd870
urlfetch async sample Working in Python 3 App Engine
engelke 89d9f10
Working in Python 2 App Engine, too
engelke 9caffad
Added storage test for migration
engelke 4df7416
Use standard environment variable name for bucket
engelke 16f1900
Added tests
engelke f225af9
WIP on urlfetch authentication
engelke 0157a5e
Added new authenticated replacement for urlfetch
engelke 821b324
Added a test
engelke 9242c48
Incoming app identity verification sample
engelke e06cfab
Added test
engelke 6b7935c
Added READMEs
engelke 992c7c2
Update README.md
engelke 504bc58
Update main.py
engelke 1fcf1ed
Update main.py
engelke fa4b530
Update main.py
engelke 1672977
Include exception in log message
engelke 73b1765
Fixed missing newline at EOF
engelke 561ed96
Removed unused import
engelke 80572d5
Removed unneeded import
engelke 05e3664
Minor lint cleanup
engelke 2d10315
Removed unneeded import
engelke 3777c1a
Added EOF newline
engelke 352a436
Lint fix
engelke 966abce
Merge branch 'master' into msprint
engelke 800bcfe
Merge branch 'master' into msprint
engelke 8b0f9ed
Removed unused import requests
engelke a59edbd
Removed unused import
engelke a89a5df
Tell linter to ignore needed module import order
engelke c2ef75f
Reordered imports
engelke dfa8260
Adding requirements-test to samples
engelke 6544799
Linting directive added
engelke 0c73d35
Merge branch 'master' into msprint
engelke 5361c24
Replaced test requirements with Python 2.7 compatible one
engelke 06527de
Merge branch 'msprint' of github.com:GoogleCloudPlatform/python-docs-…
engelke b72ec73
Another missing requirements-test.txt added
engelke 6fdbe4b
More requirements file updates
engelke 819762b
Typo fixes
engelke d4b9e51
Migration cleanup for automated tests
engelke 9dba33a
Adjusted tests for Python 2.7
engelke eb22969
Make lint happier
engelke d2eb352
Add requests adapter
engelke 2bce712
Needed stub to run in test environment
engelke c7c3269
Trying to get test to run in this environment
engelke 2eb803a
WIP
engelke cc8e15d
WIP
engelke 5447691
WIP
engelke f2ca343
Merge branch 'master' into msprint
engelke File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
## App Engine to App Engine Request Sample | ||
|
||
This sample application shows how an App Engine for Python 2.7 app can verify | ||
the caller's identity for a request from an App Engine for Python 2.7 or | ||
App Engine for Python 3.7 app. | ||
|
||
Requests from an App Engine for Python 2.7 app that use `urlfetch` have | ||
a trustworthy `X-Appengine-Inbound-Appid` header that can be used to verify | ||
the calling app's identity. | ||
|
||
Requests from an App Engine for Python 3.7 app that include an Authorization | ||
header with an ID token for the calling app's default service account can | ||
be used to verify those calling apps' identities. | ||
|
||
The appengine/standard_python37/migration/urlfetch sample app can be used | ||
to make calls to this app with a valid Authorization header. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Copyright 2020 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# 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. | ||
|
||
runtime: python27 | ||
threadsafe: yes | ||
api_version: 1 | ||
|
||
libraries: | ||
- name: ssl | ||
version: latest | ||
|
||
handlers: | ||
- url: .* | ||
script: main.app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Copyright 2020 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# 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 vendor] | ||
from google.appengine.ext import vendor | ||
|
||
# Add any libraries installed in the "lib" folder. | ||
vendor.add('lib') | ||
# [END vendor] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# Copyright 2020 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# 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. | ||
|
||
""" | ||
Authenticate requests coming from other App Engine instances. | ||
""" | ||
|
||
# [START gae_python_app_identity_incoming] | ||
from google.oauth2 import id_token | ||
from google.auth.transport import requests | ||
|
||
import logging | ||
import webapp2 | ||
|
||
|
||
def get_app_id(request): | ||
# Requests from App Engine Standard for Python 2.7 will include a | ||
# trustworthy X-Appengine-Inbound-Appid. Other requests won't have | ||
# that header, as the App Engine runtime will strip it out | ||
incoming_app_id = request.headers.get( | ||
'X-Appengine-Inbound-Appid', None) | ||
if incoming_app_id is not None: | ||
return incoming_app_id | ||
|
||
# Other App Engine apps can get an ID token for the App Engine default | ||
# service account, which will identify the application ID. They will | ||
# have to include at token in an Authorization header to be recognized | ||
# by this method. | ||
auth_header = request.headers.get('Authorization', None) | ||
if auth_header is None: | ||
return None | ||
|
||
# The auth_header must be in the form Authorization: Bearer token. | ||
bearer, token = auth_header.split() | ||
if bearer.lower() != 'bearer': | ||
return None | ||
|
||
try: | ||
info = id_token.verify_oauth2_token(token, requests.Request()) | ||
service_account_email = info['email'] | ||
incoming_app_id, domain = service_account_email.split('@') | ||
if domain != 'appspot.gserviceaccount.com': # Not App Engine svc acct | ||
return None | ||
else: | ||
return incoming_app_id | ||
except Exception as e: | ||
engelke marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# report or log if desired, as here: | ||
logging.warning('Request has bad OAuth2 id token.') | ||
return None | ||
|
||
|
||
class MainPage(webapp2.RequestHandler): | ||
allowed_app_ids = [ | ||
'other-app-id', | ||
'other-app-id-2' | ||
] | ||
|
||
def get(self): | ||
incoming_app_id = get_app_id(self.request) | ||
|
||
if incoming_app_id not in self.allowed_app_ids: | ||
self.abort(403) | ||
|
||
self.response.write('This is a protected page.') | ||
|
||
|
||
app = webapp2.WSGIApplication([ | ||
('/', MainPage) | ||
], debug=True) | ||
# [END gae_python_app_identity_incoming] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Copyright 2020 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# 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 webtest | ||
|
||
import main | ||
|
||
|
||
def test_get(): | ||
app = webtest.TestApp(main.app) | ||
|
||
response = app.get('/') | ||
|
||
assert response.status_int == 403 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
google-auth>=1.14.1 | ||
requests>=2.23.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
## App Engine cloudstorage library Replacement | ||
|
||
The runtime for App Engine standard for Python 2.7 includes the `cloudstorage` | ||
library, which is used to store and retrieve blobs. The sample in this | ||
directory shows how to do the same operations using Python libraries that | ||
work in either App Engine standard for Python runtime, version 2.7 or 3.7. | ||
The sample code is the same for each environment. | ||
|
||
To deploy and run this sample in App Engine standard for Python 2.7: | ||
|
||
pip install -t lib -r requirements.txt | ||
gcloud app deploy | ||
|
||
To deploy and run this sample in App Engine standard for Python 3.7: | ||
|
||
gcloud app deploy app3.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
runtime: python27 | ||
api_version: 1 | ||
threadsafe: yes | ||
|
||
env_variables: | ||
CLOUD_STORAGE_BUCKET: "REPLACE THIS WITH YOUR EXISTING BUCKET NAME" | ||
BLOB_NAME: "my-demo-blob" | ||
|
||
libraries: | ||
- name: ssl | ||
version: latest | ||
|
||
handlers: | ||
- url: /.* | ||
script: main.app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
runtime: python37 | ||
|
||
env_variables: | ||
CLOUD_STORAGE_BUCKET: "REPLACE THIS WITH YOUR EXISTING BUCKET NAME" | ||
BLOB_NAME: "my-demo-blob" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from google.appengine.ext import vendor | ||
|
||
# Add any libraries installed in the "lib" folder. | ||
vendor.add('lib') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# Copyright 2020 Google, LLC. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# 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 flask import Flask, make_response | ||
import os | ||
|
||
from google.cloud import storage | ||
|
||
|
||
app = Flask(__name__) | ||
|
||
|
||
@app.route('/', methods=['GET']) | ||
def get(): | ||
bucket_name = os.environ['CLOUD_STORAGE_BUCKET'] | ||
blob_name = os.environ['BLOB_NAME'] | ||
|
||
client = storage.Client() | ||
bucket = client.bucket(bucket_name) | ||
blob = bucket.blob(blob_name) | ||
|
||
response_text = '' | ||
|
||
text_to_store = b'abcde\n' + b'f'*1024*4 + b'\n' | ||
blob.upload_from_string(text_to_store) | ||
response_text += 'Stored text in a blob.\n\n' | ||
|
||
stored_contents = blob.download_as_string() | ||
if stored_contents == text_to_store: | ||
response_text += 'Downloaded text matches uploaded text.\n\n' | ||
else: | ||
response_text += 'Downloaded text DOES NOT MATCH uploaded text!\n\n' | ||
|
||
response_text += 'Blobs in the bucket:\n' | ||
for blob in client.list_blobs(bucket_name): | ||
response_text += ' ' + blob.id + '\n' | ||
response_text += '\n' | ||
|
||
bucket.delete_blob(blob_name) | ||
response_text += 'Blob ' + blob_name + ' deleted.\n' | ||
|
||
response = make_response(response_text, 200) | ||
response.mimetype = 'text/plain' | ||
return response | ||
|
||
engelke marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if __name__ == '__main__': | ||
# This is used when running locally. | ||
app.run(host='127.0.0.1', port=8080, debug=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Copyright 2020 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# 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 main | ||
import os | ||
|
||
|
||
def test_index(): | ||
main.app.testing = True | ||
client = main.app.test_client() | ||
|
||
r = client.get('/') | ||
assert r.status_code == 200 | ||
assert 'Downloaded text matches uploaded text' in r.data.decode('utf-8') | ||
|
||
bucket_name = os.environ['CLOUD_STORAGE_BUCKET'] | ||
blob_name = os.environ['BLOB_NAME'] | ||
assert ' {}/{}'.format(bucket_name, blob_name) in r.data.decode('utf-8') | ||
assert 'Blob {} deleted.'.format(blob_name) in r.data.decode('utf-8') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
google-cloud-storage==1.25.0 | ||
Flask==1.1.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
## App Engine async urlfetch Replacement | ||
|
||
The runtime for App Engine standard for Python 2.7 includes the `urlfetch` | ||
library, which is used to make HTTP(S) requests. There are several related | ||
capabilities provided by that library: | ||
|
||
* Straightforward web requests | ||
* Asynchronous web requests | ||
* Platform authenticated web requests to other App Engine apps | ||
|
||
The sample in this directory provides a way to make asynchronous web requests | ||
using only generally available Python libraries that work in either App Engine | ||
standard for Python runtime, version 2.7 or 3.7. The sample code is the same | ||
for each environment. | ||
|
||
To deploy and run this sample in App Engine standard for Python 2.7: | ||
|
||
pip install -t lib -r requirements.txt | ||
gcloud app deploy | ||
|
||
To deploy and run this sample in App Engine standard for Python 3.7: | ||
|
||
gcloud app deploy app3.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
runtime: python27 | ||
api_version: 1 | ||
threadsafe: yes | ||
|
||
handlers: | ||
- url: .* | ||
script: main.app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
runtime: python37 |
25 changes: 25 additions & 0 deletions
25
appengine/standard/migration/urlfetch/async/appengine_config.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Copyright 2020 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# 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 google.appengine.ext import vendor | ||
|
||
# Add any libraries installed in the "lib" folder. | ||
vendor.add('lib') | ||
|
||
import requests | ||
import requests_toolbelt.adapters.appengine | ||
|
||
# Use the App Engine Requests adapter. This makes sure that Requests uses | ||
# URLFetch. | ||
requests_toolbelt.adapters.appengine.monkeypatch() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May also want to verify the audience & issuer here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought long and hard about verifying the audience, but eventually decided that I was showing how to migrate from the old X-Appengine-Inbound-Appid approach, which did not perform that check.
The Google verify_oauth2_token verifies tokens issued by Google's OAuth2 server, so I don't think there needs to a separate check of the issuer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM.
The text & samples on their website for other languages vs. Python are a little misleading. I will file a bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@engelke Hold on, I thought the text implied that Python doesn't verify issuer automatically.