<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dustin Ingram</title>
    <description>The latest articles on DEV Community by Dustin Ingram (@di).</description>
    <link>https://dev.to/di</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F36168%2F9b1de446-b619-43da-9b33-1e6f1a0b29b7.png</url>
      <title>DEV Community: Dustin Ingram</title>
      <link>https://dev.to/di</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/di"/>
    <language>en</language>
    <item>
      <title>Portable Cloud Functions with the Python Functions Framework</title>
      <dc:creator>Dustin Ingram</dc:creator>
      <pubDate>Thu, 09 Jan 2020 20:10:46 +0000</pubDate>
      <link>https://dev.to/googlecloud/portable-cloud-functions-with-the-python-functions-framework-a6a</link>
      <guid>https://dev.to/googlecloud/portable-cloud-functions-with-the-python-functions-framework-a6a</guid>
      <description>&lt;p&gt;In this post, I'll show how the open-source &lt;a href="https://github.com/googlecloudplatform/functions-framework-python"&gt;Functions Framework for Python&lt;/a&gt; makes Cloud Functions portable across multiple products and ecosystems. I’ll specifically show how to migrate a Python function from Cloud Functions to a service like &lt;a href="https://cloud.google.com/run/"&gt;Cloud Run&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'll also show you how the Functions Framework gives you the ability to test your functions locally as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the Functions Frameworks?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/GoogleCloudPlatform/functions-framework"&gt;Function Frameworks&lt;/a&gt; are open source libraries for writing portable functions -- brought to you by the Google Cloud Functions team.&lt;/p&gt;

&lt;p&gt;The Functions Framework lets you write lightweight functions that run in many different environments, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Cloud Functions&lt;/li&gt;
&lt;li&gt;Cloud Run and Cloud Run on GKE&lt;/li&gt;
&lt;li&gt;Knative-based environments&lt;/li&gt;
&lt;li&gt;Your local development machine&lt;/li&gt;
&lt;li&gt;and elsewhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can think of the Functions Framework for a given language as a wrapper around a single function in that language, which handles everything necessary for your function to receive HTTP requests or Cloud Events such as Pub/Sub.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/GoogleCloudPlatform/functions-framework-python"&gt;Functions Framework for Python&lt;/a&gt; joins previously open-sourced frameworks &lt;a href="https://github.com/GoogleCloudPlatform?q=functions-framework"&gt;for several other popular languages&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going beyond Cloud Functions
&lt;/h2&gt;

&lt;p&gt;Cloud Functions is a powerful and simple tool for quickly deploying a standalone function that lets you integrate various APIs, products, services, or just respond to events.&lt;/p&gt;

&lt;p&gt;However, that simplicity also comes at a cost: the &lt;a href="https://cloud.google.com/functions/docs/concepts/python-runtime"&gt;Cloud Functions Runtime&lt;/a&gt; makes some opinionated choices about the environment in which your function runs, such as the runtime’s base image, the patch version of Python, and the underlying system libraries that are installed.&lt;/p&gt;

&lt;p&gt;For most developers, this is totally fine, as the choices that have been made for the runtime are appropriate for almost all use cases.&lt;/p&gt;

&lt;p&gt;However, occasionally you will want to make a small change, such as install a special platform-level package that you need for a particular task. &lt;/p&gt;

&lt;p&gt;Or, you'll want to continue using your function as-is, but move it to run on Cloud Run to lower costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  An example: Portability &lt;em&gt;without&lt;/em&gt; the Functions Framework
&lt;/h2&gt;

&lt;p&gt;Let's say we have this minimal Cloud Function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello world!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function doesn't do much: it takes an HTTP request, and returns "Hello world!" as a response.&lt;/p&gt;

&lt;p&gt;However, if we wanted to migrate it from Cloud Functions to a service like Cloud Run, which hosts applications, not functions, we'd have to do a lot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;choose a web framework&lt;/li&gt;
&lt;li&gt;configure the web application&lt;/li&gt;
&lt;li&gt;refactor this function as a view in our web app&lt;/li&gt;
&lt;li&gt;add the view as a route&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By this point, our "new" function wouldn't look much at all like your original function. If you had chosen Flask as your web framework, it might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'Hello world!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a lot of extra boilerplate that you don't need.&lt;/p&gt;

&lt;h2&gt;
  
  
  An example: Portability &lt;em&gt;with&lt;/em&gt; the Functions Framework
&lt;/h2&gt;

&lt;p&gt;With the Functions Framework, none of this refactoring is necessary. You can deploy a function to Cloud Run without changing a single line of code in your &lt;code&gt;main.py&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Instead, you can define a &lt;code&gt;Dockerfile&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use the official Python image.&lt;/span&gt;
&lt;span class="c"&gt;# https://hub.docker.com/_/python&lt;/span&gt;
FROM python:3.7-slim

&lt;span class="c"&gt;# Copy local code to the container image.&lt;/span&gt;
ENV APP_HOME /app
WORKDIR &lt;span class="nv"&gt;$APP_HOME&lt;/span&gt;
COPY &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Install production dependencies.&lt;/span&gt;
RUN pip &lt;span class="nb"&gt;install &lt;/span&gt;functions-framework
RUN pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Run the web service on container startup.&lt;/span&gt;
CMD &lt;span class="nb"&gt;exec &lt;/span&gt;functions-framework &lt;span class="nt"&gt;--target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;Dockerfile&lt;/code&gt; largely borrows from the &lt;code&gt;Dockerfile&lt;/code&gt; in the &lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy"&gt;Python Cloud Run quickstart&lt;/a&gt;, but with two key differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it installs the &lt;a href="https://pypi.org/p/functions-framework/"&gt;&lt;code&gt;functions-framework&lt;/code&gt;&lt;/a&gt; dependency&lt;/li&gt;
&lt;li&gt;it invokes the Functions Framework in the &lt;code&gt;CMD&lt;/code&gt; step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last steps are building and deploying the container as you would with any Cloud Run application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/GoogleCloudPlatform/functions-framework-python/tree/master/examples/cloud_run"&gt;See this entire example on Github here.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A local development experience
&lt;/h2&gt;

&lt;p&gt;Another useful feature of the Functions Framework is the ability to run your Cloud Function locally.&lt;/p&gt;

&lt;p&gt;Using the example above, you can run your function on your host machine direction using the Function Framework's handy command-line utility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ functions-framework --target hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The downside to this is that any dependencies need to be installed globally and that there may be conflicts between what your function requires and what your host machine has available.&lt;/p&gt;

&lt;p&gt;Ideally, we would isolate our function by building the container image we defined in our &lt;code&gt;Dockerfile&lt;/code&gt; and running it locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker build -t helloworld . &amp;amp;&amp;amp; docker run --rm -p 8080:8080 -e PORT=8080 helloworld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both of these will start a local development server at &lt;a href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt; that you can send HTTP traffic to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Static Typing
&lt;/h2&gt;

&lt;p&gt;One additional benefit to the Functions Framework is that it makes all of the the necessary types available to statically type your functions.&lt;/p&gt;

&lt;p&gt;For example, an HTTP function could be statically typed before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Tuple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask.wrappers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But now the &lt;code&gt;Context&lt;/code&gt; type is available for background functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;google.cloud.functions.context&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Found this project useful? &lt;a href="https://github.com/GoogleCloudPlatform/functions-framework-python/"&gt;Give it a star it on Github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Found a bug in the Python Functions Framework? &lt;a href="https://github.com/GoogleCloudPlatform/functions-framework-python/issues"&gt;File an issue&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Want similar updates? &lt;a href="https://twitter.com/di_codes"&gt;Follow me on Twitter&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>googlecloud</category>
      <category>googlecloudrun</category>
      <category>cloudfunctions</category>
      <category>functionsframework</category>
    </item>
    <item>
      <title>Using Headless Chrome with Cloud Run</title>
      <dc:creator>Dustin Ingram</dc:creator>
      <pubDate>Tue, 22 Oct 2019 18:48:57 +0000</pubDate>
      <link>https://dev.to/googlecloud/using-headless-chrome-with-cloud-run-3fdp</link>
      <guid>https://dev.to/googlecloud/using-headless-chrome-with-cloud-run-3fdp</guid>
      <description>&lt;p&gt;I often see folks trying to use headless Chrome with services like Google Cloud Functions. The phrase "Headless Chrome" might sound very spooky, but it just means the regular Chrome browser, run without a GUI and instead interacted with programatically.&lt;/p&gt;

