functions_framework 0.5.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/README.md +11 -11
- data/bin/functions-framework +4 -1
- data/bin/functions-framework-ruby +1 -1
- data/docs/deploying-functions.md +27 -22
- data/docs/overview.md +6 -6
- data/docs/testing-functions.md +59 -11
- data/docs/writing-functions.md +205 -16
- data/lib/functions_framework.rb +32 -5
- data/lib/functions_framework/cli.rb +97 -22
- data/lib/functions_framework/function.rb +142 -48
- data/lib/functions_framework/registry.rb +35 -12
- data/lib/functions_framework/server.rb +20 -15
- data/lib/functions_framework/testing.rb +103 -16
- data/lib/functions_framework/version.rb +1 -1
- metadata +10 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6d562086a839aaf78661b9417d3b931df10c74c98cb07b1710d40d6294b0490
|
4
|
+
data.tar.gz: fd070c121ce3e0c1bab7f1a738178a37c72c5d25c7726d3f3540e687d01bbe25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 478554bfc8b8bafb2efd5bb880b8e053e2442a878bcd1363a4c6ddeab77a811b49530b84f467009b01f2299b70105f17a8a9840d9b35ebbec94359ca14d7114c
|
7
|
+
data.tar.gz: 6873da52a257f542cb782fde59ba7da7f96042f4b69eaf470ea23d81cdb8479e8ff2791a6fbd0fd36ec166039f9718b7ce1b14632c690b44bea2833e280835b5
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,39 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### v0.7.1 / 2021-01-26
|
4
|
+
|
5
|
+
* DOCS: Fixed several errors in the writing-functions doc samples
|
6
|
+
* DOCS: Updated documentation to note public release of GCF support
|
7
|
+
|
8
|
+
### v0.7.0 / 2020-09-25
|
9
|
+
|
10
|
+
* Now requires Ruby 2.5 or later.
|
11
|
+
* BREAKING CHANGE: Renamed "context" hash to "globals" and made it read-only for normal functions.
|
12
|
+
* BREAKING CHANGE: Server config is no longer passed to startup blocks.
|
13
|
+
* ADDED: Provided a "logger" convenience method in the context object.
|
14
|
+
* ADDED: Globals can be set from startup blocks, which is useful for initializing shared resources.
|
15
|
+
* ADDED: Support for testing startup tasks in the Testing module.
|
16
|
+
* ADDED: Support for controlling logging in the Testing module.
|
17
|
+
* FIXED: Fixed crash introduced in 0.6.0 when a block didn't declare an expected argument.
|
18
|
+
* FIXED: Better support for running concurrent tests.
|
19
|
+
* DOCS: Expanded documentation on initialization, execution context, and shared resources.
|
20
|
+
* DEPRECATED: The functions-framework executable is deprecated. Use functions-framework-ruby instead.
|
21
|
+
|
22
|
+
### v0.6.0 / 2020-09-17
|
23
|
+
|
24
|
+
* ADDED: You can use the --version flag to print the framework version
|
25
|
+
* ADDED: You can use the --verify flag to verify that a given function is defined
|
26
|
+
* ADDED: You can now define blocks that are executed at server startup
|
27
|
+
|
28
|
+
### v0.5.2 / 2020-09-06
|
29
|
+
|
30
|
+
* FIXED: Use global $stderr rather than STDERR for logger
|
31
|
+
* DOCS: Fix instructions for deployment to Google Cloud Functions
|
32
|
+
|
33
|
+
### v0.5.1 / 2020-07-20
|
34
|
+
|
35
|
+
* Updated some documentation links. No functional changes.
|
36
|
+
|
3
37
|
### v0.5.0 / 2020-07-09
|
4
38
|
|
5
39
|
* Removed embedded CloudEvents classes and added the official CloudEvents SDK as a dependency. A `FunctionsFramework::CloudEvents` alias provides backward compatibility.
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
# Functions Framework [![Documentation](https://img.shields.io/badge/docs-FunctionsFramework-red.svg)](https://
|
1
|
+
# Functions Framework for Ruby [![Documentation](https://img.shields.io/badge/docs-FunctionsFramework-red.svg)](https://googlecloudplatform.github.io/functions-framework-ruby/latest) [![Gem Version](https://badge.fury.io/rb/functions_framework.svg)](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) *(public preview)*
|
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
@@ -31,12 +31,8 @@ Functions Framework is designed especially for functions that can be hosted on
|
|
31
31
|
Cloud Functions.
|
32
32
|
|
33
33
|
You can run Ruby functions on Google Cloud Functions by selecting the `ruby26`
|
34
|
-
runtime
|
35
|
-
|
36
|
-
|
37
|
-
> **Note:** Ruby support on Cloud Functions is currently in limited preview.
|
38
|
-
> It is not yet suitable for production workloads, and support is best-effort
|
39
|
-
> only. Access is currently limited to selected early-access users.
|
34
|
+
runtime or `ruby27` runtime to use a recent release of Ruby 2.6 or Ruby 2.7.
|
35
|
+
Support for Ruby 3.0 is forthcoming.
|
40
36
|
|
41
37
|
### Deploying and updating your function
|
42
38
|
|
@@ -46,30 +42,39 @@ is to `bundle install` or `bundle update` and run your local tests prior to
|
|
46
42
|
deploying. Cloud Functions will not accept your function unless an up-to-date
|
47
43
|
`Gemfile.lock` is present.
|
48
44
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
45
|
+
Also, make sure your source file (which defines your function) is called
|
46
|
+
`app.rb`. The Functions Framework lets you choose a function source file, but
|
47
|
+
Cloud Functions currently requires you to use `app.rb`.
|
48
|
+
|
49
|
+
Decide _which_ function in the source file to invoke, that is, the name that you
|
50
|
+
used when writing the function. This is called the **target**. (Note that if you
|
51
|
+
did not specify a name for the function, it defaults to the name `function`.)
|
52
|
+
|
53
|
+
Choose a Cloud Functions **name** for your function. The **name** identifies
|
54
|
+
this function deployment (e.g. in the cloud console) and is also part of the
|
55
|
+
function's default URL. (Note: the **name** and the **target** do not have to
|
56
|
+
be the same value.)
|
53
57
|
|
54
58
|
Then, issue the gcloud command to deploy:
|
55
59
|
|
56
60
|
```sh
|
57
|
-
gcloud functions deploy $YOUR_FUNCTION_NAME
|
58
|
-
|
59
|
-
|
61
|
+
gcloud functions deploy $YOUR_FUNCTION_NAME \
|
62
|
+
--project=$YOUR_PROJECT_ID \
|
63
|
+
--runtime=ruby27 \
|
64
|
+
--trigger-http \
|
65
|
+
--entry-point=$YOUR_FUNCTION_TARGET
|
60
66
|
```
|
61
67
|
|
62
|
-
The
|
63
|
-
|
64
|
-
|
65
|
-
`gcloud config set project`.
|
68
|
+
The `--entry-point=` flag can be omitted if the **target** has the same value
|
69
|
+
as the **name**. Additionally, the `--project` flag can be omitted if you've
|
70
|
+
set your default project using `gcloud config set project`.
|
66
71
|
|
67
72
|
If your function handles events rather than HTTP requests, you'll need to
|
68
73
|
replace `--trigger-http` with a different trigger. For details, see the
|
69
74
|
[reference documentation](https://cloud.google.com/sdk/gcloud/reference/functions/deploy)
|
70
75
|
for `gcloud functions deploy`.
|
71
76
|
|
72
|
-
To update your deployment, just redeploy using the same function name
|
77
|
+
To update your deployment, just redeploy using the same function **name**.
|
73
78
|
|
74
79
|
### Configuring Cloud Functions deployments
|
75
80
|
|
@@ -81,7 +86,7 @@ and above, set `FUNCTION_LOGGING_LEVEL` to `WARN` when deploying:
|
|
81
86
|
|
82
87
|
```sh
|
83
88
|
gcloud functions deploy $YOUR_FUNCTION_NAME --project=$YOUR_PROJECT_ID \
|
84
|
-
--runtime=
|
89
|
+
--runtime=ruby27 --trigger-http --source=$YOUR_FUNCTION_SOURCE \
|
85
90
|
--entry-point=$YOUR_FUNCTION_TARGET \
|
86
91
|
--set-env-vars=FUNCTION_LOGGING_LEVEL=WARN
|
87
92
|
```
|
@@ -116,7 +121,7 @@ Dockerfile that you can use as a starting point. Feel free to adjust it to the
|
|
116
121
|
needs of your project:
|
117
122
|
|
118
123
|
```
|
119
|
-
FROM ruby:2.
|
124
|
+
FROM ruby:2.7
|
120
125
|
WORKDIR /app
|
121
126
|
COPY . .
|
122
127
|
RUN gem install --no-document bundler \
|
@@ -142,8 +147,8 @@ command may ask you for permission to enable the Cloud Build API for the project
|
|
142
147
|
if it isn't already enabled.
|
143
148
|
|
144
149
|
Because you provide your own Docker image when deploying to Cloud Run, you can
|
145
|
-
use any version of Ruby supported by the Functions Framework, from 2.
|
146
|
-
|
150
|
+
use any version of Ruby supported by the Functions Framework, from 2.5 through
|
151
|
+
3.0.
|
147
152
|
|
148
153
|
### Deploying an image to Cloud Run
|
149
154
|
|
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) *(public preview)*
|
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
@@ -53,7 +53,7 @@ require "functions_framework"
|
|
53
53
|
|
54
54
|
FunctionsFramework.http "request_info_example" do |request|
|
55
55
|
# Include some request info in the response body.
|
56
|
-
"Received #{request.
|
56
|
+
"Received #{request.request_method} from #{request.url}!\n"
|
57
57
|
end
|
58
58
|
```
|
59
59
|
|
@@ -68,7 +68,7 @@ require "functions_framework"
|
|
68
68
|
|
69
69
|
FunctionsFramework.http "logging_example" do |request|
|
70
70
|
# Log some request info.
|
71
|
-
request.logger.info "I received #{request.
|
71
|
+
request.logger.info "I received #{request.request_method} from #{request.url}!"
|
72
72
|
# A simple response body.
|
73
73
|
"ok"
|
74
74
|
end
|
@@ -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
|
@@ -257,7 +446,7 @@ class Hello
|
|
257
446
|
end
|
258
447
|
|
259
448
|
def build_response
|
260
|
-
"Received request: #{request.
|
449
|
+
"Received request: #{@request.request_method} #{@request.url}\n"
|
261
450
|
end
|
262
451
|
end
|
263
452
|
```
|