functions_framework 0.4.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +11 -11
- data/bin/functions-framework +4 -1
- data/bin/functions-framework-ruby +1 -1
- data/docs/deploying-functions.md +22 -13
- data/docs/overview.md +6 -6
- data/docs/testing-functions.md +59 -11
- data/docs/writing-functions.md +202 -13
- data/lib/functions_framework.rb +41 -10
- data/lib/functions_framework/cli.rb +97 -22
- data/lib/functions_framework/function.rb +142 -47
- data/lib/functions_framework/legacy_event_converter.rb +10 -11
- data/lib/functions_framework/registry.rb +36 -12
- data/lib/functions_framework/server.rb +27 -22
- data/lib/functions_framework/testing.rb +123 -24
- data/lib/functions_framework/version.rb +1 -1
- metadata +24 -16
- data/lib/functions_framework/cloud_events.rb +0 -45
- data/lib/functions_framework/cloud_events/content_type.rb +0 -222
- data/lib/functions_framework/cloud_events/errors.rb +0 -42
- data/lib/functions_framework/cloud_events/event.rb +0 -84
- data/lib/functions_framework/cloud_events/event/field_interpreter.rb +0 -150
- data/lib/functions_framework/cloud_events/event/v0.rb +0 -236
- data/lib/functions_framework/cloud_events/event/v1.rb +0 -223
- data/lib/functions_framework/cloud_events/http_binding.rb +0 -310
- data/lib/functions_framework/cloud_events/json_format.rb +0 -173
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: faa749d3b692f754f7ad60dfc3a5b18c2b9b26c757769ab51e16786a6289b4ad
|
4
|
+
data.tar.gz: 8d918a95adc8caa077fdc19fbc1ec3f3b531a6de279b19da2282bbd4479305d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e372039d4885f765f634f1f90975282b12c28f97acf7ba9a5ad4dba100a444c5d6a494a8792a9ed2c2fe6cb6f969ee886dab0f26eda8334ebe21288d506413e
|
7
|
+
data.tar.gz: 1f61e76bbc9ba84283e120e77da66dc488ded6178bd07691d42d8c5472605b8d839dd85c75747b3611dfe87bffae3db2d2a4803389ff56fb3b0415f663fce2b3
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,38 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### v0.7.0 / 2020-09-25
|
4
|
+
|
5
|
+
* Now requires Ruby 2.5 or later.
|
6
|
+
* BREAKING CHANGE: Renamed "context" hash to "globals" and made it read-only for normal functions.
|
7
|
+
* BREAKING CHANGE: Server config is no longer passed to startup blocks.
|
8
|
+
* ADDED: Provided a "logger" convenience method in the context object.
|
9
|
+
* ADDED: Globals can be set from startup blocks, which is useful for initializing shared resources.
|
10
|
+
* ADDED: Support for testing startup tasks in the Testing module.
|
11
|
+
* ADDED: Support for controlling logging in the Testing module.
|
12
|
+
* FIXED: Fixed crash introduced in 0.6.0 when a block didn't declare an expected argument.
|
13
|
+
* FIXED: Better support for running concurrent tests.
|
14
|
+
* DOCS: Expanded documentation on initialization, execution context, and shared resources.
|
15
|
+
* DEPRECATED: The functions-framework executable is deprecated. Use functions-framework-ruby instead.
|
16
|
+
|
17
|
+
### v0.6.0 / 2020-09-17
|
18
|
+
|
19
|
+
* ADDED: You can use the --version flag to print the framework version
|
20
|
+
* ADDED: You can use the --verify flag to verify that a given function is defined
|
21
|
+
* ADDED: You can now define blocks that are executed at server startup
|
22
|
+
|
23
|
+
### v0.5.2 / 2020-09-06
|
24
|
+
|
25
|
+
* FIXED: Use global $stderr rather than STDERR for logger
|
26
|
+
* DOCS: Fix instructions for deployment to Google Cloud Functions
|
27
|
+
|
28
|
+
### v0.5.1 / 2020-07-20
|
29
|
+
|
30
|
+
* Updated some documentation links. No functional changes.
|
31
|
+
|
32
|
+
### v0.5.0 / 2020-07-09
|
33
|
+
|
34
|
+
* Removed embedded CloudEvents classes and added the official CloudEvents SDK as a dependency. A `FunctionsFramework::CloudEvents` alias provides backward compatibility.
|
35
|
+
|
3
36
|
### v0.4.1 / 2020-07-08
|
4
37
|
|
5
38
|
* Fixed unsupported signal error on Windows.
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
# Functions Framework [](https://
|
1
|
+
# Functions Framework [](https://googlecloudplatform.github.io/functions-framework-ruby/latest) [](https://badge.fury.io/rb/functions_framework)
|
2
2
|
|
3
3
|
An open source framework for writing lightweight, portable Ruby functions that
|
4
4
|
run in a serverless environment. Functions written to this Framework will run
|
5
5
|
in many different environments, including:
|
6
6
|
|
7
|
-
* [Google Cloud Functions](https://cloud.google.com/functions) *(
|
8
|
-
* [
|
7
|
+
* [Google Cloud Functions](https://cloud.google.com/functions) *(alpha)*
|
8
|
+
* [Google Cloud Run](https://cloud.google.com/run)
|
9
9
|
* Any other [Knative](https://github.com/knative)-based environment
|
10
10
|
* Your local development machine
|
11
11
|
|
@@ -42,11 +42,11 @@ requiring an HTTP server or complicated request handling logic.
|
|
42
42
|
|
43
43
|
## Supported Ruby versions
|
44
44
|
|
45
|
-
This library is supported on Ruby 2.
|
45
|
+
This library is supported on Ruby 2.5+.
|
46
46
|
|
47
47
|
Google provides official support for Ruby versions that are actively supported
|
48
48
|
by Ruby Core—that is, Ruby versions that are either in normal maintenance or
|
49
|
-
in security maintenance, and not end of life. Currently, this means Ruby 2.
|
49
|
+
in security maintenance, and not end of life. Currently, this means Ruby 2.5
|
50
50
|
and later. Older versions of Ruby _may_ still work, but are unsupported and not
|
51
51
|
recommended. See https://www.ruby-lang.org/en/downloads/branches/ for details
|
52
52
|
about the Ruby support schedule.
|
@@ -60,7 +60,7 @@ Create a `Gemfile` listing the Functions Framework as a dependency:
|
|
60
60
|
```ruby
|
61
61
|
# Gemfile
|
62
62
|
source "https://rubygems.org"
|
63
|
-
gem "functions_framework", "~> 0.
|
63
|
+
gem "functions_framework", "~> 0.7"
|
64
64
|
```
|
65
65
|
|
66
66
|
Create a file called `app.rb` and include the following code. This defines a
|
@@ -98,26 +98,26 @@ Stop the server with `CTRL+C`.
|
|
98
98
|
|
99
99
|
These guides provide additional getting-started information.
|
100
100
|
|
101
|
-
* **[Writing Functions](https://
|
101
|
+
* **[Writing Functions](https://googlecloudplatform.github.io/functions-framework-ruby/latest/file.writing-functions.html)**
|
102
102
|
: How to write functions that respond to HTTP requests, industry-standard
|
103
103
|
[CloudEvents](https://cloudevents.io), as well as events sent from Google
|
104
104
|
Cloud services such as [Pub/Sub](https://cloud.google.com/pubsub) and
|
105
105
|
[Storage](https://cloud.google.com/storage).
|
106
|
-
* **[Testing Functions](https://
|
106
|
+
* **[Testing Functions](https://googlecloudplatform.github.io/functions-framework-ruby/latest/file.testing-functions.html)**
|
107
107
|
: How to use the testing features of the Functions Framework to write local
|
108
108
|
unit tests for your functions using standard Ruby testing frameworks such
|
109
109
|
as [Minitest](https://github.com/seattlerb/minitest) and
|
110
110
|
[RSpec](https://rspec.info/).
|
111
|
-
* **[Running a Functions Server](https://
|
111
|
+
* **[Running a Functions Server](https://googlecloudplatform.github.io/functions-framework-ruby/latest/file.running-a-functions-server.html)**
|
112
112
|
: How to use the `functions-framework-ruby` executable to run a local
|
113
113
|
functions server.
|
114
|
-
* **[Deploying Functions](https://
|
114
|
+
* **[Deploying Functions](https://googlecloudplatform.github.io/functions-framework-ruby/latest/file.deploying-functions.html)**
|
115
115
|
: How to deploy functions to
|
116
116
|
[Google Cloud Functions](https://cloud.google.com/functions) or
|
117
117
|
[Google Cloud Run](https://cloud.google.com/run).
|
118
118
|
|
119
119
|
The library reference documentation can be found at:
|
120
|
-
https://
|
120
|
+
https://googlecloudplatform.github.io/functions-framework-ruby
|
121
121
|
|
122
122
|
Additional examples are available in the `examples` directory:
|
123
123
|
https://github.com/GoogleCloudPlatform/functions-framework-ruby/blob/master/examples/
|
data/bin/functions-framework
CHANGED
@@ -16,4 +16,7 @@
|
|
16
16
|
|
17
17
|
require "functions_framework/cli"
|
18
18
|
|
19
|
-
|
19
|
+
puts "WARNING: The functions-framework executable is deprecated and will be"
|
20
|
+
puts "removed in a future version. Please use functions-framework-ruby instead."
|
21
|
+
|
22
|
+
::FunctionsFramework::CLI.new.parse_args(::ARGV).run.complete
|
data/docs/deploying-functions.md
CHANGED
@@ -34,7 +34,7 @@ You can run Ruby functions on Google Cloud Functions by selecting the `ruby26`
|
|
34
34
|
runtime. This runtime uses a recent release of Ruby 2.6. Support for other
|
35
35
|
versions of Ruby may be added in the future.
|
36
36
|
|
37
|
-
> **Note:** Ruby support on Cloud Functions is currently in limited
|
37
|
+
> **Note:** Ruby support on Cloud Functions is currently in limited alpha.
|
38
38
|
> It is not yet suitable for production workloads, and support is best-effort
|
39
39
|
> only. Access is currently limited to selected early-access users.
|
40
40
|
|
@@ -46,30 +46,39 @@ is to `bundle install` or `bundle update` and run your local tests prior to
|
|
46
46
|
deploying. Cloud Functions will not accept your function unless an up-to-date
|
47
47
|
`Gemfile.lock` is present.
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
Also, make sure your source file (which defines your function) is called
|
50
|
+
`app.rb`. The Functions Framework lets you choose a function source file, but
|
51
|
+
Cloud Functions currently requires you to use `app.rb`.
|
52
|
+
|
53
|
+
Decide _which_ function in the source file to invoke, that is, the name that you
|
54
|
+
used when writing the function. This is called the **target**. (Note that if you
|
55
|
+
did not specify a name for the function, it defaults to the name `function`.)
|
56
|
+
|
57
|
+
Choose a Cloud Functions **name** for your function. The **name** identifies
|
58
|
+
this function deployment (e.g. in the cloud console) and is also part of the
|
59
|
+
function's default URL. (Note: the **name** and the **target** do not have to
|
60
|
+
be the same value.)
|
53
61
|
|
54
62
|
Then, issue the gcloud command to deploy:
|
55
63
|
|
56
64
|
```sh
|
57
|
-
gcloud functions deploy $YOUR_FUNCTION_NAME
|
58
|
-
|
59
|
-
|
65
|
+
gcloud functions deploy $YOUR_FUNCTION_NAME \
|
66
|
+
--project=$YOUR_PROJECT_ID \
|
67
|
+
--runtime=ruby26 \
|
68
|
+
--trigger-http \
|
69
|
+
--entry-point=$YOUR_FUNCTION_TARGET
|
60
70
|
```
|
61
71
|
|
62
|
-
The
|
63
|
-
|
64
|
-
|
65
|
-
`gcloud config set project`.
|
72
|
+
The `--entry-point=` flag can be omitted if the **target** has the same value
|
73
|
+
as the **name**. Additionally, the `--project` flag can be omitted if you've
|
74
|
+
set your default project using `gcloud config set project`.
|
66
75
|
|
67
76
|
If your function handles events rather than HTTP requests, you'll need to
|
68
77
|
replace `--trigger-http` with a different trigger. For details, see the
|
69
78
|
[reference documentation](https://cloud.google.com/sdk/gcloud/reference/functions/deploy)
|
70
79
|
for `gcloud functions deploy`.
|
71
80
|
|
72
|
-
To update your deployment, just redeploy using the same function name
|
81
|
+
To update your deployment, just redeploy using the same function **name**.
|
73
82
|
|
74
83
|
### Configuring Cloud Functions deployments
|
75
84
|
|
data/docs/overview.md
CHANGED
@@ -8,8 +8,8 @@ The Functions Framework is an open source framework for writing lightweight,
|
|
8
8
|
portable Ruby functions that run in a serverless environment. Functions written
|
9
9
|
to this Framework will run in many different environments, including:
|
10
10
|
|
11
|
-
* [Google Cloud Functions](https://cloud.google.com/functions) *(
|
12
|
-
* [
|
11
|
+
* [Google Cloud Functions](https://cloud.google.com/functions) *(alpha)*
|
12
|
+
* [Google Cloud Run](https://cloud.google.com/run)
|
13
13
|
* Any other [Knative](https://github.com/knative)-based environment
|
14
14
|
* Your local development machine
|
15
15
|
|
@@ -46,11 +46,11 @@ requiring an HTTP server or complicated request handling logic.
|
|
46
46
|
|
47
47
|
## Supported Ruby versions
|
48
48
|
|
49
|
-
This library is supported on Ruby 2.
|
49
|
+
This library is supported on Ruby 2.5+.
|
50
50
|
|
51
51
|
Google provides official support for Ruby versions that are actively supported
|
52
52
|
by Ruby Core—that is, Ruby versions that are either in normal maintenance or
|
53
|
-
in security maintenance, and not end of life. Currently, this means Ruby 2.
|
53
|
+
in security maintenance, and not end of life. Currently, this means Ruby 2.5
|
54
54
|
and later. Older versions of Ruby _may_ still work, but are unsupported and not
|
55
55
|
recommended. See https://www.ruby-lang.org/en/downloads/branches/ for details
|
56
56
|
about the Ruby support schedule.
|
@@ -64,7 +64,7 @@ Create a `Gemfile` listing the Functions Framework as a dependency:
|
|
64
64
|
```ruby
|
65
65
|
# Gemfile
|
66
66
|
source "https://rubygems.org"
|
67
|
-
gem "functions_framework", "~> 0.
|
67
|
+
gem "functions_framework", "~> 0.7"
|
68
68
|
```
|
69
69
|
|
70
70
|
Create a file called `app.rb` and include the following code. This defines a
|
@@ -121,7 +121,7 @@ These guides provide additional getting-started information.
|
|
121
121
|
[Google Cloud Run](https://cloud.google.com/run).
|
122
122
|
|
123
123
|
The library reference documentation can be found at:
|
124
|
-
https://
|
124
|
+
https://googlecloudplatform.github.io/functions-framework-ruby
|
125
125
|
|
126
126
|
Additional examples are available in the GitHub repository:
|
127
127
|
https://github.com/GoogleCloudPlatform/functions-framework-ruby/blob/master/examples/
|
data/docs/testing-functions.md
CHANGED
@@ -17,9 +17,8 @@ the output. You do not need to set up (or mock) an actual server.
|
|
17
17
|
The Functions Framework provides utility methods that streamline the process of
|
18
18
|
setting up functions and the environment for testing, constructing input
|
19
19
|
parameters, and interpreting results. These are available in the
|
20
|
-
|
21
|
-
|
22
|
-
describe block.
|
20
|
+
{FunctionsFramework::Testing} module. Generally, you can include this module in
|
21
|
+
your Minitest test class or RSpec describe block.
|
23
22
|
|
24
23
|
```ruby
|
25
24
|
require "minitest/autorun"
|
@@ -45,10 +44,10 @@ end
|
|
45
44
|
|
46
45
|
To test a function, you'll need to load the Ruby file that defines the function,
|
47
46
|
and run the function to test its results. The Testing module provides a method
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
{FunctionsFramework::Testing#load_temporary}, which loads a Ruby file, defining
|
48
|
+
functions but only for the scope of your test. This allows your test to coexist
|
49
|
+
with tests for other functions, even functions with the same name from a
|
50
|
+
different Ruby file.
|
52
51
|
|
53
52
|
```ruby
|
54
53
|
require "minitest/autorun"
|
@@ -91,8 +90,8 @@ includes helper methods that you can use to create simple requests for many
|
|
91
90
|
basic cases.
|
92
91
|
|
93
92
|
When you have constructed an input request, use
|
94
|
-
|
95
|
-
|
93
|
+
{FunctionsFramework::Testing#call_http} to call a named function, passing the
|
94
|
+
request object. This method returns a
|
96
95
|
[Rack::Response](https://rubydoc.info/gems/rack/Rack/Response) that you can
|
97
96
|
assert against.
|
98
97
|
|
@@ -142,8 +141,7 @@ end
|
|
142
141
|
|
143
142
|
Testing a CloudEvent function works similarly. The `Testing` module provides
|
144
143
|
methods to help construct example CloudEvent objects, which can then be passed
|
145
|
-
to the method
|
146
|
-
[call_event](https://rubydoc.info/gems/functions_framework/FunctionsFramework/Testing#call_event-instance_method).
|
144
|
+
to the method {FunctionsFramework::Testing#call_event}.
|
147
145
|
|
148
146
|
Unlike HTTP functions, event functions do not have a return value. Instead, you
|
149
147
|
will need to test side effects. A common approach is to test logs by capturing
|
@@ -167,3 +165,53 @@ class MyTest < Minitest::Test
|
|
167
165
|
end
|
168
166
|
end
|
169
167
|
```
|
168
|
+
|
169
|
+
## Testing startup tasks
|
170
|
+
|
171
|
+
When a functions server is starting up, it calls startup tasks automatically.
|
172
|
+
In the testing environment, when you call a function using the
|
173
|
+
{FunctionsFramework::Testing#call_http} or
|
174
|
+
{FunctionsFramework::Testing#call_event} methods, the testing environment will
|
175
|
+
also automatically execute any startup tasks for you.
|
176
|
+
|
177
|
+
You can also call startup tasks explicitly to test them in isolation, using the
|
178
|
+
{FunctionsFramework::Testing#run_startup_tasks} method. Pass the name of a
|
179
|
+
function, and the testing module will execute all defined startup blocks, in
|
180
|
+
order, as if the server were preparing that function for execution.
|
181
|
+
{FunctionsFramework::Testing#run_startup_tasks} returns the resulting globals
|
182
|
+
as a hash, so you can assert against its contents.
|
183
|
+
|
184
|
+
If you use {FunctionsFramework::Testing#run_startup_tasks} to run the startup
|
185
|
+
tasks explicitly, they will not be run again when you call the function itself
|
186
|
+
using {FunctionsFramework::Testing#call_http} or
|
187
|
+
{FunctionsFramework::Testing#call_event}. However, if startup tasks have
|
188
|
+
already been run implicitly by {FunctionsFramework::Testing#call_http} or
|
189
|
+
{FunctionsFramework::Testing#call_event}, then attempting to run them again
|
190
|
+
explicitly by calling {FunctionsFramework::Testing#run_startup_tasks} will
|
191
|
+
result in an exception.
|
192
|
+
|
193
|
+
There is currently no way to run a single startup block in isolation. If you
|
194
|
+
have multiple startup blocks defined, they are always executed together.
|
195
|
+
|
196
|
+
Following is an example test that runs startup tasks explicitly and asserts
|
197
|
+
against the effect on the globals.
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
require "minitest/autorun"
|
201
|
+
require "functions_framework/testing"
|
202
|
+
|
203
|
+
class MyTest < Minitest::Test
|
204
|
+
include FunctionsFramework::Testing
|
205
|
+
|
206
|
+
def test_startup_tasks
|
207
|
+
load_temporary "app.rb" do
|
208
|
+
globals = run_startup_tasks "my_function"
|
209
|
+
assert_equal "foo", globals[:my_global]
|
210
|
+
|
211
|
+
request = make_get_request "https://example.com/foo"
|
212
|
+
response = call_http "my_function", request
|
213
|
+
assert_equal 200, response.status
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
```
|
data/docs/writing-functions.md
CHANGED
@@ -110,9 +110,8 @@ It is easy to connect an HTTP function to a Sinatra app. First, declare the
|
|
110
110
|
dependency on Sinatra in your `Gemfile`:
|
111
111
|
|
112
112
|
```ruby
|
113
|
-
# Gemfile
|
114
113
|
source "https://rubygems.org"
|
115
|
-
gem "functions_framework", "~> 0.
|
114
|
+
gem "functions_framework", "~> 0.7"
|
116
115
|
gem "sinatra", "~> 2.0"
|
117
116
|
```
|
118
117
|
|
@@ -158,9 +157,9 @@ end
|
|
158
157
|
```
|
159
158
|
|
160
159
|
The event parameter will be either a
|
161
|
-
[CloudEvents V0.3 Event](https://
|
160
|
+
[CloudEvents V0.3 Event](https://cloudevents.github.io/sdk-ruby/latest/CloudEvents/Event/V0)
|
162
161
|
object ([see spec](https://github.com/cloudevents/spec/blob/v0.3/spec.md)) or a
|
163
|
-
[CloudEvents V1.0 Event](https://
|
162
|
+
[CloudEvents V1.0 Event](https://cloudevents.github.io/sdk-ruby/latest/CloudEvents/Event/V1)
|
164
163
|
object ([see spec](https://github.com/cloudevents/spec/blob/v1.0/spec.md)).
|
165
164
|
|
166
165
|
Some Google Cloud services send events in a legacy event format that was defined
|
@@ -198,6 +197,192 @@ FunctionsFramework.http "error_reporter" do |request|
|
|
198
197
|
end
|
199
198
|
```
|
200
199
|
|
200
|
+
## The runtime environment
|
201
|
+
|
202
|
+
A serverless environment may be somewhat different from server-based runtime
|
203
|
+
environments you might be used to. Serverless runtimes often provide a simpler
|
204
|
+
programming model, transparent scaling, and cost savings, but they do so by
|
205
|
+
controlling how your code is managed and executed. The Functions Framework is
|
206
|
+
designed around a "functions-as-a-service" (FaaS) paradigm, which runs
|
207
|
+
self-contained stateless functions that have an input and a return value. It's
|
208
|
+
important to understand what that means for your Ruby code in order to get the
|
209
|
+
most out of a cloud serverless product.
|
210
|
+
|
211
|
+
For example, multithreading is a core element of the Functions Framework. When
|
212
|
+
you write functions, you should assume that multiple executions may be taking
|
213
|
+
place concurrently in different threads, and thus you should avoid operations
|
214
|
+
that can cause concurrency issues or race conditions. The easiest way to do
|
215
|
+
this is to make your functions self-contained and stateless. Avoid global
|
216
|
+
variables and don't share mutable data between different function executions.
|
217
|
+
|
218
|
+
Additionally, a serverless runtime may throttle the CPU whenever no actual
|
219
|
+
function executions are taking place. This lets it reduce the CPU resources
|
220
|
+
used (and therefore the cost to you), while keeping your application warmed up
|
221
|
+
and ready to respond to new requests quickly. An important implication, though,
|
222
|
+
is that you should avoid starting up background threads or processes. They may
|
223
|
+
not get any CPU time during periods when your Ruby application is not actually
|
224
|
+
executing a function.
|
225
|
+
|
226
|
+
In the sections below, we'll discuss a few techniques and features of the
|
227
|
+
Functions Framework to help you write Ruby code that fits well into a
|
228
|
+
serverless paradigm.
|
229
|
+
|
230
|
+
### Startup tasks
|
231
|
+
|
232
|
+
It is sometimes useful to perform one-time initialization that applies to many
|
233
|
+
function executions, for example to warm up caches, perform precomputation, or
|
234
|
+
establish shared remote connections. To run code during initialization, use
|
235
|
+
{FunctionsFramework.on_startup} to define a _startup task_.
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
require "functions_framework"
|
239
|
+
|
240
|
+
FunctionsFramework.on_startup do |function|
|
241
|
+
# Perform initialization here.
|
242
|
+
require "my_cache"
|
243
|
+
MyCache.warmup
|
244
|
+
end
|
245
|
+
|
246
|
+
FunctionsFramework.http "hello" do |request|
|
247
|
+
# Initialization will be done by the time a normal function is called.
|
248
|
+
end
|
249
|
+
```
|
250
|
+
|
251
|
+
Startup tasks are run once per Ruby instance, before the framework starts
|
252
|
+
receiving requests and executing functions. You can define multiple startup
|
253
|
+
tasks, and they will run in order, and are guaranteed to complete before any
|
254
|
+
function is executed.
|
255
|
+
|
256
|
+
The block is optionally passed the {FunctionsFramework::Function} representing
|
257
|
+
the function that will be run. You code can, for example, perform different
|
258
|
+
initialization depending on the {FunctionsFramework::Function#name} or
|
259
|
+
{FunctionsFramework::Function#type}.
|
260
|
+
|
261
|
+
**In most cases, initialization code should live in an `on_startup` block
|
262
|
+
instead of at the "top level" of your Ruby file.** This is because some
|
263
|
+
serverless runtimes may load your Ruby code at build or deployment time (for
|
264
|
+
example, to verify that it properly defines the requested function), and this
|
265
|
+
will execute any code present at the top level of the Ruby file. If top-level
|
266
|
+
code is long-running or depends on runtime resources or environment variables,
|
267
|
+
this could cause the deployment to fail. By performing initialization in an
|
268
|
+
`on_startup` block instead, you ensure it will run only when an actual runtime
|
269
|
+
server is starting up, not at build/deployment time.
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
require "functions_framework"
|
273
|
+
|
274
|
+
# DO NOT perform initialization here because this could get run at build time.
|
275
|
+
# require "my_cache"
|
276
|
+
# MyCache.warmup
|
277
|
+
|
278
|
+
# Instead initialize in an on_startup block, which is executed only when a
|
279
|
+
# runtime server is starting up.
|
280
|
+
FunctionsFramework.on_startup do
|
281
|
+
# Perform initialization here.
|
282
|
+
require "my_cache"
|
283
|
+
MyCache.warmup
|
284
|
+
end
|
285
|
+
|
286
|
+
# ...
|
287
|
+
```
|
288
|
+
|
289
|
+
### The execution context and global data
|
290
|
+
|
291
|
+
When your function block executes, the _object context_ (i.e. `self`) is set to
|
292
|
+
an instance of {FunctionsFramework::Function::Callable}. Each function
|
293
|
+
invocation (including functions that might be running concurrently in separate
|
294
|
+
threads) runs within a different instance, to help you avoid having functions
|
295
|
+
interfere with each other.
|
296
|
+
|
297
|
+
The object context also defines a few methods that may be useful when writing
|
298
|
+
your function.
|
299
|
+
|
300
|
+
First, you can obtain the logger by calling the
|
301
|
+
{FunctionsFramework::Function::Callable#logger} convenience method. This is
|
302
|
+
the same logger that is provided by the HTTP request object or by the
|
303
|
+
{FunctionsFramework.logger} global method.
|
304
|
+
|
305
|
+
Second, you can access global shared data by passing a key to
|
306
|
+
{FunctionsFramework::Function::Callable#global}. _Global shared data_ is a set
|
307
|
+
of key-value pairs that are available to every function invocation. By default,
|
308
|
+
two keys are available to all functions:
|
309
|
+
|
310
|
+
* `:function_name` whose String value is the name of the running function.
|
311
|
+
* `:function_type` whose value is either `:http` or `:cloud_event` depending
|
312
|
+
on the type of the running function.
|
313
|
+
|
314
|
+
Following is a simple example using the `logger` and `global` methods of the
|
315
|
+
context object:
|
316
|
+
|
317
|
+
```ruby
|
318
|
+
require "functions_framework"
|
319
|
+
|
320
|
+
FunctionsFramework.cloud_event "hello" do |event|
|
321
|
+
logger.info "Now running the function called #{global(:function_name)}"
|
322
|
+
end
|
323
|
+
```
|
324
|
+
|
325
|
+
To avoid concurrency issues, global shared data is immutable when executing a
|
326
|
+
function. You cannot add or delete keys or change the value of existing keys.
|
327
|
+
However, the global data is settable during startup tasks, because startup
|
328
|
+
tasks never run concurrently. You can use this feature to initialize shared
|
329
|
+
resources, as described below.
|
330
|
+
|
331
|
+
Using the global data mechanism is generally preferred over actual Ruby global
|
332
|
+
variables, because the Functions Framework can help you avoid concurrent edits.
|
333
|
+
Additionally, the framework will isolate the sets of global data associated
|
334
|
+
with different sets of functions, which lets you test functions in isolation
|
335
|
+
without the tests interfering with one another by writing to global variables.
|
336
|
+
|
337
|
+
### Sharing resources
|
338
|
+
|
339
|
+
Although functions should generally be self-contained and stateless, it is
|
340
|
+
sometimes useful to share certain kinds of resources across multiple function
|
341
|
+
invocations that run on the same Ruby instance. For example, you might
|
342
|
+
establish a single connection to a remote database or other service, and share
|
343
|
+
it across function invocations to avoid incurring the overhead of
|
344
|
+
re-establishing it for every function invocation.
|
345
|
+
|
346
|
+
The best practice for sharing a resource across function invocations is to
|
347
|
+
initialize it in a {FunctionsFramework.on_startup} block, and reference it from
|
348
|
+
global shared data. (As discussed above, prefer to initialize shared resources
|
349
|
+
in a startup task rather than at the top level of a Ruby file, and prefer using
|
350
|
+
the Functions Framework's global data mechanism rather than Ruby's global
|
351
|
+
variables.)
|
352
|
+
|
353
|
+
Here is a simple example:
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
require "functions_framework"
|
357
|
+
|
358
|
+
# Use an on_startup block to initialize a shared client and store it in
|
359
|
+
# the global shared data.
|
360
|
+
FunctionsFramework.on_startup do
|
361
|
+
require "google/cloud/storage"
|
362
|
+
set_global :storage_client, Google::Cloud::Storage.new
|
363
|
+
end
|
364
|
+
|
365
|
+
# The shared storage_client can be accessed by all function invocations
|
366
|
+
# via the global shared data.
|
367
|
+
FunctionsFramework.http "storage_example" do |request|
|
368
|
+
bucket = global(:storage_client).bucket "my-bucket"
|
369
|
+
file = bucket.file "path/to/my-file.txt"
|
370
|
+
file.download.to_s
|
371
|
+
end
|
372
|
+
```
|
373
|
+
|
374
|
+
Importantly, if you do share a resource across function invocations, make sure
|
375
|
+
the resource is thread-safe, so that separate functions running concurrently in
|
376
|
+
different threads can access them safely. The API clients provided by Google,
|
377
|
+
for example, are thread-safe and can be used concurrently.
|
378
|
+
|
379
|
+
Also of note: There is no guaranteed cleanup hook. The Functions Framework does
|
380
|
+
not provide a way to register a cleanup task, and we recommend against using
|
381
|
+
resources that require explicit "cleanup". This is because serverless runtimes
|
382
|
+
may perform CPU throttling, and therefore there may not be an opportunity for
|
383
|
+
cleanup tasks to run. (For example, you could register a `Kernel.at_exit` task,
|
384
|
+
but the Ruby VM may still terminate without calling it.)
|
385
|
+
|
201
386
|
## Structuring a project
|
202
387
|
|
203
388
|
A Functions Framework based "project" or "application" is a typical Ruby
|
@@ -207,15 +392,16 @@ needed by the function. It must include at least one Ruby source file that
|
|
207
392
|
defines functions, and can also include additional Ruby files defining classes
|
208
393
|
and methods that assist in the function implementation.
|
209
394
|
|
210
|
-
|
211
|
-
|
212
|
-
|
395
|
+
By convention, the "main" Ruby file that defines functions should be called
|
396
|
+
`app.rb` and be located at the root of the project. The path to this file is
|
397
|
+
sometimes known as the **function source**. The Functions Framework allows you
|
398
|
+
to specify an arbitrary source, but some hosting environments (such as Google
|
399
|
+
Cloud Functions) require it to be `./app.rb`.
|
213
400
|
|
214
|
-
|
215
|
-
|
216
|
-
cases.
|
401
|
+
A source file can define any number of functions (with distinct names). Each of
|
402
|
+
the names is known as a **function target**.
|
217
403
|
|
218
|
-
|
404
|
+
Following is a typical layout for a Functions Framework based project.
|
219
405
|
|
220
406
|
```
|
221
407
|
(project directory)
|
@@ -236,13 +422,16 @@ A simple project might look like this:
|
|
236
422
|
```ruby
|
237
423
|
# Gemfile
|
238
424
|
source "https://rubygems.org"
|
239
|
-
gem "functions_framework", "~> 0.
|
425
|
+
gem "functions_framework", "~> 0.7"
|
240
426
|
```
|
241
427
|
|
242
428
|
```ruby
|
243
429
|
# app.rb
|
244
430
|
require "functions_framework"
|
245
|
-
|
431
|
+
|
432
|
+
FunctionsFramework.on_startup do
|
433
|
+
require_relative "lib/hello"
|
434
|
+
end
|
246
435
|
|
247
436
|
FunctionsFramework.http "hello" do |request|
|
248
437
|
Hello.new(request).build_response
|