&lt;p&gt;Unfortunately, the necessary Chrome binaries are not installed in the Cloud Functions runtime, and there isn't a way to modify the runtime besides installing Python dependencies.&lt;/p&gt;

&lt;p&gt;However, one alternative would be to use &lt;a href="https://cloud.google.com/run/" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt;, which lets you fully customize the runtime, including installing Chrome! So let's do that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring: &lt;code&gt;Dockerfile&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;First, we'll create a &lt;code&gt;Dockerfile&lt;/code&gt;. This uses the official Python base image, installs some additional dependencies, installs Chrome, and installs the dependencies for our application. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Use the official Python image.&lt;/span&gt;
&lt;span class="c"&gt;# https://hub.docker.com/_/python&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.7&lt;/span&gt;

&lt;span class="c"&gt;# Install manually all the missing libraries&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; gconf-service libasound2 libatk1.0-0 libcairo2 libcups2 libfontconfig1 libgdk-pixbuf2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libxss1 fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils

&lt;span class="c"&gt;# Install Chrome&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
&lt;span class="k"&gt;RUN &lt;/span&gt;dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; google-chrome-stable_current_amd64.deb&lt;span class="p"&gt;;&lt;/span&gt; apt-get &lt;span class="nt"&gt;-fy&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Install Python dependencies.&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt requirements.txt&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Copy local code to the container image.&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; APP_HOME /app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; $APP_HOME&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Run the web service on container startup. Here we use the gunicorn&lt;/span&gt;
&lt;span class="c"&gt;# webserver, with one worker process and 8 threads.&lt;/span&gt;
&lt;span class="c"&gt;# For environments with multiple CPU cores, increase the number of workers&lt;/span&gt;
&lt;span class="c"&gt;# to be equal to the cores available.&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; exec gunicorn --bind :$PORT --workers 1 --threads 8 main:app&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Configuring: &lt;code&gt;requirements.txt&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This &lt;code&gt;Dockerfile&lt;/code&gt; uses a &lt;code&gt;requirements.txt&lt;/code&gt; file with specific versions of all our Python dependencies. We'll need to install &lt;a href="https://pypi.org/project/selenium/" rel="noopener noreferrer"&gt;&lt;code&gt;selenium&lt;/code&gt;&lt;/a&gt; as well as the specific version of the &lt;a href="https://pypi.org/project/chromedriver-binary/" rel="noopener noreferrer"&gt;&lt;code&gt;chromedriver-binary&lt;/code&gt;&lt;/a&gt; project that corresponds with the version of Chrome that we've installed:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# requirements.txt

Flask==1.0.2
gunicorn==19.9.0
selenium==3.141.0
chromedriver-binary==77.0.3865.40.0


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Configuring: &lt;code&gt;main.py&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, we'll write a Python application using Flask, Selenium&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# main.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send_file&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;selenium&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;chromedriver_binary&lt;/span&gt;  &lt;span class="c1"&gt;# Adds chromedriver binary to path
&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The following options are required to make headless Chrome
# work in a Docker container
&lt;/span&gt;&lt;span class="n"&gt;chrome_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ChromeOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;chrome_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--headless&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;chrome_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--disable-gpu&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;chrome_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;window-size=1024,768&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;chrome_options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--no-sandbox&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize a new browser
&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;webdriver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Chrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chrome_options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chrome_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.google.com/search?q=headless+horseman&amp;amp;tbm=isch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_screenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spooky.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;send_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spooky.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Testing &amp;amp; Deploying
&lt;/h3&gt;

&lt;p&gt;If we have Docker installed locally, we can run this to test it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker build -t my_screenshot_service .
$ docker run --rm -p 8080:8080 -e PORT=8080 my_screenshot_service


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And view it at &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fyl9fkj4aa4hnuvabajop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fyl9fkj4aa4hnuvabajop.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Otherwise, we can deploy it directly to Cloud Run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ gcloud builds submit --tag gcr.io/YOUR_PROJECT/my_screenshot_service
$ gcloud beta run deploy my_screenshot_service --image gcr.io/YOUR_PROJECT/my_screenshot_service --region us-central1 --platform managed


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And that's it!&lt;/p&gt;

&lt;p&gt;A few notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We're using &lt;code&gt;--no-sandbox&lt;/code&gt; to ensure compatibility with the Docker container, so only point such a service towards URLs you trust.&lt;/li&gt;
&lt;li&gt;Be careful when exposing such a service to user input: For example, if the URL we were screenshotting was supplied by the user, they could potentially take a screenshot of any file on the filesystem as well!&lt;/li&gt;
&lt;li&gt;Be sure to create a new service account with no permission and use it as the identity of the service, for better security. See &lt;a href="https://cloud.google.com/run/docs/securing/service-identity" rel="noopener noreferrer"&gt;https://cloud.google.com/run/docs/securing/service-identity&lt;/a&gt; for an example.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>cloudrun</category>
      <category>docker</category>
      <category>selenium</category>
    </item>
    <item>
      <title>Developer Advocacy: Frequently Asked Questions</title>
      <dc:creator>Dustin Ingram</dc:creator>
      <pubDate>Thu, 19 Sep 2019 15:34:46 +0000</pubDate>
      <link>https://dev.to/di/developer-advocacy-frequently-asked-questions-577k</link>
      <guid>https://dev.to/di/developer-advocacy-frequently-asked-questions-577k</guid>
      <description>&lt;p&gt;I get a lot of questions about what it's like to be a Developer Advocate (DA). I love these questions because each one is an opportunity to advocate for developer advocacy itself!&lt;/p&gt;

&lt;p&gt;Recently a colleague who's considering some DA positions reached out to me for advice, and their questions so nicely summed up every question I've ever been asked about my job that I figured I'd answer them here. &lt;/p&gt;

&lt;p&gt;(Also, it ended up being way too long for an email).&lt;/p&gt;

&lt;p&gt;A bit about me: I'm a Developer Advocate at Google, where I focus on advocacy for the Python community. I have a BS/MS in Computer Science, and spent many years working in university/government research labs until I decided to take a break from academia. After doing a number of non-software things, I joined a &lt;a href="https://www.promptworks.com/" rel="noopener noreferrer"&gt;software consultancy&lt;/a&gt; where I spent a fair amount of time building custom software, before eventually joining Google.&lt;/p&gt;

&lt;p&gt;Also, a disclaimer: I've only been a DA at Google. While I'm aware of differences between what this role means at other organizations, I really can only speak for what it means at my organization. I've also only ever been myself, so I also can't speak for all DAs at Google, either, but I don't think my experience is totally unique amongst us.&lt;/p&gt;

&lt;p&gt;So, first, I'll answer a question which wasn't on the original list, but is one that I get asked the most:&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Developer Advocate?
&lt;/h2&gt;

&lt;p&gt;The way I like to describe this is: I help represent the Python community at Google. When we build products that people in the Python community will use, I help make sure those products are right for those users.&lt;/p&gt;

&lt;p&gt;This means that part of my job is to be deeply involved in the Python community. The way I do this is basically entirely up to me, so this usually manifests itself as the things I like doing in the Python community, like working on the &lt;a href="https://pypi.org" rel="noopener noreferrer"&gt;Python Package Index (PyPI)&lt;/a&gt; or other packaging tools, organizing &lt;a href="http://pytexas.org" rel="noopener noreferrer"&gt;PyTexas&lt;/a&gt;, and attending and &lt;a href="https://di.codes/speaking/" rel="noopener noreferrer"&gt;giving talks at Python conferences&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Being an advocate also means I don't want to "sell" you a product, but I do want you to know that it exists, but &lt;em&gt;only if I think it might help you&lt;/em&gt;. I really don't want to show you tools you don't actually need, and I'm far more likely to tell you to use a simpler or cheaper tool if I think it's a better fit, even if it's not one of our products!&lt;/p&gt;

&lt;p&gt;More importantly, if you &lt;em&gt;are&lt;/em&gt; using the products I work with, I care a lot: I want to know what you think about them, if you've tried them, what your first impressions are, what your challenges have been, etc. And, I want to be a resource for you in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  How did you feel about leaving a traditional engineering role?
&lt;/h2&gt;

