mirror of
https://github.com/minio/docs.git
synced 2025-04-19 21:02:14 +03:00
Closes #898 - Adds a short section on synchronous vs asynchronous bucket notifications - Adds a note on specific configs which now return redacted data on `mc admin config get` - Found a few new webhook lambda envvars to add
378 lines
14 KiB
ReStructuredText
378 lines
14 KiB
ReStructuredText
.. _developers-object-lambda:
|
|
|
|
=================================
|
|
Transforms with Object Lambda
|
|
=================================
|
|
|
|
.. default-domain:: minio
|
|
|
|
.. contents:: Table of Contents
|
|
:local:
|
|
:depth: 2
|
|
|
|
MinIO's Object Lambda enables developers to programmatically transform objects on demand.
|
|
You can transform objects as needed for your use case, such as redacting personally identifiable information (PII), enriching data with information from other sources, or converting between formats.
|
|
|
|
Overview
|
|
--------
|
|
|
|
An :ref:`Object Lambda handler <minio_object_lambda_handers>` is a small code module that transforms the contents of an object and returns the results.
|
|
Like :s3-docs:`Amazon S3 Object Lambda functions <transforming-objects.html>`, you trigger a MinIO Object Lambda handler function with a GET request from an application.
|
|
The handler retrieves the requested object from MinIO, transforms it, and returns the modified data back to MinIO to send to the original application.
|
|
The original object remains unchanged.
|
|
|
|
Each handler is an independent process, and multiple handlers can transform the same data.
|
|
This allows you to use the same object for different purposes without maintaining different versions of the original.
|
|
|
|
.. _minio_object_lambda_handers:
|
|
|
|
Object Lambda Handlers
|
|
----------------------
|
|
|
|
You can write a handler function in any language capable of sending and receiving HTTP requests.
|
|
It must be able to:
|
|
|
|
- Listen for an HTTP POST request.
|
|
- Retrieve the original object using a URL.
|
|
- Return the transformed contents and authorization tokens.
|
|
|
|
Create a Function
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
A handler function should perform the following steps:
|
|
|
|
#. Extract the object details from the incoming POST request.
|
|
|
|
The ``getObjectContext`` property of the JSON request payload contains details about the original object.
|
|
To construct the response, you need the following values:
|
|
|
|
.. list-table::
|
|
:widths: 25 75
|
|
:header-rows: 1
|
|
|
|
* - Value
|
|
- Description
|
|
|
|
* - ``inputS3Url``
|
|
- A `presigned URL <https://min.io/docs/minio/linux/developers/go/API.html#presigned-operations>`__ for the original object.
|
|
The calling application generates the URL and sends it in the original request.
|
|
This allows the handler to access the original object without the MinIO credentials usually required.
|
|
The URL is valid for one hour.
|
|
|
|
* - ``outputRoute``
|
|
- A token that allows MinIO to validate the destination for the transformed object.
|
|
Return this value with the response in an ``x-amz-request-route`` header.
|
|
|
|
* - ``outputToken``
|
|
- A token that allows MinIO to validate the response.
|
|
Return this value in the response in an ``x-amz-request-token`` header.
|
|
|
|
#. Retrieve the original object from MinIO.
|
|
|
|
Use the presigned URL to retrieve the object from the MinIO deployment.
|
|
The contents of the object are in the body of the response.
|
|
|
|
#. Transform the object as desired.
|
|
|
|
Perform any operations needed to generate a transformed object.
|
|
Since the calling application is waiting for a response, you may wish to avoid potentially long running operations.
|
|
|
|
#. Construct a response containing the following information:
|
|
|
|
- The transformed object contents.
|
|
- An ``x-amz-request-route`` header with the ``outputRoute`` token.
|
|
- An ``x-amz-request-token`` header with the ``outputToken`` token.
|
|
|
|
#. Return the response back to Object Lambda.
|
|
|
|
MinIO validates the response and sends the transformed data back to the original calling application.
|
|
|
|
|
|
.. admonition:: Response headers
|
|
:class: note
|
|
|
|
Handlers **must** include the ``outputRoute`` and ``outputToken`` values in the appropriate response headers.
|
|
This allows MinIO to correctly validate the response from the handler.
|
|
|
|
|
|
Register the Handler
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
To enable MinIO to call the handler, register the handler function as a webhook with the following :ref:`MinIO server Object Lambda environment variables <minio-server-envvar-object-lambda-webhook>`:
|
|
|
|
:envvar:`MINIO_LAMBDA_WEBHOOK_ENABLE_functionname <MINIO_LAMBDA_WEBHOOK_ENABLE>`
|
|
Enable or disable Object Lambda for a handler function.
|
|
For multiple handlers, set this environment variable for each function name.
|
|
|
|
:envvar:`MINIO_LAMBDA_WEBHOOK_ENDPOINT_functionname <MINIO_LAMBDA_WEBHOOK_ENDPOINT>`
|
|
Register an endpoint for a handler function.
|
|
For multiple handlers, set this environment variable for each function endpoint.
|
|
|
|
MinIO also supports the following environment variables for authenticated webhook endpoints:
|
|
|
|
:envvar:`MINIO_LAMBDA_WEBHOOK_AUTH_TOKEN_functionanme <MINIO_LAMBDA_WEBHOOK_AUTH_TOKEN>`
|
|
Specify the opaque string or JWT authorization token for authenticating to the webhook.
|
|
|
|
:envvar:`MINIO_LAMBDA_WEBHOOK_CLIENT_CERT_functionname <MINIO_LAMBDA_WEBHOOK_CLIENT_CERT>`
|
|
Specify the client certificate to use for mTLS authentication to the webhook.
|
|
|
|
:envvar:`MINIO_LAMBDA_WEBHOOK_CLIENT_KEY_functionname <MINIO_LAMBDA_WEBHOOK_CLIENT_CERT>`
|
|
Specify the private key to use for mTLS authentication to the webhook.
|
|
|
|
Restart MinIO to apply the changes.
|
|
|
|
|
|
Trigger From an Application
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
To request a transformed object from your application:
|
|
|
|
#. Connect to the MinIO deployment.
|
|
|
|
#. Set the Object Lambda target by adding a ``lambdaArn`` parameter with the ARN of the desired handler.
|
|
|
|
#. Generate a `presigned URL <https://min.io/docs/minio/linux/developers/go/API.html#presigned-operations>`__ for the original object.
|
|
|
|
#. Use the generated URL to retrieve the transformed object.
|
|
|
|
MinIO sends the request to the target Object Lambda handler.
|
|
The handler returns the transformed contents back to MinIO, which validates the response and sends it back to the application.
|
|
|
|
|
|
Example
|
|
-------
|
|
|
|
Transform the contents of an object using Python, Go, and ``curl``:
|
|
|
|
- Create and register an Object Lambda handler.
|
|
- Create a bucket and an object to transform.
|
|
- Request and display the transformed object contents.
|
|
|
|
Prerequisites:
|
|
|
|
- An existing :ref:`MinIO <minio-installation>` deployment
|
|
- Working Python (3.8+) and Golang development environments
|
|
- :doc:`The MinIO Go SDK </developers/go/minio-go>`
|
|
|
|
|
|
Create a Handler
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
The sample handler, written in Python, retrieves the target object using a `presigned URL <https://min.io/docs/minio/linux/developers/go/API.html#presigned-operations>`__ generated by the caller.
|
|
The handler then transforms the object's contents and returns the new text.
|
|
It uses the `Flask web framework <https://flask.palletsprojects.com/en/2.2.x/>`__ and Python 3.8+.
|
|
|
|
The following command installs Flask and other needed dependencies:
|
|
|
|
.. code-block:: shell
|
|
:class: copyable
|
|
|
|
pip install flask requests
|
|
|
|
The handler calls ``swapcase()`` to change the case of each letter in the original text.
|
|
It then sends the results back to MinIO, which returns it to the caller.
|
|
|
|
.. code-block:: py
|
|
:class: copyable
|
|
|
|
from flask import Flask, request, abort, make_response
|
|
import requests
|
|
|
|
app = Flask(__name__)
|
|
@app.route('/', methods=['POST'])
|
|
def get_webhook():
|
|
if request.method == 'POST':
|
|
# Get the request event from the 'POST' call
|
|
event = request.json
|
|
|
|
# Get the object context
|
|
object_context = event["getObjectContext"]
|
|
|
|
# Get the presigned URL
|
|
# Used to fetch the original object from MinIO
|
|
s3_url = object_context["inputS3Url"]
|
|
|
|
# Extract the route and request tokens from the input context
|
|
request_route = object_context["outputRoute"]
|
|
request_token = object_context["outputToken"]
|
|
|
|
# Get the original S3 object using the presigned URL
|
|
r = requests.get(s3_url)
|
|
original_object = r.content.decode('utf-8')
|
|
|
|
# Transform the text in the object by swapping the case of each char
|
|
transformed_object = original_object.swapcase()
|
|
|
|
# Return the object back to Object Lambda, with required headers
|
|
# This sends the transformed data to MinIO
|
|
# and then to the user
|
|
resp = make_response(transformed_object, 200)
|
|
resp.headers['x-amz-request-route'] = request_route
|
|
resp.headers['x-amz-request-token'] = request_token
|
|
return resp
|
|
|
|
else:
|
|
abort(400)
|
|
|
|
if __name__ == '__main__':
|
|
app.run()
|
|
|
|
|
|
Start the Handler
|
|
+++++++++++++++++
|
|
|
|
Use the following command to start the handler in your local development environment:
|
|
|
|
.. code-block:: shell
|
|
:class: copyable
|
|
|
|
python lambda_handler.py
|
|
|
|
The output resembles the following:
|
|
|
|
.. code-block:: shell
|
|
|
|
* Serving Flask app 'lambda_handler'
|
|
* Debug mode: off
|
|
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
|
|
* Running on http://127.0.0.1:5000
|
|
Press CTRL+C to quit
|
|
|
|
|
|
Start MinIO
|
|
+++++++++++
|
|
|
|
Once the handler is running, start MinIO with the :envvar:`MINIO_LAMBDA_WEBHOOK_ENABLE` and :envvar:`MINIO_LAMBDA_WEBHOOK_ENDPOINT` environment variables to register the function with MinIO.
|
|
To identify the specific Object Lambda handler, append the name of the function to the name of the environment variable.
|
|
|
|
The following command starts MinIO in your local development environment:
|
|
|
|
.. code-block:: shell
|
|
:class: copyable
|
|
|
|
MINIO_LAMBDA_WEBHOOK_ENABLE_myfunction=on MINIO_LAMBDA_WEBHOOK_ENDPOINT_myfunction=http://localhost:5000 minio server /data
|
|
|
|
Replace ``myfunction`` with the name of your handler function and ``/data`` with the location of the MinIO directory for your local deployment.
|
|
The output resembles the following:
|
|
|
|
.. code-block:: shell
|
|
|
|
MinIO Object Storage Server
|
|
Copyright: 2015-2023 MinIO, Inc.
|
|
License: GNU AGPLv3 <https://www.gnu.org/licenses/agpl-3.0.html>
|
|
Version: RELEASE.2023-03-24T21-41-23Z (go1.19.7 linux/arm64)
|
|
|
|
Status: 1 Online, 0 Offline.
|
|
API: http://192.168.64.21:9000 http://127.0.0.1:9000
|
|
RootUser: minioadmin
|
|
RootPass: minioadmin
|
|
Object Lambda ARNs: arn:minio:s3-object-lambda::myfunction:webhook
|
|
|
|
|
|
Test the Handler
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
To test the Lambda handler function, first create an object to transform.
|
|
Then invoke the handler, in this case with ``curl``, using the presigned URL from a Go function.
|
|
|
|
#. Create a bucket and object for the handler to transform.
|
|
|
|
.. code-block:: shell
|
|
:class: copyable
|
|
|
|
mc alias set myminio/ http://localhost:9000 minioadmin minioadmin
|
|
mc mb myminio/myfunctionbucket
|
|
cat > testobject << EOF
|
|
Hello, World!
|
|
EOF
|
|
mc cp testobject myminio/myfunctionbucket/
|
|
|
|
#. Invoke the Handler
|
|
|
|
The following Go code uses the :doc:`The MinIO Go SDK </developers/go/minio-go>` to generate a presigned URL and print it to ``stdout``.
|
|
|
|
.. code-block:: go
|
|
:class: copyable
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"net/url"
|
|
"time"
|
|
"fmt"
|
|
|
|
"github.com/minio/minio-go/v7"
|
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
|
)
|
|
|
|
func main() {
|
|
|
|
// Connect to the MinIO deployment
|
|
s3Client, err := minio.New("localhost:9000", &minio.Options{
|
|
Creds: credentials.NewStaticV4("my_admin_user", "my_admin_password", ""),
|
|
Secure: false,
|
|
})
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
// Set the Lambda function target using its ARN
|
|
reqParams := make(url.Values)
|
|
reqParams.Set("lambdaArn", "arn:minio:s3-object-lambda::myfunction:webhook")
|
|
|
|
// Generate a presigned url to access the original object
|
|
presignedURL, err := s3Client.PresignedGetObject(context.Background(), "myfunctionbucket", "testobject", time.Duration(1000)*time.Second, reqParams)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
// Print the URL to stdout
|
|
fmt.Println(presignedURL)
|
|
}
|
|
|
|
In the code above, replace the following values:
|
|
|
|
- Replace ``my_admin_user`` and ``my_admin_password`` with user credentials for a MinIO deployment.
|
|
- Replace ``myfunction`` with the same function name set in the ``MINIO_LAMBDA_WEBHOOK_ENABLE`` and ``MINIO_LAMBDA_WEBHOOK_ENDPOINT`` environment variables.
|
|
|
|
To retrieve the transformed object, execute the Go code with ``curl`` to generate a GET request:
|
|
|
|
.. code-block:: shell
|
|
:class: copyable
|
|
|
|
curl -v $(go run presigned.go)
|
|
|
|
``curl`` runs the Go code and then retrieves the object with a GET request to the presigned URL.
|
|
The output resembles the following:
|
|
|
|
.. code-block:: shell
|
|
|
|
* Trying 127.0.0.1:9000...
|
|
* Connected to localhost (127.0.0.1) port 9000 (#0)
|
|
> GET /myfunctionbucket/testobject?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minioadmin%2F20230406%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230406T184749Z&X-Amz-Expires=1000&X-Amz-SignedHeaders=host&lambdaArn=arn%3Aminio%3As3-object-lambda%3A%3Amyfunction%3Awebhook&X-Amz-Signature=68fe7e03929a7c0da38255121b2ae09c302840c06654d1b79d7907d942f69915 HTTP/1.1
|
|
> Host: localhost:9000
|
|
> User-Agent: curl/7.81.0
|
|
> Accept: */*
|
|
>
|
|
* Mark bundle as not supporting multiuse
|
|
< HTTP/1.1 200 OK
|
|
< Content-Security-Policy: block-all-mixed-content
|
|
< Strict-Transport-Security: max-age=31536000; includeSubDomains
|
|
< Vary: Origin
|
|
< Vary: Accept-Encoding
|
|
< X-Amz-Id-2: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
|
< X-Amz-Request-Id: 17536CF16130630E
|
|
< X-Content-Type-Options: nosniff
|
|
< X-Xss-Protection: 1; mode=block
|
|
< Date: Thu, 06 Apr 2023 18:47:49 GMT
|
|
< Content-Length: 14
|
|
< Content-Type: text/plain; charset=utf-8
|
|
<
|
|
hELLO, wORLD!
|
|
* Connection #0 to host localhost left intact
|
|
|
|
|
|
|