-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Infra: Sharing my Bazel rules for GAE and GCF deployments.
- Loading branch information
0 parents
commit 4527798
Showing
21 changed files
with
752 additions
and
0 deletions.
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,3 @@ | ||
__pycache__/ | ||
bazel-* | ||
*.py[co] |
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,8 @@ | ||
{ | ||
"python.formatting.provider": "yapf", | ||
"python.pythonPath": "/usr/bin/python3", | ||
"files.exclude": { | ||
"**/__pycache__": true, | ||
"**/bazel-*": 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,13 @@ | ||
Copyright 2018 Google LLC. 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 | ||
|
||
https://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. |
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,103 @@ | ||
================================================================ | ||
Bazel Rules: Python 3 Google App Engine & Google Cloud Functions | ||
================================================================ | ||
|
||
This repository contains the `Bazel <https://bazel.build>`_ (`Starlark <https://docs.bazel.build/versions/master/skylark/language.html>`_) rules to build Python 3 and deploy to Google App Engine and Google Cloud Functions. | ||
|
||
Usage | ||
===== | ||
|
||
There is a `GAE app example <examples/app_engine/BUILD>`_ and `a GCF example <examples/function/BUILD>`_ in the ``examples`` directory. | ||
|
||
Google App Engine (Python 3) | ||
---------------------------- | ||
|
||
Put this in the ``BUILD`` file:: | ||
|
||
py_binary( | ||
name = "app", | ||
srcs = [ | ||
"app.py", | ||
], | ||
) | ||
|
||
py_app_engine( | ||
name = "app_deploy", | ||
src = ":app", | ||
descriptor = "app.yaml", | ||
entry = "app", | ||
requirements = [ | ||
"flask", | ||
], | ||
) | ||
|
||
and run:: | ||
|
||
examples$ bazel run //app_engine:app_deploy | ||
|
||
Google Cloud Functions (Python 3) | ||
--------------------------------- | ||
|
||
Put this in the ``BUILD`` file:: | ||
|
||
py_binary( | ||
name = "hello", | ||
srcs = [ | ||
"hello.py", | ||
], | ||
) | ||
|
||
py_cloud_function( | ||
name = "hello_deploy", | ||
src = ":hello", | ||
entry = "hello", | ||
) | ||
|
||
and run:: | ||
|
||
examples$ bazel run //function:hello_deploy | ||
|
||
Features | ||
======== | ||
|
||
In the BUILD rule you can also specify: | ||
|
||
PyPI (pip) requirements | ||
either as a package list or pointing to a ``requirements.txt`` file | ||
|
||
The GCP project name | ||
if you don't want to use the default one used by ``gcloud`` | ||
|
||
Version (App Engine only) | ||
the version string of the GAE deployment | ||
|
||
Deploy name (Cloud Functions only) | ||
the name of the deployed function (in GCP) and also in the HTTP path | ||
|
||
Pub/Sub topic trigger (Cloud Functions only) | ||
if you want your function to be triggered by a Cloud Pub/Sub message | ||
|
||
GCS bucket trigger (Cloud Functions only) | ||
if you want your function to be triggered by events from a GCS bucket | ||
|
||
Memory in MiB (Cloud Functions only) | ||
the default is 256 MiB but you can request more, up to 2,048 MiB | ||
|
||
Timeout in seconds (Cloud Functions only) | ||
the default is 60 s but you can request up to 540 s. | ||
|
||
Requirements | ||
============ | ||
|
||
This solution depends on these commands being available: | ||
|
||
* The Python 3 interpreter at ``/usr/bin/python3`` | ||
* `The fish shell <http://fishshell.com/>`_ at ``fish`` | ||
* `The Google Cloud SDK <https://cloud.google.com/sdk/>`_ at ``gcloud`` | ||
|
||
Your Bazel workspace should be set to generate a ZIP package for ``py_binary`` targets, by setting some parameters in the ``.bazelrc`` file, `like this <examples/.bazelrc>`_. | ||
|
||
LICENSE | ||
======= | ||
|
||
``bazel_for_gcloud_python`` is released under `the Apache 2.0 License <LICENSE>`_. |
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 @@ | ||
workspace(name = "bazel_for_gcloud_python") |
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,3 @@ | ||
# Use Python 3 and build to ZIP. | ||
run --python_top=//dev/env:python3 --build_python_zip | ||
build --python_top=//dev/env:python3 --build_python_zip |
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,14 @@ | ||
workspace(name = "bazel_for_gcloud_python_examples") | ||
|
||
local_repository( | ||
name = "bazel_for_gcloud_python", | ||
path = "../", | ||
) | ||
|
||
# Or, use the git repository (and comment the above): | ||
# load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") | ||
# git_repository( | ||
# name = "bazel_for_gcloud_python", | ||
# remote = "https://github.com/weisi/bazel_for_gcloud_python.git", | ||
# commit = "Replace this with a Git commit SHA", | ||
# ) |
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,44 @@ | ||
load("@bazel_for_gcloud_python//infra/serverless:gae_rules.bzl", "py_app_engine") | ||
|
||
py_library( | ||
name = "gae_utils", | ||
srcs = ["gae_utils.py"], | ||
deps = [ | ||
"//function:gcf_utils", | ||
], | ||
visibility = ["//visibility:public"], | ||
) | ||
|
||
py_binary( | ||
name = "app", | ||
srcs = [ | ||
"app.py", | ||
], | ||
data = [ | ||
"static/file.html", | ||
], | ||
deps = [ | ||
":gae_utils", | ||
], | ||
) | ||
|
||
# 'bazel run' this rule to trigger deployment. | ||
py_app_engine( | ||
# Required parameters: | ||
name = "app_deploy", | ||
src = ":app", | ||
descriptor = "app.yaml", | ||
entry = "app", | ||
|
||
# Specify your pip requirements here | ||
requirements = [ | ||
# flask is required. | ||
"flask", | ||
], | ||
|
||
# Specify a GCP project name instead of using the default: | ||
# gcloud_project = "my-gcp-project", | ||
|
||
# Print the arguments for debugging when running the rule: | ||
# 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,19 @@ | ||
from flask import Flask, request | ||
|
||
from app_engine import gae_utils | ||
|
||
app = Flask(__name__) | ||
|
||
@app.route('/') | ||
@gae_utils.plain_text_response | ||
def homepage(): | ||
messages = list() | ||
messages.append('Hi there,') | ||
messages.append(f'You are visiting path "{request.path}".') | ||
messages.append( | ||
f'This app "{gae_utils.current_application_name()}" ' | ||
f'is running on GCP project {gae_utils.current_gcloud_project()}.' | ||
) | ||
messages.append(f'Your IP address is {gae_utils.remote_ip()}') | ||
messages.append('Visit any other path to see a static file.') | ||
return '\n'.join(messages) |
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,14 @@ | ||
runtime: python37 | ||
# service: service-name | ||
instance_class: F1 | ||
automatic_scaling: | ||
min_instances: 0 | ||
max_instances: 1 | ||
handlers: | ||
- url: /$ | ||
secure: always | ||
script: auto | ||
- url: /.*$ | ||
static_files: app_engine/static/file.html | ||
upload: app_engine/static/file\.html | ||
secure: always |
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 @@ | ||
# Helper methods for using Python 3 on Google App Engine. | ||
# Don't use gcf_utils directly if running on GAE, since some details are different. | ||
|
||
from flask import request, Response | ||
import http | ||
import os | ||
|
||
from function.gcf_utils import \ | ||
plain_text_response, \ | ||
http_no_content, \ | ||
current_gcloud_project, \ | ||
remote_ip | ||
|
||
|
||
def current_application_name(): | ||
# In the form of 's~xcode-dev'. | ||
return os.environ.get('GAE_APPLICATION') | ||
|
||
|
||
def current_service_name(): | ||
return os.environ.get('GAE_SERVICE') | ||
|
||
|
||
def current_version(): | ||
return os.environ.get('GAE_VERSION') | ||
|
||
|
||
def current_instance(): | ||
# It's the GAE instance ID, a.k.a. "clone_id". | ||
return os.environ.get('GAE_INSTANCE') |
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,14 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Hello, static file</title> | ||
</head> | ||
|
||
<body> | ||
<h3>Hello, static file</h3> | ||
<p>This is a static file, served on Google App Engine.</p> | ||
</body> | ||
|
||
</html> |
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,6 @@ | ||
py_runtime( | ||
name = "python3", | ||
files = [], | ||
interpreter_path = "/usr/bin/python3", | ||
visibility = ["//visibility:public"], | ||
) |
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,56 @@ | ||
load("@bazel_for_gcloud_python//infra/serverless:gcf_rules.bzl", "py_cloud_function") | ||
|
||
py_library( | ||
name = "gcf_utils", | ||
srcs = [ | ||
"gcf_utils.py", | ||
], | ||
visibility = ["//visibility:public"], | ||
) | ||
|
||
py_binary( | ||
name = "hello", | ||
srcs = [ | ||
"hello.py", | ||
], | ||
deps = [ | ||
":gcf_utils", | ||
] | ||
) | ||
|
||
# 'bazel run' this rule to trigger deployment. | ||
py_cloud_function( | ||
# Required parameters: | ||
name = "hello_deploy", | ||
src = ":hello", | ||
entry = "hello", | ||
|
||
# Specify your pip requirements here: | ||
# requirements = [ | ||
# "google-cloud-logging", | ||
# ], | ||
|
||
# or specify them in another file: | ||
# requirements_file = "//function:requirements.txt" | ||
|
||
# Specify a GCP project name instead of using the default: | ||
# gcloud_project = "my-gcp-project", | ||
|
||
# The function name that appears in HTTP path: | ||
# deploy_name = "function_name", | ||
|
||
# Use pubsub as a trigger instead of HTTP: | ||
# trigger_topic = "pubsub_topic", | ||
|
||
# Use GCS as a trigger instead of HTTP: | ||
# trigger_bucket = "my_gcs_bucket", | ||
|
||
# Specify the memory limit in MiB (default is 256 MiB): | ||
# memory = 2048, | ||
|
||
# Specify the timeout of the function in seconds (default is 60 s): | ||
# timeout = 200, | ||
|
||
# Print the arguments for debugging when running the rule: | ||
# 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,42 @@ | ||
from flask import request, Response | ||
import http | ||
import os | ||
|
||
|
||
def plain_text_response(fn): | ||
def _wrapper(*args, **kwargs): | ||
return Response(fn(*args, **kwargs), mimetype='text/plain') | ||
|
||
_wrapper.__name__ = fn.__name__ | ||
return _wrapper | ||
|
||
|
||
def http_no_content(fn): | ||
def _wrapper(*args, **kwargs): | ||
fn(*args, **kwargs) | ||
return (str(), http.HTTPStatus.NO_CONTENT) | ||
|
||
_wrapper.__name__ = fn.__name__ | ||
return _wrapper | ||
|
||
|
||
def execution_id(): | ||
return request.headers.get('Function-Execution-Id') | ||
|
||
|
||
def remote_ip(): | ||
return request.headers.get('X-Appengine-User-Ip') | ||
|
||
|
||
def current_gcloud_project(): | ||
return os.environ.get('GCP_PROJECT') \ | ||
or os.environ.get('GCLOUD_PROJECT') \ | ||
or os.environ.get('GOOGLE_CLOUD_PROJECT') | ||
|
||
|
||
def current_function_name(): | ||
return os.environ.get('FUNCTION_NAME') | ||
|
||
|
||
def current_function_region(): | ||
return os.environ.get('FUNCTION_REGION') |
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,13 @@ | ||
from function import gcf_utils | ||
|
||
@gcf_utils.plain_text_response | ||
def hello(request): | ||
messages = list() | ||
messages.append('Hi there,') | ||
messages.append(f'You are visiting path "{request.path}".') | ||
messages.append( | ||
f'This function "{gcf_utils.current_function_name()}" ' | ||
f'is running on GCP project {gcf_utils.current_gcloud_project()}.' | ||
) | ||
messages.append(f'Your IP address is {gcf_utils.remote_ip()}') | ||
return '\n'.join(messages) |
Oops, something went wrong.