&lt;p&gt;I get this question a lot; in fact, one of the first things a friend said to me after I told them I was going to be working in developer relations was: "So does that mean you aren't allowed to code anymore?"&lt;/p&gt;

&lt;p&gt;I actually consider developer/user advocacy a part of a traditional engineering role, just one that is often neglected by traditional engineering organizations. So, while this usually means "how do you feel about not coding all the time", in fact it's entirely up to me: if I wanted to, I could be coding as much as someone in a "traditional engineering" role.&lt;/p&gt;

&lt;p&gt;At Google, we currently have two titles: "Developer Advocate" (DA), and "Developer Programs Engineer" (DPE). The stereotype for a DA would be that they spend their time working with content, talking to customers, and giving talks. Similarly, the stereotype for a DPE would be that they build and maintain our client libraries, write code samples, and work on our open source projects.&lt;/p&gt;

&lt;p&gt;But the truth is that these two titles just represent the extremes of a gradient of a single role, "Developer Programs Engineer". This means that some folks who are DAs still write code, and some folks who are DPEs give talks at conferences. &lt;/p&gt;

&lt;p&gt;And while each individual does have a given set of responsibilities, the "slider" between these two extremes is more or less up to us. If I wanted to do more engineering than I currently do, there are plenty of opportunities for me to do so, and finding that balance is more or less up to me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you feel that if you chose to go back to 'traditional engineering' that it would be possible?
&lt;/h2&gt;

&lt;p&gt;Given all the above, I'd say yes, not only do I feel like I could go back to a "traditional" engineering role, but I probably have an even better perspective now and would be an even better engineer than before.&lt;/p&gt;

&lt;p&gt;Being a DA has given me the ability to connect with such a wide and diverse group of users, and it constantly makes me reëvaluate whether a 'good idea' is what I, the engineer want, or what the user &lt;em&gt;actually&lt;/em&gt; wants. &lt;/p&gt;

&lt;h2&gt;
  
  
  Are you finding a good work/life balance? I know DAs tend to travel quite a bit.
&lt;/h2&gt;

&lt;p&gt;Absolutely, but this is heavily influenced by the culture at Google. The nice part about my job is that, contrary to popular belief, travel is not actually a requirement to be a DA at Google. There are DAs that are constantly traveling, and there are DAs that only travel once or twice a year, and both are totally fine and will be successful in their careers.&lt;/p&gt;

&lt;p&gt;This means that I travel exactly as much as I want to, and at any time I have the ability to adjust how much I'm traveling to suit my work/life balance.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you find to be the biggest challenges with a role such as this?
&lt;/h2&gt;

&lt;p&gt;The biggest challenge: measuring your impact.&lt;/p&gt;

&lt;p&gt;Measuring impact is challenging because it's hard to connect the outcomes we want to achieve with the things we do. Things like "developers have more confidence in our products than a year ago" is not only a hard metric to measure, but also hard to find the source of the shift even if we could measure it. Was it all the talks we gave? The YouTube content? &lt;a href="https://www.blog.google/products/google-cloud/most-calculated-digits-pi/" rel="noopener noreferrer"&gt;The Guinness World Record&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;This makes it hard to know what's worth working on, and also makes it hard to prove that you're doing a good job. View counts on blog posts are a decent proxy for popularity, but since "having a popular blog" isn't our goal, beyond that it's hard to quantify.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the day of a DA look like at Google? What do you spend your time doing?
&lt;/h2&gt;

&lt;p&gt;The impression is that a DA spends all their time writing talks, traveling to conferences, and speaking. This makes sense, because the DAs you probably see the most are the ones that are doing these things, and you don't see them doing anything else.&lt;/p&gt;

&lt;p&gt;A lot of people are surprised to discover that traveling and speaking is actually a very small part of my job, given the number of talks I give, and how often they see me at events. And, like I mentioned before, the main reason I do it is because it's one of my ways to be part of the community, and something I enjoy.&lt;/p&gt;

&lt;p&gt;I think part of this perception comes from the conflation between an &lt;em&gt;advocate&lt;/em&gt; and an &lt;em&gt;evangelist&lt;/em&gt;. There is endless debate about what these terms mean in the context of developer relations, but at least in the context of Google, the difference is this: an evangelist wants users to know that a product exists; advocates want products to know that their users exist.&lt;/p&gt;

&lt;p&gt;(In my opinion, good way to tell if you're looking at an evangelist position or an advocacy position: who would you report to? Is it an engineering director? Or marketing?)&lt;/p&gt;

&lt;p&gt;For example, you might notice very few of my general conference talks are actually about our products. This is because my talks are simply a means for interacting with my community, not a venue to inform you about our products.&lt;/p&gt;

&lt;p&gt;So, if speaking is actually optional for being a DA, and I do it optionally, but takes up a small amount of my time, what the heck am I doing? Well, a lot:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Talking to customers:&lt;/strong&gt; When I travel to events, I usually try to set up some meetings with local developers that use Google Cloud, or try to meet some while I'm there. I also occasionally sit in on calls with customers, either to check in on how their experience is going, talk about a specific issue, or help them determine how best to use our products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Talking to non-customers:&lt;/strong&gt; I'm generally and genuinely curious what everyone's working on, and the tools they're using, and whether these tools are effective or not. This helps me build a general sense of what people expect to be able to do with our products, strengths and weaknesses in other products, and new trends in the industry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Talking to our product teams:&lt;/strong&gt; Usually this is around a launch of a new product or feature, but sometimes is to set general strategy for the Python community.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Being "user zero" for new products or features:&lt;/strong&gt; This means playing with new things and seeing how intuitive they are, and whether there are any edge cases we haven't considered, but might be valid use cases for our users, and filing bug reports as necessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keeping an eye on places where users might raise issues:&lt;/strong&gt; I am constantly trolling StackOverflow, and have various ways to keep an eye on mediums like DEV, Twitter, Hacker News, etc. for people talking about their experience with Google Cloud, and then taking that feedback and turning it into actionable issues for our engineering teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Working in open source:&lt;/strong&gt; This generally satisfies my desire to write code, and has the added benefit of keeping me involved in the community, and also giving back to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating content for Python developers:&lt;/strong&gt; This may be anything from blog posts, to launch announcements, to YouTube videos, to podcasts, to documentation, to workshops/tutorials/codelabs, &lt;a href="https://dev.to/googlecloud/ministry-of-silly-runtimes-vintage-python-on-cloud-run-3b9d"&gt;to stunts like this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting strategy for the Python community:&lt;/strong&gt; I help determine our presence and sponsorships at events, but also contribute to wider plans for the future of our products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Serving as a general source of expertise at Google about Python and the it's community:&lt;/strong&gt; One of the most fun parts of my job is that I get pulled into discussions with teams all across Google who want input about how to work with the Python ecosystem, target Python users, and make things work in a way that they would expect and appreciate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leading a team of people who work on Python advocacy:&lt;/strong&gt; I'm not the only person at Google who cares about Python! In order to coordinate our efforts, I lead a team of people who do all the same things I do (and often, much more).&lt;/p&gt;

&lt;h2&gt;
  
  
  Do you have any tips or advice for someone considering this role?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1) Work on your empathy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the very least, start doing more advocacy at your current organization. Attend or observe some user experience studies of your product, or just sit down with a friend who's never used it and see if they can make it through your quickstart. I promise, it will be eye-opening.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Pick your community, and deeply understand it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maybe you've already done the first half, but have you done the second? You need to be the voice at your organization for a huge group of people, and you need to be able to accurately represent what they want.&lt;/p&gt;

&lt;p&gt;Keep in mind that the class of people who make up your community are not "everyone who uses our product" or "everyone who uses our competitors product". That's not a community, it's a market segment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) Consider joining a software consultancy first.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To quote the person who's taught me a &lt;em&gt;lot&lt;/em&gt; about doing developer relations in the last year:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1137127323780624384-19" src="https://platform.twitter.com/embed/Tweet.html?id=1137127323780624384"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1137127323780624384-19');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1137127323780624384&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Obviously I'm biased too, because this was my route to my current role, but I totally agree.&lt;/p&gt;

