Knative Functions (func)

repository·main·Indexed 18 days ago

https://github.com/knative/func

A Client Library and CLI toolset for developing, deploying, and managing platform-agnostic Knative Functions. It supports multiple languages including Go, Node.js, Python, Quarkus, Rust, SpringBoot, and TypeScript, with deployment targets for Kubernetes, OpenShift, and Localhost.

Tokens
94.3K
Snippets
321
Records
415
Agent score
62%

What's inside knative-func

  1. Overview of HCL (HashiCorp Configuration Language)

    main

    HCL is a structured configuration language designed to be both human-readable and machine-friendly, specifically targeted towards DevOps tools and servers. It serves as a middle ground between verbose data languages like JSON and complex programming languages like Ruby.

    Key characteristics:

    • JSON Compatibility: HCL is fully JSON compatible. Any valid JSON can be used as input to a system expecting HCL, making it an ideal interoperability layer for machine-generated configurations.
    • Human-Centric: Designed to be easily written and modified by humans, supporting comments and a cleaner syntax than JSON.
  2. Overview of Knative Functions

    main

    Knative Functions (func) is a Client Library and CLI designed to enable the development of platform-agnostic functions. It allows developers to write functions in multiple languages and deploy them across various environments without being tied to a specific infrastructure.

    Supported Languages

    You can use built-in templates to write functions in:

    • Go (Golang)
    • Node.js (JavaScript)
    • Python
    • Quarkus (Java)
    • Rust
    • SpringBoot (Java)
    • TypeScript

    Supported Deployment Platforms

    Functions developed with func can be deployed to:

    • Kubernetes
    • OpenShift
    • Localhost
  3. Overview of the func Client Library and CLI

    main
    The func project provides both a Client Library and a Command Line Interface (CLI) designed to enable the development and deployment of Knative Functions. It serves as the primary toolset for developers to manage the lifecycle of functions, from creation to deployment and configuration management.
  4. Use the func CLI to manage Knative Functions

    main

    The func command-line interface is used to manage the lifecycle of Knative Function resources. You can use it to create new functions from templates, build container images, deploy functions to a registry, run functions locally, and manage event subscriptions.

    Common workflows include:

    • Creating a function: Use func create with a --language flag to scaffold a new function in the current directory.
    • Deploying a function: Use func deploy specifying a --registry to host the function's container image.
    • Local development: Use func run to execute the function locally or func invoke to trigger it.
    # Create a new Node.js function in the current directory
    func create --language node myfunction
    
    # Deploy the function using Docker hub to host the image
    func deploy --registry docker.io/alice
  5. High level overview of Knative Func

    main

    Knative Func is a CLI designed to simplify the lifecycle of building and deploying serverless functions on Kubernetes. It abstracts away the complexity of writing Dockerfiles and Kubernetes manifests by providing a streamlined workflow through three primary commands:

    1. func create: Initializes a new function project from a template.
    2. func build: Builds an OCI image from the source code using various builder engines (Buildpacks, S2I, or direct OCI).
    3. func deploy: Pushes the built image to a registry and creates a Knative Service or a bare Kubernetes deployment.

    The CLI handles image building, registry pushing, and deployment orchestration automatically.

  6. Use go-retryablehttp for automatic HTTP retries

    main

    The retryablehttp package provides an HTTP client interface that automatically performs retries with exponential backoff. It acts as a thin wrapper over the standard net/http library, making it easy to integrate into existing Go programs.

    Retry Behavior

    retryablehttp triggers a retry under the following conditions:

    • An error is returned by the client (e.g., connection errors).
    • A 500-range response code is received (except for 501 Not Implemented).

    Request Bodies

    For requests requiring a body (e.g., POST, PUT), the library supports various methods to provide the body that allow for "rewinding." This ensures the full request body can be re-sent if an initial attempt fails.

  7. What is cleanhttp and why use it?

    main

    In Go, http.DefaultClient and http.DefaultTransport are shared global values. While http.Client is safe for concurrent use, modifying the fields of the client struct itself is not thread-safe. If multiple libraries or goroutines attempt to tweak these global defaults, it can lead to race conditions and unpredictable behavior.

    cleanhttp provides functions to obtain a "clean" http.Client. This client uses the same default values as the Go standard library but returns an instance that does not share state with other clients, preventing accidental side effects from library dependencies.

  8. Overview of E2E test categories

    main

    The E2E test suite is divided into several categories to verify different aspects of the func CLI and the underlying system:

    • Core: Validates basic CRUDL operations: func init (Create), func run (local execution), func deploy (cluster execution/Update), func describe (Read), and func list (List). It also tests remote repository references and templates.
    • Metadata: Ensures environment variables, labels, volumes, secrets, and event subscriptions are correctly applied to a Function.
    • Remote: Verifies features related to in-cluster builds and remote deployment.
    • Podman: Confirms support for the Podman container engine (requires podman and ssh in PATH).
    • Matrix: A large-scale test set that checks operations across different language runtimes, templates, and builders. It executes for each language/template/builder permutation. Language-specific scenarios are prefixed with TestMatrix_<Runtime>_* (e.g., TestMatrix_Python_*).
    • Config CI: Verifies that GitHub Actions workflows generated by func config ci work correctly using act against a real cluster.
  9. How to process request data in Python ASGI handlers

    main

    Because Python functions use the ASGI protocol, you must asynchronously iterate through the receive callable to collect the request body. For JSON data, you collect the bytes and then use json.loads().

    async def handle(self, scope, receive, send):
        """Process POST requests with JSON data."""
        if scope['method'] == 'POST':
            # Receive request body
            body = b''
            while True:
                message = await receive()
                if message['type'] == 'http.request':
                    body += message.get('body', b'')
                    if not message.get('more_body', False):
                        break
    
            # Process JSON data
            import json
            data = json.loads(body)
            
            # ... logic ...
    
            # Send response
            await send({
                'type': 'http.response.start',
                'status': 200,
                'headers': [[b'content-type', b'application/json']],
            })
            await send({
                'type': 'http.response.body',
                'body': json.dumps(data).encode(),
            })
  10. Implement a Python CloudEvents Function

    main

    To create a Python CloudEvents function, you must implement a class that provides a new() method. This method is responsible for returning a function instance.

    Beyond the required new() method, your function class can optionally implement the following lifecycle and health methods:

    • handle(): Processes incoming CloudEvent requests.
    • start(): Used to initialize the function with specific configuration.
    • stop(): Used to clean up resources when the function is shutting down.
    • alive(): Implements a liveness check.
    • ready(): Implements a readiness check.
    # See the default implementation in ./function/func.py for reference
    
    # Required method:
    def new():
        return FunctionInstance()
    
    # Optional methods:
    class FunctionInstance:
        def handle(self, event):
            ...
        def start(self):
            ...
        def stop(self):
            ...
        def alive(self):
            ...
        def ready(self):
            ...
  11. Implement a Node.js function handler

    main

    A Node.js function must export a single function from index.js. The handler receives two parameters:

    1. context: A Context object containing request metadata.
    2. event (optional): The CloudEvent object if the function is triggered by a CloudEvent.

    Return Values

    • Nothing: Returns 204 No Content.
    • JavaScript Type: Returns the value as the response body.
    • CloudEvent or Message: Returns the event to the Knative eventing system.
    • Object with headers: Sets custom HTTP response headers.
    • Object with statusCode: Sets a specific HTTP response code.
    • Thrown Error: If an Error object is thrown, you can set err.statusCode to control the response code.
    // Basic CloudEvent handler
    function handle(context, event) {
      return processCustomer(event.data)
    }
    
    function processCustomer(customer) {
      // Return a new CloudEvent to push into the eventing system
      return new CloudEvent({
        source: 'customer.processor',
        type: 'customer.processed'
      })
    }
    
    // Returning custom headers
    function processCustomerWithHeaders(customer) {
      return { headers: { customerid: customer.id } };
    }
    
    // Returning a custom status code
    function processCustomerWithStatus(customer) {
      if (customer.restricted) {
        return { statusCode: 451 }
      }
    }
    
    // Throwing an error with a status code
    function processCustomerWithError(customer) {
      if (customer.restricted) {
        const err = new Error('Unavailable for legal reasons');
        err.statusCode = 451;
        throw err;
      }
    }
  12. Structure of a Rust function project

    main

    A Rust function generated by func uses a standard directory layout. The core logic is typically split between main.rs, handler.rs, and config.rs within the src directory.

    Example directory structure:

    fn
    ├── Cargo.lock
    ├── Cargo.toml
    ├── func.yaml
    ├── README.md
    └── src
        ├── config.rs
        ├── handler.rs
        └── main.rs