Questo contenuto non è disponibile nella lingua selezionata.
Chapter 12. Functions development reference guide
12.1. Developing Go functions Copia collegamentoCollegamento copiato negli appunti!
After you have created a Go function project, you can change the template files provided to add business logic to your function. This includes configuring function invocation and the returned headers and status codes.
OpenShift Serverless Functions with Go is a Technology Preview feature only. Technology Preview features are not supported with Red Hat production service level agreements (SLAs) and might not be functionally complete. Red Hat does not recommend using them in production. These features provide early access to upcoming product features, enabling customers to test functionality and provide feedback during the development process.
For more information about the support scope of Red Hat Technology Preview features, see Technology Preview Features Support Scope.
12.1.1. Go function template structure Copia collegamentoCollegamento copiato negli appunti!
When you create a Go function by using the Knative (kn) CLI, the project directory looks like a typical Go project. The only exception is the additional func.yaml configuration file, which is used for specifying the image.
Before you can develop functions, you must configure the function.
Go functions have few restrictions. The only requirements are that your project must be defined in a function module, and must export the function Handle().
Both http and event trigger functions have the same template structure:
fn
├── README.md
├── func.yaml
├── go.mod
├── go.sum
├── handle.go
└── handle_test.go
func.yaml-
The
func.yamlconfiguration file is used to decide the image name and registry. go.mod-
You can add any required dependencies to the
go.modfile, which can include additional local Go files. When the project is built for deployment, these dependencies are included in the resulting runtime container image.
$ go get gopkg.in/yaml.v2@v2.4.0
12.1.2. About invoking Go functions Copia collegamentoCollegamento copiato negli appunti!
When you use the Knative (kn) CLI to create a function project, you can generate a project that responds to CloudEvents or simple HTTP requests. Different methods call Go functions, depending on whether an HTTP request or a CloudEvent triggers them.
12.1.2.1. Functions triggered by an HTTP request Copia collegamentoCollegamento copiato negli appunti!
When a function receives an incoming HTTP request, the runtime calls it with a standard Go Context as the first parameter, followed by the http.ResponseWriter and http.Request parameters. You can use standard Go techniques to access the request and set the HTTP response.
The following example displays the HTTP response:
func Handle(ctx context.Context, res http.ResponseWriter, req *http.Request) {
// Read body
body, err := ioutil.ReadAll(req.Body)
defer req.Body.Close()
if err != nil {
http.Error(res, err.Error(), 500)
return
}
// Process body and function logic
// ...
}
12.1.2.2. Functions triggered by a cloud event Copia collegamentoCollegamento copiato negli appunti!
When an incoming cloud event is received, the event is invoked by the CloudEvents Go SDK. The invocation uses the Event type as a parameter.
You can use the Go Context as an optional parameter in the function contract, as shown in the list of supported function signatures:
The following example displays supported function signatures:
Handle()
Handle() error
Handle(context.Context)
Handle(context.Context) error
Handle(cloudevents.Event)
Handle(cloudevents.Event) error
Handle(context.Context, cloudevents.Event)
Handle(context.Context, cloudevents.Event) error
Handle(cloudevents.Event) *cloudevents.Event
Handle(cloudevents.Event) (*cloudevents.Event, error)
Handle(context.Context, cloudevents.Event) *cloudevents.Event
Handle(context.Context, cloudevents.Event) (*cloudevents.Event, error)
The following example displays CloudEvent trigger:
A cloud event is received which contains a JSON string in the data property:
{
"customerId": "0123456",
"productId": "6543210"
}
To access this data, a structure must be defined which maps properties in the cloud event data, and retrieves the data from the incoming event. The following example uses the Purchase structure:
type Purchase struct {
CustomerId string `json:"customerId"`
ProductId string `json:"productId"`
}
func Handle(ctx context.Context, event cloudevents.Event) (err error) {
purchase := &Purchase{}
if err = event.DataAs(purchase); err != nil {
fmt.Fprintf(os.Stderr, "failed to parse incoming CloudEvent %s\n", err)
return
}
// ...
}
A Go encoding/json package could be used to access the cloud event directly as JSON in the form of a bytes array:
func Handle(ctx context.Context, event cloudevents.Event) {
bytes, err := json.Marshal(event)
// ...
}
12.1.3. Go function return values Copia collegamentoCollegamento copiato negli appunti!
Functions triggered by HTTP requests can set the response directly. You can configure the function to do this by using the Go http.ResponseWriter parameter.
The following example displays HTTP response:
func Handle(ctx context.Context, res http.ResponseWriter, req *http.Request) {
// Set response
res.Header().Add("Content-Type", "text/plain")
res.Header().Add("Content-Length", "3")
res.WriteHeader(200)
_, err := fmt.Fprintf(res, "OK\n")
if err != nil {
fmt.Fprintf(os.Stderr, "error or response write: %v", err)
}
}
Functions triggered by a cloud event might return nothing, error, or CloudEvent to push events into the Knative Eventing system. In this case, you must set a unique ID, proper Source, and a Type for the cloud event. The data can be populated from a defined structure, or from a map.
The following example displays CloudEvent response:
func Handle(ctx context.Context, event cloudevents.Event) (resp *cloudevents.Event, err error) {
// ...
response := cloudevents.NewEvent()
response.SetID("example-uuid-32943bac6fea")
response.SetSource("purchase/getter")
response.SetType("purchase")
// Set the data from Purchase type
response.SetData(cloudevents.ApplicationJSON, Purchase{
CustomerId: custId,
ProductId: prodId,
})
// OR set the data directly from map
response.SetData(cloudevents.ApplicationJSON, map[string]string{"customerId": custId, "productId": prodId})
// Validate the response
resp = &response
if err = resp.Validate(); err != nil {
fmt.Printf("invalid event created. %v", err)
}
return
}
12.1.4. Testing Go functions Copia collegamentoCollegamento copiato negli appunti!
Go functions can be tested locally on your computer. In the default project that is created when you create a function by using kn func create, there is a handle_test.go file, which has some basic tests. These tests can be extended as needed.
Prerequisites
- You have installed the OpenShift Serverless Operator and Knative Serving on the cluster.
-
You have installed the Knative (
kn) CLI. -
You have created a function by using
kn func create.
Procedure
- Navigate to the test folder for your function.
Run the tests by running the following command:
$ go test
12.2. Developing Quarkus functions Copia collegamentoCollegamento copiato negli appunti!
After you have created a Quarkus function project, you can change the template files provided to add business logic to your function. This includes configuring function invocation and the returned headers and status codes.
12.2.1. Quarkus function template structure Copia collegamentoCollegamento copiato negli appunti!
When you create a Quarkus function by using the Knative (kn) CLI, the project directory looks similar to a typical Maven project. Additionally, the project has the func.yaml file, which is used for configuring the function.
Before you can develop functions, you must configure the function.
Both http and event trigger functions have the same template structure:
.
├── func.yaml
├── mvnw
├── mvnw.cmd
├── pom.xml
├── README.md
└── src
├── main
│ ├── java
│ │ └── functions
│ │ ├── Function.java
│ │ ├── Input.java
│ │ └── Output.java
│ └── resources
│ └── application.properties
└── test
└── java
└── functions
├── FunctionTest.java
└── NativeFunctionIT.java
func.yaml- Used to find the image name and registry.
pom.xml- The Project Object Model (POM) file has project configuration, such as information about dependencies. You can add additional dependencies by modifying this file.
Function.java-
The function project must contain a Java method annotated with
@Funq. You can place this method in theFunction.javaclass. functions- Contains simple test cases that can be used to test your function locally.
You get an output similar to the following example:
...
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.8.0</version>
<scope>test</scope>
</dependency>
</dependencies>
...
Dependencies are downloaded during the first compilation.
12.2.2. About invoking Quarkus functions Copia collegamentoCollegamento copiato negli appunti!
You can create a Quarkus project that responds to cloud events, or one that responds to simple HTTP requests. Cloud events in Knative are transported over HTTP as a POST request, so either function type can listen and respond to incoming HTTP requests.
When an incoming request is received, Quarkus functions are invoked with an instance of a permitted type.
The following table displays function invocation options:
| Invocation method | Data type contained in the instance | Example of data |
|---|---|---|
| HTTP POST request | JSON object in the body of the request |
|
| HTTP GET request | Data in the query string |
|
|
|
JSON object in the |
|
The following example shows a function that receives and processes the customerId and productId buy data that is listed in the earlier table:
You get an output similar to the following example:
public class Functions {
@Funq
public void processPurchase(Purchase purchase) {
// process the purchase
}
}
The corresponding Purchase JavaBean class that has the purchase data looks as follows:
You get an output similar to the following example:
public class Purchase {
private long customerId;
private long productId;
// getters and setters
}
12.2.2.1. Invocation examples Copia collegamentoCollegamento copiato negli appunti!
The following example code defines three functions named withBeans, withCloudEvent, and withBinary;
You get an output similar to the following example:
import io.quarkus.funqy.Funq;
import io.quarkus.funqy.knative.events.CloudEvent;
public class Input {
private String message;
// getters and setters
}
public class Output {
private String message;
// getters and setters
}
public class Functions {
@Funq
public Output withBeans(Input in) {
// function body
}
@Funq
public CloudEvent<Output> withCloudEvent(CloudEvent<Input> in) {
// function body
}
@Funq
public void withBinary(byte[] in) {
// function body
}
}
The withBeans function of the Functions class can be invoked by:
An HTTP POST request with a JSON body:
$ curl "http://localhost:8080/withBeans" -X POST \ -H "Content-Type: application/json" \ -d '{"message": "Hello there."}'An HTTP GET request with query parameters:
$ curl "http://localhost:8080/withBeans?message=Hello%20there." -X GETA
CloudEventobject in binary encoding:$ curl "http://localhost:8080/" -X POST \ -H "Content-Type: application/json" \ -H "Ce-SpecVersion: 1.0" \ -H "Ce-Type: withBeans" \ -H "Ce-Source: cURL" \ -H "Ce-Id: 42" \ -d '{"message": "Hello there."}'A
CloudEventobject in structured encoding:$ curl http://localhost:8080/ \ -H "Content-Type: application/cloudevents+json" \ -d '{ "data": {"message":"Hello there."}, "datacontenttype": "application/json", "id": "42", "source": "curl", "type": "withBeans", "specversion": "1.0"}'
The withCloudEvent function of the Functions class can be invoked by using a CloudEvent object, similarly to the withBeans function. However, unlike withBeans, withCloudEvent cannot be invoked with a plain HTTP request.
The withBinary function of the Functions class can be invoked by:
A
CloudEventobject in binary encoding:$ curl "http://localhost:8080/" -X POST \ -H "Content-Type: application/octet-stream" \ -H "Ce-SpecVersion: 1.0"\ -H "Ce-Type: withBinary" \ -H "Ce-Source: cURL" \ -H "Ce-Id: 42" \ --data-binary '@img.jpg'A
CloudEventobject in structured encoding:$ curl http://localhost:8080/ \ -H "Content-Type: application/cloudevents+json" \ -d "{ \"data_base64\": \"$(base64 --wrap=0 img.jpg)\", \"datacontenttype\": \"application/octet-stream\", \"id\": \"42\", \"source\": \"curl\", \"type\": \"withBinary\", \"specversion\": \"1.0\"}"
12.2.3. CloudEvent attributes Copia collegamentoCollegamento copiato negli appunti!
If you need to read or write the attributes of a CloudEvent, such as type or subject, you can use the CloudEvent<T> generic interface and the CloudEventBuilder builder. The <T> type parameter must be one of the permitted types.
In the following example, CloudEventBuilder is used to return success or failure of processing the purchase:
public class Functions {
private boolean _processPurchase(Purchase purchase) {
// do stuff
}
public CloudEvent<Void> processPurchase(CloudEvent<Purchase> purchaseEvent) {
System.out.println("subject is: " + purchaseEvent.subject());
if (!_processPurchase(purchaseEvent.data())) {
return CloudEventBuilder.create()
.type("purchase.error")
.build();
}
return CloudEventBuilder.create()
.type("purchase.success")
.build();
}
}
12.2.4. Quarkus function return values Copia collegamentoCollegamento copiato negli appunti!
Functions can return an instance of any type from the list of permitted types. Or, they can return the Uni<T> type, where the <T> type parameter can be of any type from the permitted types.
The Uni<T> type is useful if a function calls asynchronous APIs, because the returned object is serialized in the same format as the received object. For example:
- If a function receives an HTTP request, then the returned object is sent in the body of an HTTP response.
-
If a function receives a
CloudEventobject in binary encoding, then the returned object is sent in the data property of a binary-encodedCloudEventobject.
The following example shows a function that fetches a list of purchases:
public class Functions {
@Funq
public List<Purchase> getPurchasesByName(String name) {
// logic to retrieve purchases
}
}
- Invoking this function through an HTTP request produces an HTTP response that has a list of purchases in the body of the response.
-
Invoking this function through an incoming
CloudEventobject produces aCloudEventresponse with a list of purchases in thedataproperty.
12.2.4.1. Permitted types Copia collegamentoCollegamento copiato negli appunti!
The input and output of a function can be any of the void, String, or byte[] types. Additionally, they can be primitive types and their wrappers, for example, int and Integer. They can also be the following complex objects: Javabeans, maps, lists, arrays, and the special CloudEvents<T> type.
Maps, lists, arrays, the <T> type parameter of the CloudEvents<T> type, and attributes of Javabeans can only be of types listed here.
Similar to the following example:
public class Functions {
public List<Integer> getIds();
public Purchase[] getPurchasesByName(String name);
public String getNameById(int id);
public Map<String,Integer> getNameIdMapping();
public void processImage(byte[] img);
}
12.2.5. Testing Quarkus functions Copia collegamentoCollegamento copiato negli appunti!
You can test Quarkus functions locally on your computer. The default project that kn func create creates has a src/test/ directory with basic Maven tests. You can extend these tests as needed.
Prerequisites
- You have created a Quarkus function.
-
You have installed the Knative (
kn) CLI.
Procedure
- Navigate to the project folder for your function.
Run the Maven tests:
$ ./mvnw test
12.2.6. Overriding liveness and readiness probe values for Quarkus functions Copia collegamentoCollegamento copiato negli appunti!
You can override liveness and readiness probe values for your Quarkus functions. Configure these probes to define how the platform performs health checks on the function.
Prerequisites
- You have installed the OpenShift Serverless Operator and Knative Serving on the cluster.
-
You have installed the Knative (
kn) CLI. -
You have created a function by using
kn func create.
Procedure
Override the
/health/livenessand/health/readinesspaths with your own values. You can do this either by changing properties in the function source or by setting theQUARKUS_SMALLRYE_HEALTH_LIVENESS_PATHandQUARKUS_SMALLRYE_HEALTH_READINESS_PATHenvironment variables onfunc.yamlfile.To override the paths using the function source, update the path properties in the
src/main/resources/application.propertiesfile:quarkus.smallrye-health.root-path=/health quarkus.smallrye-health.liveness-path=alive quarkus.smallrye-health.readiness-path=readyquarkus.smallrye-health.root-path=/health-
The root path, which is automatically prepended to the
livenessandreadinesspaths. quarkus.smallrye-health.liveness-path=alive-
The liveness path, set to
/health/alivehere. quarkus.smallrye-health.readiness-path=ready-
The readiness path, set to
/health/readyhere.
To override the paths using environment variables, define the path variables in the
buildblock of thefunc.yamlfile:build: builder: s2i buildEnvs: - name: QUARKUS_SMALLRYE_HEALTH_LIVENESS_PATH value: alive1 - name: QUARKUS_SMALLRYE_HEALTH_READINESS_PATH value: ready2 value: alive-
The liveness path, set to
/health/alivehere. value: ready-
The readiness path, set to
/health/readyhere.
Add the new endpoints to the
func.yamlfile, so that they are properly bound to the container for the Knative service:deploy: healthEndpoints: liveness: /health/alive readiness: /health/ready
12.3. Developing Node.js functions Copia collegamentoCollegamento copiato negli appunti!
After you have created a Node.js function project, you can change the template files provided to add business logic to your function. This includes configuring function invocation and the returned headers and status codes.
12.3.1. Node.js function template structure Copia collegamentoCollegamento copiato negli appunti!
When you create a Node.js function by using the Knative (kn) CLI, the project directory looks like a typical Node.js project. The only exception is the additional func.yaml file, which is used to configure the function.
Before you can develop functions, you must configure the function.
Both http and event trigger functions have the same template structure:
.
├── func.yaml
├── index.js
├── package.json
├── README.md
└── test
├── integration.js
└── unit.js
func.yaml-
The
func.yamlconfiguration file is used to determine the image name and registry. index.js-
Your project must contain an
index.jsfile which exports a single function. package.json-
You are not restricted to the dependencies provided in the template
package.jsonfile. You can add additional dependencies as you would in any other Node.js project. test- Integration and unit test scripts are provided as part of the function template.
You get an output similar to the following example:
npm install --save opossum
When the project is built for deployment, these dependencies are included in the created runtime container image.
12.3.2. About invoking Node.js functions Copia collegamentoCollegamento copiato negli appunti!
When using the Knative (kn) CLI to create a function project, you can generate a project that responds to CloudEvents, or one that responds to simple HTTP requests. CloudEvents in Knative are transported over HTTP as a POST request, so both function types listen for and respond to incoming HTTP events.
Node.js functions can be invoked with a simple HTTP request. When an incoming request is received, functions are invoked with a context object as the first parameter.
12.3.2.1. Node.js context objects Copia collegamentoCollegamento copiato negli appunti!
Functions are invoked by providing a context object as the first parameter. This object provides access to the incoming HTTP request information.
This information includes the HTTP request method, any query strings or headers sent with the request, the HTTP version, and the request body. Incoming requests that contain a CloudEvent attach the incoming instance of the CloudEvent to the context object so that it can be accessed by using context.cloudevent.
12.3.2.1.1. Context object methods Copia collegamentoCollegamento copiato negli appunti!
The context object has a single method, cloudEventResponse(), that accepts a data value and returns a CloudEvent.
In a Knative system, if a function deployed as a service is invoked by an event broker sending a CloudEvent, the broker examines the response. If the response is a CloudEvent, this event is handled by the broker.
The following example displays the context object method:
// Expects to receive a CloudEvent with customer data
function handle(context, customer) {
// process the customer
const processed = handle(customer);
return context.cloudEventResponse(customer)
.source('/handle')
.type('fn.process.customer')
.response();
}
12.3.2.1.2. CloudEvent data Copia collegamentoCollegamento copiato negli appunti!
If the incoming request is a CloudEvent, any data associated with the CloudEvent is extracted from the event and provided as a second parameter. For example, if a CloudEvent is received that has a JSON string in its data property that is similar to the following:
{
"customerId": "0123456",
"productId": "6543210"
}
When invoked, the second parameter to the function, after the context object, will be a JavaScript object that has customerId and productId properties.
You get an output similar to the following example:
function handle(context, data)
The data parameter in this example is a JavaScript object that has the customerId and productId properties.
12.3.2.1.3. Arbitrary data Copia collegamentoCollegamento copiato negli appunti!
A function can receive any data, not just CloudEvents. For example, you might want to call a function by using POST with an arbitrary object in the body:
{
"id": "12345",
"contact": {
"title": "Mr.",
"firstname": "John",
"lastname": "Smith"
}
}
In this case, you can define the function as follows:
function handle(context, customer) {
return "Hello " + customer.contact.title + " " + customer.contact.lastname;
}
Supplying the contact object to the function would then return the following output:
Hello Mr. Smith
12.3.2.1.4. Supported data types Copia collegamentoCollegamento copiato negli appunti!
CloudEvents can contain various data types, including JSON, XML, plain text, and binary data. These data types are provided to the function in their dedicated formats:
- JSON Data: Provided as a JavaScript object.
- XML Data: Provided as an XML document.
- Plain Text: Provided as a string.
- Binary Data: Provided as a Buffer object.
12.3.2.1.5. Multiple data types in a function Copia collegamentoCollegamento copiato negli appunti!
Ensure your function can handle different data types by checking the Content-Type header and parsing the data. For example:
function handle(context, data) {
if (context.headers['content-type'] === 'application/json') {
// handle JSON data
} else if (context.headers['content-type'] === 'application/xml') {
// handle XML data
} else {
// handle other data types
}
}
12.3.3. Node.js function return values Copia collegamentoCollegamento copiato negli appunti!
Functions can return any valid JavaScript type or can have no return value. When a function has no return value specified, and no failure is indicated, the caller receives a 204 No Content response.
Functions can also return a CloudEvent or a Message object to push events into the Knative Eventing system. In this case, the developer is not required to understand or implement the CloudEvent messaging specification. Headers and other relevant information from the returned values are extracted and sent with the response.
You get an output similar to the following example:
function handle(context, customer) {
// process customer and return a new CloudEvent
return new CloudEvent({
source: 'customer.processor',
type: 'customer.processed'
})
}
12.3.3.1. Returning primitive types Copia collegamentoCollegamento copiato negli appunti!
Functions can return any valid JavaScript type, including primitives such as strings, numbers, and booleans:
You get an output similar to the following example:
function handle(context) {
return "This function Works!"
}
Calling this function returns the following string:
$ curl https://myfunction.example.com
This function Works!
You get an output similar to the following example:
function handle(context) {
let somenumber = 100
return { body: somenumber }
}
Calling this function returns the following number:
$ curl https://myfunction.example.com
100
You get an output similar to the following example:
function handle(context) {
let someboolean = false
return { body: someboolean }
}
Calling this function returns the following boolean:
$ curl https://myfunction.example.com
false
Returning primitives directly without wrapping them in an object results in a 204 No Content status code with an empty body:
You get an output similar to the following example:
function handle(context) {
let someboolean = false
return someboolean
}
Calling this function returns the following:
$ http :8080
HTTP/1.1 204 No Content
Connection: keep-alive
...
12.3.3.2. Returning headers Copia collegamentoCollegamento copiato negli appunti!
You can set a response header by adding a headers property to the return object. These headers are extracted and sent with the response to the caller.
You get an output similar to the following example:
function handle(context, customer) {
// process customer and return custom headers
// the response will be '204 No content'
return { headers: { customerid: customer.id } };
}
12.3.3.3. Returning status codes Copia collegamentoCollegamento copiato negli appunti!
You can set a status code that is returned to the caller by adding a statusCode property to the return object:
You get an output similar to the following example:
function handle(context, customer) {
// process customer
if (customer.restricted) {
return { statusCode: 451 }
}
}
Status codes can also be set for errors that are created and thrown by the function:
You get an output similar to the following example:
function handle(context, customer) {
// process customer
if (customer.restricted) {
const err = new Error(‘Unavailable for legal reasons’);
err.statusCode = 451;
throw err;
}
}
12.3.4. Testing Node.js functions Copia collegamentoCollegamento copiato negli appunti!
Node.js functions can be tested locally on your computer. In the default project that is created when you create a function by using kn func create, there is a test folder that has some simple unit and integration tests.
Prerequisites
- You have installed the OpenShift Serverless Operator and Knative Serving on the cluster.
-
You have installed the Knative (
kn) CLI. -
You have created a function by using
kn func create.
Procedure
- Navigate to the test folder for your function.
Run the tests:
$ npm test
12.3.5. Overriding liveness and readiness probe values for Node.js functions Copia collegamentoCollegamento copiato negli appunti!
You can override liveness and readiness probe values for your Node.js functions. You can configure health checks performed on the function.
Prerequisites
- You have installed the OpenShift Serverless Operator and Knative Serving on the cluster.
-
You have installed the Knative (
kn) CLI. -
You have created a function by using
kn func create.
Procedure
-
In your function code, create the
Functionobject, which implements the following interface:
export interface Function {
init?: () => any;
shutdown?: () => any;
liveness?: HealthCheck;
readiness?: HealthCheck;
logLevel?: LogLevel;
handle: CloudEventFunction | HTTPFunction;
}
init?: () ⇒ any- The initialization function, called before the server is started. This function is optional and should be synchronous.
shutdown?: () ⇒ any;- The shutdown function, called after the server is stopped. This function is optional and should be synchronous.
liveness?: HealthCheck;- The liveness function, called to check if the server is alive. This function is optional and should return 200/OK if the server is alive.
readiness?: HealthCheck;-
The readiness function, called to check if the server is ready to accept requests. This function is optional and should return
200/OKif the server is ready. handle: CloudEventFunction | HTTPFunction;- The function to handle HTTP requests.
For example, add the following code to the index.js file:
const Function = {
handle: (context, body) => {
// The function logic goes here
return 'function called'
},
liveness: () => {
process.stdout.write('In liveness\n');
return 'ok, alive';
},
readiness: () => {
process.stdout.write('In readiness\n');
return 'ok, ready';
}
};
Function.liveness.path = '/alive';
Function.readiness.path = '/ready';
module.exports = Function;
Function.liveness.path = '/alive';-
Custom
livenessendpoint. Function.readiness.path = '/ready';-
Custom
readinessendpoint.
As an alternative to Function.liveness.path and Function.readiness.path, you can specify custom endpoints by using the LIVENESS_URL and READINESS_URL environment variables:
run:
envs:
- name: LIVENESS_URL
value: /alive
- name: READINESS_URL
value: /ready
value: /alive-
The liveness path, set to
/alivehere. value: /ready-
The readiness path, set to
/readyhere.
-
Add the new endpoints to the
func.yamlfile, so that they are properly bound to the container for the Knative service:
deploy:
healthEndpoints:
liveness: /alive
readiness: /ready
12.3.6. Node.js context object reference Copia collegamentoCollegamento copiato negli appunti!
The context object has several properties that can be accessed by the function developer. Accessing these properties can give information about HTTP requests and write output to the cluster logs.
12.3.6.1. Log Copia collegamentoCollegamento copiato negli appunti!
Provides a logging object that can be used to write output to the cluster logs. The log adheres to the Pino logging API.
function handle(context) {
context.log.info(“Processing customer”);
}
You can access the function by using the kn func invoke command:
You get an output similar to the following example command:
$ kn func invoke --target 'http://example.function.com'
You get an output similar to the following example:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"Processing customer"}
You can change the log level to one of fatal, error, warn, info, debug, trace, or silent. To do that, change the value of logLevel by using one of these values to the environment variable FUNC_LOG_LEVEL by using the config command.
12.3.6.2. query Copia collegamentoCollegamento copiato negli appunti!
Returns the query string for the request, if any, as key-value pairs. These attributes are also found on the context object itself.
function handle(context) {
// Log the 'name' query parameter
context.log.info(context.query.name);
// Query parameters are also attached to the context
context.log.info(context.name);
}
You can access the function by using the kn func invoke command:
You get an output similar to the following example command:
$ kn func invoke --target 'http://example.com?name=tiger'
You get an output similar to the following example:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"tiger"}
12.3.6.3. body Copia collegamentoCollegamento copiato negli appunti!
Returns the request body if any. If the request body contains JSON code, this will be parsed so that the attributes are directly available.
function handle(context) {
// log the incoming request body's 'hello' parameter
context.log.info(context.body.hello);
}
You can access the function by using the curl command to invoke it:
You get an output similar to the following example command:
$ kn func invoke -d '{"Hello": "world"}'
You get an output similar to the following example:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"world"}
12.3.6.4. headers Copia collegamentoCollegamento copiato negli appunti!
Returns the HTTP request headers as an object.
function handle(context) {
context.log.info(context.headers["custom-header"]);
}
You can access the function by using the kn func invoke command:
You get an output similar to the following example command:
$ kn func invoke --target 'http://example.function.com'
You get an output similar to the following example:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"some-value"}
12.3.6.5. HTTP requests Copia collegamentoCollegamento copiato negli appunti!
- method
- Returns the HTTP request method as a string.
- httpVersion
- Returns the HTTP version as a string.
- httpVersionMajor
- Returns the HTTP major version number as a string.
- httpVersionMinor
- Returns the HTTP minor version number as a string.
12.4. Developing TypeScript functions Copia collegamentoCollegamento copiato negli appunti!
After you have created a TypeScript function project, you can change the template files provided to add business logic to your function. This includes configuring function invocation and the returned headers and status codes.
12.4.1. TypeScript function template structure Copia collegamentoCollegamento copiato negli appunti!
When you create a TypeScript function by using the Knative (kn) CLI, the project directory looks like a typical TypeScript project. The only exception is the additional func.yaml file, which is used for configuring the function.
Before you can develop functions, you must configure the function.
Both http and event trigger functions have the same template structure:
.
├── func.yaml
├── package.json
├── package-lock.json
├── README.md
├── src
│ └── index.ts
├── test
│ ├── integration.ts
│ └── unit.ts
└── tsconfig.json
func.yaml-
The
func.yamlconfiguration file is used to find the image name and registry. package.json-
You are not restricted to the dependencies provided in the template
package.jsonfile. You can add additional dependencies as you would in any other TypeScript project. index.ts-
Your project must contain an
src/index.jsfile that exports a function namedhandle. test- Integration and unit test scripts are provided as part of the function template.
The following displays how to add npm dependencies:
npm install --save opossum
When the project is built for deployment, these dependencies are included in the created runtime container image.
12.4.2. About invoking TypeScript functions Copia collegamentoCollegamento copiato negli appunti!
When you use the Knative (kn) CLI to create a function project, you can generate a project that responds to CloudEvents or simple HTTP requests. Knative sends CloudEvents over HTTP as POST requests, so both function types listen for and respond to incoming HTTP events.
You can call TypeScript functions with a simple HTTP request. When a function receives a request, the runtime calls it with a context object as the first parameter.
12.4.2.1. TypeScript context objects Copia collegamentoCollegamento copiato negli appunti!
To call a function, pass a context object as the first parameter. Access its properties to get information about the incoming HTTP request.
The following example displayes context object:
function handle(context:Context): string
This information includes the HTTP request method, any query strings or headers sent with the request, the HTTP version, and the request body. Incoming requests that contain a CloudEvent attach the incoming instance of the CloudEvent to the context object so that it can be accessed by using context.cloudevent.
12.4.2.1.1. Context object methods Copia collegamentoCollegamento copiato negli appunti!
The context object has a single method, cloudEventResponse(), that accepts a data value and returns a CloudEvent.
In a Knative system, if a function deployed as a service is invoked by an event broker sending a CloudEvent, the broker examines the response. If the response is a CloudEvent, this event is handled by the broker.
The following example displayes context object method:
// Expects to receive a CloudEvent with customer data
export function handle(context: Context, cloudevent?: CloudEvent): CloudEvent {
// process the customer
const customer = cloudevent.data;
const processed = processCustomer(customer);
return context.cloudEventResponse(customer)
.source('/customer/process')
.type('customer.processed')
.response();
}
12.4.2.1.2. Context types Copia collegamentoCollegamento copiato negli appunti!
The TypeScript type definition files export the following types for use in your functions.
The following example displayes exported type definitions:
// Invokable is the expeted Function signature for user functions
export interface Invokable {
(context: Context, cloudevent?: CloudEvent): any
}
// Logger can be used for structural logging to the console
export interface Logger {
debug: (msg: any) => void,
info: (msg: any) => void,
warn: (msg: any) => void,
error: (msg: any) => void,
fatal: (msg: any) => void,
trace: (msg: any) => void,
}
// Context represents the function invocation context, and provides
// access to the event itself as well as raw HTTP objects.
export interface Context {
log: Logger;
req: IncomingMessage;
query?: Record<string, any>;
body?: Record<string, any>|string;
method: string;
headers: IncomingHttpHeaders;
httpVersion: string;
httpVersionMajor: number;
httpVersionMinor: number;
cloudevent: CloudEvent;
cloudEventResponse(data: string|object): CloudEventResponse;
}
// CloudEventResponse is a convenience class used to create
// CloudEvents on function returns
export interface CloudEventResponse {
id(id: string): CloudEventResponse;
source(source: string): CloudEventResponse;
type(type: string): CloudEventResponse;
version(version: string): CloudEventResponse;
response(): CloudEvent;
}
12.4.2.1.3. CloudEvent data Copia collegamentoCollegamento copiato negli appunti!
If the incoming request is a CloudEvent, any data associated with the CloudEvent is extracted from the event and provided as a second parameter. For example, if a CloudEvent is received that has a JSON string in its data property that is similar to the following:
{
"customerId": "0123456",
"productId": "6543210"
}
When invoked, the second parameter to the function, after the context object, will be a JavaScript object that has customerId and productId properties.
You get an output similar to the following example signature:
function handle(context: Context, cloudevent?: CloudEvent): CloudEvent
The cloudevent parameter in this example is a JavaScript object that has the customerId and productId properties.
12.4.3. TypeScript function return values Copia collegamentoCollegamento copiato negli appunti!
Functions can return any valid JavaScript type or can have no return value. When a function has no return value specified, and no failure is indicated, the caller receives a 204 No Content response.
Functions can also return a CloudEvent or a Message object to push events into the Knative Eventing system. In this case, the developer is not required to understand or implement the CloudEvent messaging specification. Headers and other relevant information from the returned values are extracted and sent with the response.
You get an output similar to the following example:
export const handle: Invokable = function (
context: Context,
cloudevent?: CloudEvent
): Message {
// process customer and return a new CloudEvent
const customer = cloudevent.data;
return HTTP.binary(
new CloudEvent({
source: 'customer.processor',
type: 'customer.processed'
})
);
};
12.4.3.1. Returning headers Copia collegamentoCollegamento copiato negli appunti!
You can set a response header by adding a headers property to the return object. These headers are extracted and sent with the response to the caller.
The following example displayes response header:
export function handle(context: Context, cloudevent?: CloudEvent): Record<string, any> {
// process customer and return custom headers
const customer = cloudevent.data as Record<string, any>;
return { headers: { 'customer-id': customer.id } };
}
12.4.3.2. Returning status codes Copia collegamentoCollegamento copiato negli appunti!
You can set a status code that is returned to the caller by adding a statusCode property to the return object:
The following example displayes status code:
export function handle(context: Context, cloudevent?: CloudEvent): Record<string, any> {
// process customer
const customer = cloudevent.data as Record<string, any>;
if (customer.restricted) {
return {
statusCode: 451
}
}
// business logic, then
return {
statusCode: 240
}
}
Status codes can also be set for errors that are created and thrown by the function:
The following example displayes error status code:
export function handle(context: Context, cloudevent?: CloudEvent): Record<string, string> {
// process customer
const customer = cloudevent.data as Record<string, any>;
if (customer.restricted) {
const err = new Error(‘Unavailable for legal reasons’);
err.statusCode = 451;
throw err;
}
}
12.4.4. Testing TypeScript functions Copia collegamentoCollegamento copiato negli appunti!
TypeScript functions can be tested locally on your computer. In the default project that is created when you create a function by using kn func create, there is a test folder that has some simple unit and integration tests.
Prerequisites
- You have installed the OpenShift Serverless Operator and Knative Serving on the cluster.
-
You have installed the Knative (
kn) CLI. -
You have created a function by using
kn func create.
Procedure
If you have not previously run tests, install the dependencies first by running the following command:
$ npm install- Navigate to the test folder for your function.
Run the tests:
$ npm test
12.4.5. Overriding liveness and readiness probe values for TypeScript functions Copia collegamentoCollegamento copiato negli appunti!
You can override liveness and readiness probe values for your TypeScript functions. Use these probes to configure how the platform performs health checks on the function.
Prerequisites
- You have installed the OpenShift Serverless Operator and Knative Serving on the cluster.
-
You have installed the Knative (
kn) CLI. -
You have created a function by using
kn func create.
Procedure
In your function code, create the
Functionobject, which implements the following interface:export interface Function { init?: () => any; shutdown?: () => any; liveness?: HealthCheck; readiness?: HealthCheck; logLevel?: LogLevel; handle: CloudEventFunction | HTTPFunction; }init?: () ⇒ any;- The initialization function, called before the server is started. This function is optional and should be synchronous.
shutdown?: () ⇒ any; <2>- The shutdown function, called after the server is stopped. This function is optional and should be synchronous.
liveness?: HealthCheck;- The liveness function, called to check if the server is alive. This function is optional and should return 200/OK if the server is alive.
readiness?: HealthCheck;- The readiness function, called to check if the server is ready to accept requests. This function is optional and should return 200/OK if the server is ready.
handle: CloudEventFunction | HTTPFunction;- The function to handle HTTP requests.
For example, add the following code to the
index.jsfile:const Function = { handle: (context, body) => { // The function logic goes here return 'function called' }, liveness: () => { process.stdout.write('In liveness\n'); return 'ok, alive'; }, readiness: () => { process.stdout.write('In readiness\n'); return 'ok, ready'; } }; Function.liveness.path = '/alive'; Function.readiness.path = '/ready'; module.exports = Function;liveness: () ⇒ {-
Custom
livenessfunction. readiness: () ⇒ {-
Custom
readinessfunction. Function.liveness.path = '/alive';-
Custom
livenessendpoint. Function.readiness.path = '/ready';-
Custom
readinessendpoint.
As an alternative to
Function.liveness.pathandFunction.readiness.path, you can specify custom endpoints by using theLIVENESS_URLandREADINESS_URLenvironment variables:run: envs: - name: LIVENESS_URL value: /alive - name: READINESS_URL value: /readyvalue: /alive-
The liveness path, set to
/alivehere. value: /ready-
The readiness path, set to
/readyhere.
Add the new endpoints to the
func.yamlfile, so that they are properly bound to the container for the Knative service:deploy: healthEndpoints: liveness: /alive readiness: /ready
12.4.6. TypeScript context object reference Copia collegamentoCollegamento copiato negli appunti!
The context object has several properties that can be accessed by the function developer. Accessing these properties can give information about incoming HTTP requests and write output to the cluster logs.
12.4.6.1. log Copia collegamentoCollegamento copiato negli appunti!
Provides a logging object that can be used to write output to the cluster logs. The log adheres to the Pino logging API.
You get an output similar to the following log example:
export function handle(context: Context): string {
// log the incoming request body's 'hello' parameter
if (context.body) {
context.log.info((context.body as Record<string, string>).hello);
} else {
context.log.info('No data received');
}
return 'OK';
}
You can access the function by using the kn func invoke command:
You get an output similar to the following example command:
$ kn func invoke --target 'http://example.function.com'
You get an output similar to the following example:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"Processing customer"}
You can change the log level to one of fatal, error, warn, info, debug, trace, or silent. To do that, change the value of logLevel by assigning one of these values to the environment variable FUNC_LOG_LEVEL by using the config command.
12.4.6.2. query Copia collegamentoCollegamento copiato negli appunti!
Returns the query string for the request, if any, as key-value pairs. These attributes are also found on the context object itself.
You get an output similar to the following query example:
export function handle(context: Context): string {
// log the 'name' query parameter
if (context.query) {
context.log.info((context.query as Record<string, string>).name);
} else {
context.log.info('No data received');
}
return 'OK';
}
You can access the function by using the kn func invoke command:
You get an output similar to the following example command:
$ kn func invoke --target 'http://example.function.com' --data '{"name": "tiger"}'
You get an output similar to the following example:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"tiger"}
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"tiger"}
12.4.6.3. body Copia collegamentoCollegamento copiato negli appunti!
Returns the request body, if any. If the request body contains JSON code, this will be parsed so that the attributes are directly available.
You get an output similar to the following body example:
export function handle(context: Context): string {
// log the incoming request body's 'hello' parameter
if (context.body) {
context.log.info((context.body as Record<string, string>).hello);
} else {
context.log.info('No data received');
}
return 'OK';
}
You can access the function by using the kn func invoke command:
You get an output similar to the following example command:
$ kn func invoke --target 'http://example.function.com' --data '{"hello": "world"}'
You get an output similar to the following example:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"world"}
12.4.6.4. headers Copia collegamentoCollegamento copiato negli appunti!
Returns the HTTP request headers as an object.
You get an output similar to the following headers example:
export function handle(context: Context): string {
// log the incoming request body's 'hello' parameter
if (context.body) {
context.log.info((context.headers as Record<string, string>)['custom-header']);
} else {
context.log.info('No data received');
}
return 'OK';
}
You can access the function by using the curl command to call it:
You get an output similar to the following example command:
$ curl -H'x-custom-header: some-value’' http://example.function.com
You get an output similar to the following example:
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"some-value"}
12.4.6.5. HTTP requests Copia collegamentoCollegamento copiato negli appunti!
method- Returns the HTTP request method as a string.
httpVersion- Returns the HTTP version as a string.
httpVersionMajor- Returns the HTTP major version number as a string.
httpVersionMinor- Returns the HTTP minor version number as a string.
12.5. Developing Python functions Copia collegamentoCollegamento copiato negli appunti!
After you have created a Python function project, you can change the template files provided to add business logic to your function. This includes configuring function invocation and the returned headers and status codes.
12.5.1. Python function template structure Copia collegamentoCollegamento copiato negli appunti!
When you create a Python function by using the Knative (kn) CLI, the project directory looks similar to a typical Python project. Python functions have very few restrictions. The only requirements are that your project contains a func.py file that contains a main() function, and a func.yaml configuration file.
Before you can develop functions, you must configure the function.
Developers are not restricted to the dependencies provided in the template requirements.txt file. Additional dependencies can be added as they would be in any other Python project. When the project is built for deployment, these dependencies will be included in the created runtime container image.
Both http and event trigger functions have the same template structure:
Template structure
fn
├── func.py
├── func.yaml
├── requirements.txt
└── test_func.py
12.5.2. About invoking Python functions Copia collegamentoCollegamento copiato negli appunti!
You can call Python functions with a simple HTTP request. When a function receives a request, the runtime calls it with a context object as the first parameter.
The context object is a Python class with two attributes:
-
The
requestattribute is always present, and has the Flaskrequestobject. -
The second attribute,
cloud_event, is populated if the incoming request is aCloudEventobject.
Developers can access any CloudEvent data from the context object.
You get an output similar to the following example:
def main(context: Context):
"""
The context parameter contains the Flask request object and any
CloudEvent received with the request.
"""
print(f"Method: {context.request.method}")
print(f"Event data {context.cloud_event.data}")
# ... business logic here
12.5.3. Python function return values Copia collegamentoCollegamento copiato negli appunti!
Functions can return any value supported by Flask. This is because the invocation framework proxies these values directly to the Flask server.
You get an output similar to the following example:
def main(context: Context):
body = { "message": "Howdy!" }
headers = { "content-type": "application/json" }
return body, 200, headers
Functions can set both headers and response codes as secondary and tertiary response values from function invocation.
12.5.3.1. Returning CloudEvents Copia collegamentoCollegamento copiato negli appunti!
Developers can use the @event decorator to tell the invoker that the function return value must be converted to a CloudEvent before sending the response.
You get an output similar to the following example:
@event("event_source"="/my/function", "event_type"="my.type")
def main(context):
# business logic here
data = do_something()
# more data processing
return data
This example sends a CloudEvent as the response value, with a type of "my.type" and a source of "/my/function". The CloudEvent data property is set to the returned data variable. The event_source and event_type decorator attributes are both optional.
12.5.4. Testing Python functions Copia collegamentoCollegamento copiato negli appunti!
You can test Python functions locally on your computer. The default project has a test_func.py file, which provides a simple unit test for functions.
The default test framework for Python functions is unittest. You can use a different test framework if you prefer.
Prerequisites
To run Python functions tests locally, you must install the required dependencies:
$ pip install -r requirements.txt
Procedure
-
Navigate to the folder for your function that has the
test_func.pyfile. Run the tests:
$ python3 test_func.py