&lt;p&gt;As a consultant, if you're not doing "client advocacy" with your team, you're gonna build something the client doesn't want. And if you can't educate the client, they're not going to know how to use what you built. Either way: you're not gonna have that client very long.&lt;/p&gt;

&lt;p&gt;Finally, another question not on the original list, but one I get asked a lot:&lt;/p&gt;

&lt;h2&gt;
  
  
  Are you hiring?
&lt;/h2&gt;

&lt;p&gt;There are developer relations teams across just about every product area at Google, and we're always looking for new colleagues. Take a look at &lt;a href="https://careers.google.com/jobs/results/?company=Google&amp;amp;employment_type=FULL_TIME&amp;amp;hl=en_US&amp;amp;jlo=en_US&amp;amp;q=%22developer%20relations%22&amp;amp;skills=&amp;amp;sort_by=relevance" rel="noopener noreferrer"&gt;this job search query&lt;/a&gt; to find a role that might suit you, and feel free to reach out to me on Twitter (&lt;a href="https://twitter.com/di_codes" rel="noopener noreferrer"&gt;@di_codes&lt;/a&gt;) or in the comments if you have any questions that aren't answered here!&lt;/p&gt;

</description>
      <category>google</category>
      <category>devrel</category>
      <category>advocacy</category>
    </item>
    <item>
      <title>HTML templates with Google Cloud Functions</title>
      <dc:creator>Dustin Ingram</dc:creator>
      <pubDate>Thu, 23 May 2019 21:01:29 +0000</pubDate>
      <link>https://dev.to/googlecloud/html-templates-with-google-cloud-functions-29bc</link>
      <guid>https://dev.to/googlecloud/html-templates-with-google-cloud-functions-29bc</guid>
      <description>&lt;p&gt;A Google Cloud Function can be called in response to multiple types of events, and of the most useful events to use is an HTTP request. This allows you to make a &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt; or other HTTP request to a URL provided by your Cloud Function, and have it run in response to the given request.&lt;/p&gt;

&lt;p&gt;When you activate your Cloud Function via HTTP, you can also optionally return a response, much like a HTTP server would. In the simplest examples, this is usually just a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello, World!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This creates an HTTP response that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.0 200 OK
Content-Length: 13
Content-Type: text/html; charset=utf-8

Hello, World!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You'll notice here that the &lt;code&gt;Content-Type&lt;/code&gt; is &lt;code&gt;text/html&lt;/code&gt;, but the response is not HTML at all, it's just plaintext. What if we wanted to add some formatting?&lt;/p&gt;

&lt;h1&gt;
  
  
  Adding some formatting
&lt;/h1&gt;

&lt;p&gt;Let's say we wanted to make part of our response &lt;strong&gt;bold&lt;/strong&gt;. We could do this by adding some HTML tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello, &amp;lt;b&amp;gt;World!&amp;lt;/b&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Which would give us the following response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.0 200 OK
Content-Length: 20
Content-Type: text/html; charset=utf-8

Hello, &amp;lt;b&amp;gt;World!&amp;lt;/b&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This would probably render correctly in our browsers, but only because modern browsers have an amazing propensity for dealing with invalid HTML. To make this actually a valid HTML response, we would need to change our function to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"""
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;Saying Hello&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  Hello, &amp;lt;b&amp;gt;World!&amp;lt;/b&amp;gt;
&amp;lt;/body&amp;gt;
"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is starting to get a bit long, so perhaps we would make it a separate variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;INDEX_TEMPLATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;Saying Hello&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  Hello, &amp;lt;b&amp;gt;World!&amp;lt;/b&amp;gt;
&amp;lt;/body&amp;gt;
"""&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;INDEX_TEMPLATE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Passing variables and controlling flow
&lt;/h1&gt;

&lt;p&gt;If we want to pass variables into our template, we could just do it with &lt;code&gt;.format&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;INDEX_TEMPLATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;Saying Hello&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  Hello, &amp;lt;b&amp;gt;{place}!&amp;lt;/b&amp;gt;
&amp;lt;/body&amp;gt;
"""&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;INDEX_TEMPLATE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"World"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What if we had multiple places we wanted to say hello to, on separate lines?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;INDEX_TEMPLATE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;places&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"World"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"dev.to"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;At this point, our "homemade" template would start break down, and we'd need to start getting creative in order to make it continue to work.&lt;/p&gt;

&lt;h1&gt;
  
  
  Inline templates with Cloud Functions
&lt;/h1&gt;

&lt;p&gt;Instead, let's use some existing tools. Since under the hood Cloud Functions is using Flask, this means that the &lt;a href="http://flask.pocoo.org/docs/1.0/api/#flask.render_template"&gt;&lt;code&gt;render_template_string&lt;/code&gt;&lt;/a&gt; function is available to us. This lets us render a &lt;a href="http://jinja.pocoo.org/docs/"&gt;Jinja2&lt;/a&gt; template in a similar way as our custom solution, but also lets us do loops, if statements, etc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;render_template_string&lt;/span&gt;

&lt;span class="n"&gt;INDEX_TEMPLATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;Saying Hello&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  {% for place in places %}
  Hello, &amp;lt;b&amp;gt;{{ place }}!&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;
  {% endfor %}
&amp;lt;/body&amp;gt;
"""&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_template_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;INDEX_TEMPLATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;places&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"World"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"dev.to"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This would produce a response such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP/1.0 200 OK
Content-Length: 140
Content-Type: text/html; charset=utf-8

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;Saying Hello&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  Hello, &amp;lt;b&amp;gt;World!&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;
  Hello, &amp;lt;b&amp;gt;dev.to!&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Templates directory
&lt;/h1&gt;

&lt;p&gt;Finally, perhaps your template is quite long and you don't want it to exist in the same file as your function, you can put it in a directory called &lt;code&gt;templates&lt;/code&gt; and use the &lt;a href="http://flask.pocoo.org/docs/1.0/api/#flask.render_template"&gt;&lt;code&gt;render_template&lt;/code&gt;&lt;/a&gt; function instead.&lt;/p&gt;

&lt;p&gt;Your source files should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── main.py
├── requirements.txt
└── templates
    └── hello.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;hello.html&lt;/code&gt; being the same as our previous example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Saying Hello&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  {% for place in places %}
  Hello, &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;{{ place }}!&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;
  {% endfor %}
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the function configured like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hello.html'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;places&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"World"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"dev.to"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And that's it! From here, you can use all of Jinja2's features that you normally would in any other template.&lt;/p&gt;

</description>
      <category>python</category>
      <category>cloudfunctions</category>
      <category>serverless</category>
      <category>jinja2</category>
    </item>
    <item>
      <title>Hitting a Cloud Function when you submit a Google Form</title>
      <dc:creator>Dustin Ingram</dc:creator>
      <pubDate>Wed, 15 May 2019 17:55:06 +0000</pubDate>
      <link>https://dev.to/googlecloud/hitting-a-cloud-function-when-you-submit-a-google-form-3hkn</link>
      <guid>https://dev.to/googlecloud/hitting-a-cloud-function-when-you-submit-a-google-form-3hkn</guid>
      <description>&lt;p&gt;&lt;a href="http://forms.google.com" rel="noopener noreferrer"&gt;Google Forms&lt;/a&gt; is a great tool--easy to create, easy to use, etc. I was recently at a meetup talking to users about Google Cloud Platform and someone said they wished they could hook a Google Form up to a Google Cloud Function.&lt;/p&gt;

&lt;p&gt;After thinking for a second, I thought "this must be possible". Although there's not usually a lot of direct interoperability between G Suite and Google Cloud, I knew that you could write an Apps Script trigger for most Google Docs, which would let you make HTTP requests, and that Cloud Functions can accept arbitrary HTTP requests as events, so there's no reason this shouldn't work.&lt;/p&gt;

&lt;p&gt;And in fact, it totally works!&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing your Cloud Function
&lt;/h2&gt;

&lt;p&gt;There's really nothing special about the Cloud Function you'll create for this: it's just like any other Cloud Function that accepts a HTTP request. Let's assume we're going to be passing JSON back and forth as the payload:&lt;/p&gt;

&lt;p&gt;The function would look something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;form_trigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;silent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Payload was: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will get the JSON from the &lt;code&gt;POST&lt;/code&gt; request, print it to our logs, and just return an "OK". Obviously you can do then whatever you want with the payload at this point: store it in your database, kick off a job, etc.&lt;/p&gt;

&lt;p&gt;Deploy that function with &lt;code&gt;gcloud functions deploy form_trigger --trigger-http --runtime python37&lt;/code&gt; and move on to the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating your Google Form
&lt;/h2&gt;

&lt;p&gt;The Google Form you create will just be like any other form: you can have multi-part questions, multiple-choice questions, free-form questions, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a script for your form
&lt;/h2&gt;

&lt;p&gt;Here's where we can start connecting the dots. First, literally select the three dots in the menu when you're editing your form:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqk2x3e3i6pvkpj19pmr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqk2x3e3i6pvkpj19pmr8.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From this menu, choose &lt;strong&gt;"Script editor"&lt;/strong&gt; to get taken to the Apps Script editor for this form. This should give you a file named &lt;code&gt;Code.gs&lt;/code&gt; with an empty function like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We're going to update it to be something like the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// Replace with the URL to your deployed Cloud Function&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOUR CLOUD FUNCTION URL&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;// This function will be called when the form is submitted&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// The event is a FormResponse object:&lt;/span&gt;
  &lt;span class="c1"&gt;// https://developers.google.com/apps-script/reference/forms/form-response&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;formResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Gets all ItemResponses contained in the form response&lt;/span&gt;
  &lt;span class="c1"&gt;// https://developers.google.com/apps-script/reference/forms/form-response#getItemResponses()&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;itemResponses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItemResponses&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Gets the actual response strings from the array of ItemResponses&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;itemResponses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Post the payload as JSON to our Cloud Function  &lt;/span&gt;
  &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;method&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;responses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;responses&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Make sure to update the &lt;code&gt;url&lt;/code&gt; variable with the full URL to your deployed Cloud Function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a trigger
&lt;/h2&gt;

&lt;p&gt;Now, from the script editor, click on &lt;strong&gt;"Edit"&lt;/strong&gt; &amp;gt; &lt;strong&gt;"Current project's triggers"&lt;/strong&gt; . This will prompt you to give your project a name, and then take you to the G Suite Developer Hub, and show you all the triggers for your project (there should be none).&lt;/p&gt;

&lt;p&gt;In the bottom right corner, select "+ Add Trigger" to add a new trigger:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtzvmoix4zjmlln4vxay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtzvmoix4zjmlln4vxay.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dialog should default to the &lt;code&gt;onSubmit&lt;/code&gt; function you declared for your form, otherwise select it. &lt;/p&gt;

&lt;p&gt;You should also be sure to change the "Select event type" field from "On open" to "On form submit".&lt;/p&gt;

&lt;p&gt;This will create a popup window to allow Apps Script to view and modify your form.&lt;/p&gt;

&lt;p&gt;Finally, save your trigger and it should appear in the list of triggers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;At this point, your form is fully connected to Cloud Functions! You can submit some test responses and you should see the responses appearing in the logs for your function.&lt;/p&gt;

&lt;p&gt;From here, you can hook the function up to other services, or do things conditionally based on the responses, and you shouldn't need to write additional Apps Script to make it happen.&lt;/p&gt;

</description>
      <category>cloudfunctions</category>
      <category>googleforms</category>
      <category>python</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Moving your cron job to the cloud with Google Cloud Functions</title>
      <dc:creator>Dustin Ingram</dc:creator>
      <pubDate>Fri, 03 May 2019 00:53:18 +0000</pubDate>
      <link>https://dev.to/googlecloud/moving-your-cron-job-to-the-cloud-with-google-cloud-functions-1ecp</link>
      <guid>https://dev.to/googlecloud/moving-your-cron-job-to-the-cloud-with-google-cloud-functions-1ecp</guid>
      <description>&lt;p&gt;A &lt;a href="https://en.wikipedia.org/wiki/Cron" rel="noopener noreferrer"&gt;cron&lt;/a&gt; job is a way to run a program on a server at a specified interval. This is often a small script that does some repeatable task, like collect metrics, check the health of a service, take a snapshot of some dataset, etc.&lt;/p&gt;

&lt;p&gt;The cron utility has a highly customizable format, called a crontab file, which allows us to run essentially any command at whatever interval we want. You can have your script run as often as once a minute, and as infrequently as once a year.&lt;/p&gt;

&lt;p&gt;Most often, a cron job takes some input, does a little processing, and then generates some output. The input could be a web page, another application, a database, etc. The output might be anything from adding a row to a table in a database, to putting a file in storage, to sending a message.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using cron jobs
&lt;/h1&gt;

&lt;p&gt;Cron jobs have a &lt;em&gt;lot&lt;/em&gt; of uses: essentially anything you want to happen in a repeatable way can be made into a cron job.&lt;/p&gt;

&lt;p&gt;One thing I like to use cron jobs for is to keep an eye on websites that I visit infrequently. For example, I'd like to see articles posted to &lt;a href="https://news.ycombinator.com" rel="noopener noreferrer"&gt;Hacker News&lt;/a&gt; about Serverless, but I don't have time to check it every day, and look at the title of every post to see if it's about Python.&lt;/p&gt;

&lt;p&gt;Instead, I can write a cron job to do this for me. The steps will be roughly as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once a day, get the top stories from the API;&lt;/li&gt;
&lt;li&gt;Iterate over every story;&lt;/li&gt;
&lt;li&gt;If any of their titles match "serverless":

&lt;ul&gt;
&lt;li&gt;send an email with the links&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Here's the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/python
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send_email&lt;/span&gt;

&lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://hacker-news.firebaseio.com/v0/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;top_stories_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;topstories.json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;item_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;item/{}.json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scan_hacker_news&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Make a request to the API
&lt;/span&gt;    &lt;span class="n"&gt;top_stories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;top_stories_url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;serverless_stories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="c1"&gt;# Iterate over every story
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;story_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;top_stories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;story_id&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;serverless&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;story&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;serverless_stories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;story&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# If there are any, send an email
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;serverless_stories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverless_stories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes a request to the API, iterates over every story, filters out stories that have "serverless" in the title, and sends me an email (we'll leave that function as an exercise to the reader).&lt;/p&gt;

&lt;p&gt;If I wanted to set this up as a cron job on my Linux machine, I would save it to a file (&lt;code&gt;send_me_pythons.py&lt;/code&gt;), make it executable (&lt;code&gt;chmod 755 send_me_pythons.py&lt;/code&gt;) and put it in my local bin (&lt;code&gt;/usr/local/bin/send_me_pythons.py&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Then, I would edit my crontab (&lt;code&gt;/etc/crontab&lt;/code&gt;) and add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 0 * * * /usr/local/bin/send_me_pythons.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs the script once a day at midnight. I like to use &lt;a href="https://crontab.guru/" rel="noopener noreferrer"&gt;https://crontab.guru/&lt;/a&gt; to ensure that my crontab is right before I set it. Here's a diagram of each field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 0 0 * * * /usr/local/bin/send_me_pythons.py
# │ │ │ │ │ └── run this script
# │ │ │ │ └──── every year
# │ │ │ └────── every month
# │ │ └──────── every day
# │ └────────── at hour zero
# └──────────── at minute zero
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the crontab is set, my machine will pick up on the new cron job and run it as often as I've specified.&lt;/p&gt;

&lt;h1&gt;
  
  
  Moving our script to the cloud
&lt;/h1&gt;

&lt;p&gt;This is great, but it has one huge downside: now I have to keep my server up and running to ensure that the emails get sent, otherwise I'll be missing valuable Serverless content. And this is further complicated by the fact that either I should be using this server for other things besides running this little script once a day, or I need to keep an entire server up and running just for this little script! Not ideal.&lt;/p&gt;

&lt;p&gt;Instead, let's take this function to the cloud. First, we'll turn it into a &lt;a href="https://cloud.google.com/functions/" rel="noopener noreferrer"&gt;Google Cloud Function&lt;/a&gt;. We'll wrap our entire existing script in a Python function, and put it in a file called &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send_email&lt;/span&gt;

&lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://hacker-news.firebaseio.com/v0/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;top_stories_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;topstories.json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;item_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;item/{}.json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_pythons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Make a request to the API
&lt;/span&gt;    &lt;span class="n"&gt;top_stories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;top_stories_url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;serverless_stories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="c1"&gt;# Iterate over every story
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;story_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;top_stories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;story&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;story_id&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;serverless&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;story&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;serverless_stories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;story&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# If there are any, send an email
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;serverless_stories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverless_stories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Only the lines that need to happen every time the function is called actually need to be in the &lt;code&gt;send_pythons&lt;/code&gt; function. The import statements only need to be executed once when the function is loaded, and can be left outside the function.&lt;/p&gt;

&lt;p&gt;Next, we'll define our dependencies. We're using &lt;code&gt;requests&lt;/code&gt;, so we need to put it in our &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requests==2.20.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can deploy this with the &lt;a href="https://cloud.google.com/sdk/gcloud/" rel="noopener noreferrer"&gt;&lt;code&gt;gcloud&lt;/code&gt;&lt;/a&gt; command line tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud beta functions deploy test --runtime python37 --trigger-http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give us an endpoint, something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://us-central1-[PROJECT_ID].cloudfunctions.net/send_pythons
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And making an HTTP &lt;code&gt;GET&lt;/code&gt; request to that endpoint will result in our function being run.&lt;/p&gt;

&lt;h1&gt;
  
  
  Scheduling our script in the cloud
&lt;/h1&gt;

&lt;p&gt;Now we've got our script as a function in the cloud, all we need to do is schedule it. We can create a new Google Cloud Scheduler job with the &lt;code&gt;gcloud&lt;/code&gt; command line tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud beta scheduler jobs create http send_pythons_job \
    --schedule="0 0 * * *" \
    --uri=https://us-central1-[PROJECT_ID].cloudfunctions.net/send_pythons
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This specifies a name for our job, &lt;code&gt;send_pythons_job&lt;/code&gt;, which is unique per-project. It also specifies the crontab schedule we set above, and points the job to our HTTP function we created.&lt;/p&gt;

&lt;p&gt;We can list our job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud beta scheduler jobs list --project=$PROJECT
ID                LOCATION     SCHEDULE (TZ)        TARGET_TYPE  STATE
send_pythons_job  us-central1  0 0 * * * (Etc/UTC)  HTTP         ENABLED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if we want to run our job out of schedule, we can do it from the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud beta scheduler jobs run send_pythons_job
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Next steps
&lt;/h1&gt;

&lt;p&gt;There's lots more you can do with Cloud Functions + Cloud Scheduler! Follow the links below to learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/appengine/docs/standard/python/config/cron" rel="noopener noreferrer"&gt;Schedule more complex tasks with Cloud Scheduler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/scheduler/docs/tut-pub-sub" rel="noopener noreferrer"&gt;Use Cloud Scheduler and Pub/Sub to trigger a Cloud Function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Send email from App Engine with &lt;a href="https://cloud.google.com/appengine/docs/standard/python3/sending-emails-with-mailjet" rel="noopener noreferrer"&gt;Mailjet&lt;/a&gt; or &lt;a href="https://cloud.google.com/appengine/docs/standard/python3/sending-emails-with-sendgrid" rel="noopener noreferrer"&gt;SendGrid&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;All code © Google w/ Apache 2 license&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>cloudfunctions</category>
      <category>serverless</category>
      <category>cloudscheduler</category>
    </item>
    <item>
      <title>Using Secrets in Google Cloud Functions</title>
      <dc:creator>Dustin Ingram</dc:creator>
      <pubDate>Thu, 25 Apr 2019 20:41:46 +0000</pubDate>
      <link>https://dev.to/googlecloud/using-secrets-in-google-cloud-functions-5aem</link>
      <guid>https://dev.to/googlecloud/using-secrets-in-google-cloud-functions-5aem</guid>
      <description>&lt;p&gt;Google Cloud Functions makes it easy to build serverless Python programs. This post will show you how you can use the &lt;a href="https://cloud.google.com/secret-manager" rel="noopener noreferrer"&gt;Google Secret Manager&lt;/a&gt; to safely and securely use secrets in your function.&lt;/p&gt;

&lt;p&gt;Hard-coding or using environment variables to store plain-text strings that should be "secret", like API keys, secret tokens for cookies, etc. generally isn't recommended. Any third-party dependency or library you use has access to these same environment variables.&lt;/p&gt;

&lt;p&gt;However, it's very convenient to store secrets along side your function. You can still do it, as long as you store these secrets safely and securely. &lt;/p&gt;

&lt;p&gt;We'll do this by storing our secret with the &lt;a href="https://cloud.google.com/secret-manager" rel="noopener noreferrer"&gt;Google Secret Manager&lt;/a&gt;, and accessing our secrets at the application layer. Doing so will limit access to the secret to just members of your team who have access to the secret (and, of course, the function when it's running on Google Cloud).&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup
&lt;/h1&gt;

&lt;p&gt;I'll assume that you've already created a Google Cloud project with a name (mine is &lt;code&gt;my_cloud_project&lt;/code&gt;). If you haven't, go ahead and do that now.&lt;/p&gt;

&lt;h1&gt;
  
  
  Storing your secret
&lt;/h1&gt;

&lt;p&gt;Now that you've created a project, the next step is to create a secret in it. We can do this on the command line with the &lt;code&gt;gcloud&lt;/code&gt; tool! Also, we'll do it via Cloud Shell, so no credentials ever have to leave the cloud.&lt;/p&gt;

&lt;p&gt;First, &lt;a href="https://console.cloud.google.com/home/dashboard?cloudshell=true" rel="noopener noreferrer"&gt;launch Cloud Shell&lt;/a&gt; for your project.&lt;/p&gt;

&lt;p&gt;You should see a prompt that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Welcome to Cloud Shell! Type "help" to get started.
Your Cloud Platform project in this session is set to my_cloud_project.
Use “gcloud config set project [PROJECT_ID]” to change to a different project.
di@cloudshell:~ (my_cloud_project)$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we'll enable the &lt;a href="https://console.developers.google.com/apis/api/secretmanager.googleapis.com/overview" rel="noopener noreferrer"&gt;Secret Manager API&lt;/a&gt; (and the &lt;a href="https://console.developers.google.com/apis/api/cloudfunctions.googleapis.com/overview" rel="noopener noreferrer"&gt;Cloud Functions API&lt;/a&gt;, while we're at it):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud services enable secretmanager.googleapis.com cloudfunctions.googleapis.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we'll create a new secret with the &lt;code&gt;gcloud&lt;/code&gt; command line tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo -n "The chickens are in the hayloft." | \
    gcloud secrets create my-secret \
      --data-file=- \
      --replication-policy automatic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--data-file=-&lt;/code&gt; flag allows us to pipe the secret to the &lt;code&gt;gcloud&lt;/code&gt; command from the output of the previous command, which just &lt;code&gt;echo&lt;/code&gt;s the secret (with no newline).  &lt;/p&gt;

&lt;p&gt;Our secret is now securely stored. If we want to verify this, we can use the following command to access our secret on the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud secrets versions access 1 --secret="my-secret"
The chickens are in the hayloft.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Using your secret
&lt;/h1&gt;

&lt;p&gt;Now we'll write our function. Here's our whole function that we'll deploy, that will live in a file called &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.cloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;secretmanager&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secretmanager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SecretManagerServiceClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;secret_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;project_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-gcp-project&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;projects/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;project_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/secrets/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;secret_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/versions/latest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;access_secret_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;secret_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UTF-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;secret_hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;secret_string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function is determining the resource name for the secret based on the function's environment, accessing the latest version of our secret, and then decoding the response, and finally returning the original string.&lt;/p&gt;

&lt;p&gt;Also note how the function is doing this work &lt;em&gt;outside&lt;/em&gt; of the Python function itself. This will speed up our function by only accessing the secret once when the Cloud Function is instantiated, and not once for every single request.&lt;/p&gt;

&lt;p&gt;We'll also need to tell our function to install the &lt;code&gt;google-cloud-secret-manager&lt;/code&gt; library, so we can access the secret from Python. In a file called &lt;code&gt;requirements.txt&lt;/code&gt;, add the line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;google-cloud-secret-manager==2.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, your directory structure should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── main.py
└── requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Applying Permissions
&lt;/h1&gt;

&lt;p&gt;We'll also need to give our function permission to access the secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud secrets add-iam-policy-binding my-secret \
    --role roles/secretmanager.secretAccessor \
    --member serviceAccount:my-cloud-project@appspot.gserviceaccount.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;serviceAccount&lt;/code&gt; here is the default service account for Cloud Functions.&lt;/p&gt;

&lt;h1&gt;
  
  
  Deploying your app
&lt;/h1&gt;

&lt;p&gt;The last step is to deploy our function with the &lt;code&gt;gcloud&lt;/code&gt; tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud functions deploy secret_hello \
    --runtime python38 \
    --trigger-http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can test our function once it's deployed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud functions call secret_hello
executionId: 4856xc8an4fa
result: The chickens are in the hayloft.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Next steps
&lt;/h1&gt;

&lt;p&gt;There's lots more you can do with Cloud Functions! Follow the links below to learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/functions/docs/writing/background" rel="noopener noreferrer"&gt;Write "background" trigger functions that respond to events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/functions/docs/monitoring/" rel="noopener noreferrer"&gt;Monitor functions at a high level with Stackdriver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/functions/docs/bestpractices/tips" rel="noopener noreferrer"&gt;See additional tips &amp;amp; tricks for writing Cloud Functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;All code © Google w/ Apache 2 license&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>cloudfunctions</category>
      <category>serverless</category>
      <category>secretmanager</category>
    </item>
    <item>
      <title>Serverless Python Quickstart with Google Cloud Functions</title>
      <dc:creator>Dustin Ingram</dc:creator>
      <pubDate>Thu, 18 Apr 2019 20:06:40 +0000</pubDate>
      <link>https://dev.to/googlecloud/serverless-python-quickstart-with-google-cloud-functions-19bb</link>
      <guid>https://dev.to/googlecloud/serverless-python-quickstart-with-google-cloud-functions-19bb</guid>
      <description>&lt;p&gt;There's a new runtime in town: the Google Cloud team has brought everything awesome about Python to Google Cloud Functions by releasing a Python 3.7 runtime in addition to the existing Node.js runtime.&lt;/p&gt;

&lt;p&gt;Cloud Functions are great for writing lightweight, self-contained microapps that can be individually scaled (and are a fraction of the cost of deploying a full webapp). They can also be used as "triggers": for example, automatically resizing an image into a smaller thumbnail once it's uploaded to a Google Cloud Storage bucket.&lt;/p&gt;

&lt;p&gt;And now that you can write Cloud Functions in Python, you have the full power of the Python language (and any package from PyPI) at your disposal.&lt;/p&gt;

&lt;h1&gt;
  
  
  Initial setup
&lt;/h1&gt;

&lt;p&gt;Cloud Functions have a simple directory structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── main.py
└── requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;main.py&lt;/code&gt;, we'll define our function. In &lt;code&gt;requirements.txt&lt;/code&gt;, we'll add any requirements that we might need to install from PyPI (or just leave it empty if there aren't any).&lt;/p&gt;

&lt;h1&gt;
  
  
  Writing our function
&lt;/h1&gt;

&lt;p&gt;To start, let's write a simple function in our &lt;code&gt;main.py&lt;/code&gt; file that just says hello:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello World!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Running locally
&lt;/h1&gt;

&lt;p&gt;To quickly test out our function, we can run it locally before deploying.&lt;/p&gt;

&lt;p&gt;You'll need Python installed to follow this part. If you don't yet, see &lt;a href="https://cloud.google.com/python/setup" rel="noopener noreferrer"&gt;"Setting Up A Python Development Environment"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you've got a Python environment locally, we can add the following code to the end of our &lt;code&gt;main.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a virtual environment, activate it, and install Flask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python -m venv env
$ source env/bin/activate
(env) $ pip install flask
Collecting flask
  ...
Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7
flask-1.0.2 itsdangerous-0.24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and visit &lt;a href="http://localhost:5000" rel="noopener noreferrer"&gt;http://localhost:5000&lt;/a&gt; to see your endpoint locally.&lt;/p&gt;

&lt;h1&gt;
  
  
  Deploying our function
&lt;/h1&gt;

&lt;p&gt;Our function is only useful if it's deployed! Before deploying our function, you'll need to &lt;a href="https://cloud.google.com/sdk/docs/" rel="noopener noreferrer"&gt;install and initialize the &lt;code&gt;gcloud&lt;/code&gt; command line tool&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once the &lt;code&gt;gcloud&lt;/code&gt; SDK is installed, we can deploy our function with one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud beta functions deploy hello --trigger-http --runtime python37 --project YOUR_PROJECT_NAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once deployed, this command will give you a link to your live function.&lt;/p&gt;

&lt;h1&gt;
  
  
  Passing in variables
&lt;/h1&gt;

&lt;p&gt;You're probably going to want to pass some data into your function. One way to do this is via URL parameters, like &lt;code&gt;http://foo.bar/hello?name=Dustin&lt;/code&gt;. Let's make our example optionally take a parameter called &lt;code&gt;name&lt;/code&gt; and use that instead of "World":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;World&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then when we redeploying our function, and visit &lt;code&gt;https://YOUR_PROJECT_NAME.cloudfunctions.net/hello?name=dev.to&lt;/code&gt;, it will say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hello dev.to!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Adding dependencies
&lt;/h1&gt;

&lt;p&gt;One of the nice things about Cloud Functions is that you can install the same dependencies from PyPI that you would normally use anywhere else.&lt;/p&gt;

&lt;p&gt;Let's add a dependency on the &lt;a href="https://pypi.org/project/emoji/" rel="noopener noreferrer"&gt;&lt;code&gt;emoji&lt;/code&gt;&lt;/a&gt; package, so we can add an optional emoji to the end of our message as well.&lt;/p&gt;

&lt;p&gt;We'll add the name and version of the package to our &lt;code&gt;requirements.txt&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;emoji==0.5.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will automatically get installed when we redeploy the function.&lt;/p&gt;

&lt;p&gt;Next, we'll want to import and use the library in our function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;emoji&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;World&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;reaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emojize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;reaction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;reaction&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can visit &lt;code&gt;https://YOUR_PROJECT_NAME.cloudfunctions.net/hello?name=dev.to&amp;amp;reaction=:wave:&lt;/code&gt; and it will say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hello dev.to 👋&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Next steps
&lt;/h1&gt;

&lt;p&gt;There's lots more you can do with Cloud Functions! Follow the links below to learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/functions/docs/writing/background" rel="noopener noreferrer"&gt;Write "background" trigger functions that respond to events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/functions/docs/monitoring/" rel="noopener noreferrer"&gt;Monitor functions at a high level with Stackdriver&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/functions/docs/bestpractices/tips" rel="noopener noreferrer"&gt;See additional tips &amp;amp; tricks for writing Cloud Functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;All code © Google w/ Apache 2 license&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>cloudfunctions</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Ministry of Silly Runtimes: Vintage Python on Cloud Run</title>
      <dc:creator>Dustin Ingram</dc:creator>
      <pubDate>Tue, 09 Apr 2019 16:06:57 +0000</pubDate>
      <link>https://dev.to/googlecloud/ministry-of-silly-runtimes-vintage-python-on-cloud-run-3b9d</link>
      <guid>https://dev.to/googlecloud/ministry-of-silly-runtimes-vintage-python-on-cloud-run-3b9d</guid>
      <description>&lt;h2&gt;
  
  
  Introducing Cloud Run
&lt;/h2&gt;

&lt;p&gt;Today, Google announced &lt;a href="http://cloud.google.com/run"&gt;Cloud Run&lt;/a&gt;, a product that is really beautiful in its simplicity:&lt;/p&gt;

&lt;p&gt;1) You write an service/app/function/whatever that listens for HTTP requests on &lt;code&gt;$PORT&lt;/code&gt;;&lt;br&gt;
2) You write a &lt;code&gt;Dockerfile&lt;/code&gt; that inherits from any base image, installs any necessary dependencies, and starts your service;&lt;br&gt;
3) You deploy the image to Cloud Run, and it scales on demand (even to zero).&lt;/p&gt;

&lt;p&gt;Basically, it's "serverless Docker containers as a service" and it's exactly what I think the PaaS/FaaS development community could use right now, for a number of reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The most idiomatic runtime possible&lt;/strong&gt;&lt;br&gt;
There's no potential for vendor-specific weirdness here. Really, you can't get more idiomatic than using &lt;code&gt;FROM python:3.7&lt;/code&gt;, or whatever other base image you want to use. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customizability without paying for idle virtual machines&lt;/strong&gt;&lt;br&gt;
If you have specific workloads that don't fit within the bounds of the most "common" runtimes usually provided, before now your best option was to spin up a custom VM which won't scale to zero and will spend a lot of time sitting idle. With Cloud Run, you can use the &lt;code&gt;Dockerfile&lt;/code&gt; to customize your runtime to your heart's content. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Containerization means portability&lt;/strong&gt;&lt;br&gt;
Defining your runtime with a standard &lt;code&gt;Dockerfile&lt;/code&gt; means you can take it anywhere—&lt;em&gt;even locally&lt;/em&gt;—which means that you're not locked into a specific vendor, and that  local development of your application is built right in without needing to do anything special.&lt;/p&gt;

&lt;p&gt;These are all great, and they'll certainly help developers do great things...&lt;/p&gt;

&lt;p&gt;...But what if you wanted to do something really &lt;em&gt;silly&lt;/em&gt; with it instead?&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the first Python 1.x serverless runtime
&lt;/h2&gt;

&lt;p&gt;When I first started playing with Cloud Run, I really wanted to test the limits of what I could do with it. Could I really run &lt;em&gt;any&lt;/em&gt; runtime I wanted? Could I run a super modern Python 3.8.0a3 alpha release? (Yes). Well, what about a "vintage" version of Python? Could I deploy a "Hello World" app running on Python 1.x to Cloud run? 🤔&lt;/p&gt;

&lt;h4&gt;
  
  
  Searching for "vintage" images
&lt;/h4&gt;

&lt;p&gt;The first problem was finding a base image for Python 1.x. The &lt;a href="https://hub.docker.com/_/python/"&gt;official Python Docker images&lt;/a&gt; only publish images for 2.7 and up, so no luck there. I couldn't find it anywhere else, so it looked like I'd have to build the base image from scratch myself.&lt;/p&gt;

&lt;h4&gt;
  
  
  Searching for "vintage" source
&lt;/h4&gt;

&lt;p&gt;Building the base image from scratch meant that I had to:&lt;/p&gt;

&lt;p&gt;a) Find the old source code for a Python 1.x version;&lt;br&gt;
b) Get it to compile on a modern distribution's base image;&lt;br&gt;
c) Publish this new base image.&lt;/p&gt;

&lt;p&gt;Finding the source was slightly challenging. The source for modern Python releases is published on &lt;a href="https://www.python.org/downloads/source/"&gt;https://www.python.org/downloads/source/&lt;/a&gt;, but this only goes back as far as Python 2.0.1, released June 22, 2001.&lt;/p&gt;

&lt;p&gt;Luckily, I found &lt;a href="https://legacy.python.org/download/releases/src/"&gt;https://legacy.python.org/download/releases/src/&lt;/a&gt;, which includes source releases as far back as Python 1.0.1, which was released just over 25 years ago on February 15th, 1994.&lt;/p&gt;

&lt;p&gt;I decided to attempt to target the latest Ubuntu distribution, and amazingly, with just some small patches, I was able to get Python 1.0.1, 1.1, 1.2 and 1.3 to compile under Ubuntu 18.04.&lt;/p&gt;

&lt;h4&gt;
  
  
  Publishing some "vintage" images
&lt;/h4&gt;

&lt;p&gt;I published these resulting builds as Docker images under the &lt;a href="https://hub.docker.com/r/dustingram/vintage-python"&gt;&lt;code&gt;vintage-python&lt;/code&gt;&lt;/a&gt; repository, so this means you can now start a vintage Python REPL whenever you want:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run -it dustingram/vintage-python:1.0.1
Python 1.0.1 (Feb 23 2019)
Copyright 1991-1994 Stichting Mathematisch Centrum, Amsterdam
&amp;gt;&amp;gt;&amp;gt; print "Hello world!"
Hello world!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It also means I can do &lt;code&gt;FROM dustingram/vintage-python:1.0.1&lt;/code&gt; in my &lt;code&gt;Dockerfile&lt;/code&gt;, which is a huge step towards having my silly runtime.&lt;/p&gt;

&lt;h4&gt;
  
  
  Searching for an HTTP server
&lt;/h4&gt;

&lt;p&gt;I had my heart set on Python 1.0.1, but after realizing that Python didn't ship with an HTTP server until Python 1.3, I decided to use that instead of attempting to backport the HTTP server to an older version.&lt;/p&gt;

&lt;p&gt;In Python 1.3, I was able to write this:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;BaseHTTPServer&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HTTPServer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;SimpleHTTPServer&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SimpleHTTPRequestHandler&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SimpleHTTPRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;protocol_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"HTTP/1.1"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end_headers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from Python "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'__main__'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Running...'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;HTTPServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;'0.0.0.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;MyHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serve_forever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This sets up a basic HTTP/1.1 server on port 8080 that responds to every request with the given message. I &lt;em&gt;really&lt;/em&gt; wanted to prove that this was a vintage version of Python serving the request, so I included the &lt;code&gt;sys.version&lt;/code&gt; along with the response.&lt;/p&gt;

&lt;h4&gt;
  
  
  Containerizing some vintage Python
&lt;/h4&gt;

&lt;p&gt;After saving that as &lt;code&gt;app.py&lt;/code&gt;, all that was left to do was write the &lt;code&gt;Dockerfile&lt;/code&gt;. Since my app doesn't have any dependencies, the &lt;code&gt;Dockerfile&lt;/code&gt; is pretty close to &lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy"&gt;the &lt;code&gt;Dockerfile&lt;/code&gt; in the Cloud Run quickstart&lt;/a&gt;&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; dustingram/vintage-python:1.3&lt;/span&gt;

&lt;span class="c"&gt;# Copy local code to the container image.&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; APP_HOME /app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; $APP_HOME&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Service must listen to $PORT environment variable.&lt;/span&gt;
&lt;span class="c"&gt;# This default value facilitates local development.&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PORT 8080&lt;/span&gt;

&lt;span class="c"&gt;# Run the web service on container startup.&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here, we inherit from my vintage Python image, copy in my &lt;code&gt;app.py&lt;/code&gt; to the container, set the &lt;code&gt;PORT&lt;/code&gt; environment variable (for local development), and then start the server by running &lt;code&gt;python app.py&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Deploying some vintage Python
&lt;/h4&gt;

&lt;p&gt;With Cloud Run, there's a two step deploy:&lt;/p&gt;

&lt;p&gt;Build and submit your image with Cloud Build:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud builds submit --tag gcr.io/my_project_id/helloworld
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, deploy the submitted image to Cloud Run:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud beta run deploy --image gcr.io/my_project_id/helloworld
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then it was deployed!&lt;/p&gt;

&lt;p&gt;See it in action here: &lt;a href="https://helloworld-bpzdnjbrsq-uc.a.run.app/"&gt;https://helloworld-bpzdnjbrsq-uc.a.run.app/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://cloud.google.com/run"&gt;Cloud Run&lt;/a&gt; is definitely flexible enough to run any runtime I could throw at it -- even one of the oldest versions of Python. Obviously I wouldn't seriously encourage anyone to use Python 1.x in production: any version past it's EOL is essentially unmaintained and severely feature-limited.&lt;/p&gt;

&lt;p&gt;From here, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See the &lt;a href="https://github.com/di/vintage-python"&gt;&lt;code&gt;vintage-python&lt;/code&gt; Github repo&lt;/a&gt; for more details on the runtimes;&lt;/li&gt;
&lt;li&gt;Use the &lt;a href="https://hub.docker.com/r/dustingram/vintage-python"&gt;&lt;code&gt;vintage-python&lt;/code&gt; images&lt;/a&gt; in your own silly project;&lt;/li&gt;
&lt;li&gt;See the &lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy"&gt;Cloud Run quickstart&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/di_codes"&gt;Follow me on Twitter&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>serverless</category>
      <category>googlecloud</category>
      <category>cloudrun</category>
    </item>
  </channel>
</rss>
