Skip to content

Commit ddae83b

Browse files
weltekialexellis
authored andcommitted
Improve README structure and clarity
Signed-off-by: Han Verstraete (OpenFaaS Ltd) <han@openfaas.com>
1 parent a49e0f4 commit ddae83b

1 file changed

Lines changed: 40 additions & 62 deletions

File tree

README.md

Lines changed: 40 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@ The official Python SDK for [OpenFaaS](https://www.openfaas.com).
44

55
## Features
66

7-
- Full coverage of the OpenFaaS REST API — functions, namespaces, secrets, logs, system info, and function invocation
8-
- Synchronous `Client` backed by `requests`
9-
- Multiple auth strategies: Basic auth, OpenFaaS IAM (token exchange), OAuth2 client credentials
10-
- Pydantic v2 models for all request and response types — validated, typed, IDE-friendly
11-
- Streaming log support via iterators
12-
- `FunctionBuilder` client for the [OpenFaaS Pro Function Builder API](https://docs.openfaas.com/openfaas-pro/builder/) — build and push function images from source
13-
- `FAAS_DEBUG=1` environment variable for request/response logging (auth headers redacted)
14-
- Context manager support for automatic connection cleanup
7+
- Manage functions, namespaces, and secrets
8+
- Invoke functions synchronously or asynchronously
9+
- Stream function logs
10+
- Basic auth and [OpenFaaS IAM](https://docs.openfaas.com/openfaas-pro/iam/overview/) support
11+
- Build and push function images from source via the [Function Builder API](https://docs.openfaas.com/openfaas-pro/builder/)
1512

1613
## Requirements
1714

@@ -72,20 +69,26 @@ with open("/var/secrets/basic-auth-password") as f:
7269
auth = BasicAuth(username="admin", password=password)
7370
```
7471

75-
### Custom auth
72+
### OpenFaaS IAM — external IdP via client credentials
7673

77-
Subclass `requests.auth.AuthBase` directly to implement your own strategy:
74+
For workloads outside Kubernetes, use `ClientCredentialsTokenSource` to obtain tokens from an external IdP and exchange them for an OpenFaaS gateway JWT:
7875

7976
```python
80-
import requests.auth
77+
from openfaas_sdk import Client, TokenAuth, ClientCredentialsTokenSource
8178

82-
class MyTokenAuth(requests.auth.AuthBase):
83-
def __init__(self, token: str) -> None:
84-
self._token = token
79+
ts = ClientCredentialsTokenSource(
80+
client_id="my-app",
81+
client_secret="secret",
82+
token_url="https://idp.example.com/realms/master/protocol/openid-connect/token",
83+
scope="openid",
84+
)
85+
auth = TokenAuth(
86+
token_url="https://gateway.example.com/oauth/token",
87+
token_source=ts,
88+
)
8589

86-
def __call__(self, r: requests.PreparedRequest) -> requests.PreparedRequest:
87-
r.headers["Authorization"] = f"Bearer {self._token}"
88-
return r
90+
with Client("https://gateway.example.com", auth=auth) as client:
91+
functions = client.get_functions("openfaas-fn")
8992
```
9093

9194
### OpenFaaS IAM — Kubernetes workload identity
@@ -110,28 +113,6 @@ with Client("https://gateway.example.com", auth=auth) as client:
110113

111114
`TokenAuth` also implements the `TokenSource` protocol, so it is automatically used as the `function_token_source` for per-function scoped token exchange when calling `get_function_token()`.
112115

113-
### OpenFaaS IAM — external IdP via client credentials
114-
115-
For workloads outside Kubernetes, use `ClientCredentialsTokenSource` to obtain tokens from an external IdP and exchange them for an OpenFaaS gateway JWT:
116-
117-
```python
118-
from openfaas_sdk import Client, TokenAuth, ClientCredentialsTokenSource
119-
120-
ts = ClientCredentialsTokenSource(
121-
client_id="my-app",
122-
client_secret="secret",
123-
token_url="https://idp.example.com/realms/master/protocol/openid-connect/token",
124-
scope="openid",
125-
)
126-
auth = TokenAuth(
127-
token_url="https://gateway.example.com/oauth/token",
128-
token_source=ts,
129-
)
130-
131-
with Client("https://gateway.example.com", auth=auth) as client:
132-
functions = client.get_functions("openfaas-fn")
133-
```
134-
135116
### Custom token sources
136117

137118
If the built-in token sources don't fit your setup, you can implement your own. The `TokenSource` protocol requires a single method, `token() -> str`, that returns a raw OIDC JWT. The implementation can contain any logic you need, for example reading a token from a file, calling an external API, or signing a JWT locally:
@@ -160,7 +141,9 @@ auth = TokenAuth(
160141

161142
### Per-function scoped tokens
162143

163-
`get_function_token()` exchanges the current identity token for a short-lived token scoped to a specific function (audience `"<namespace>:<function-name>"`). Use this token when invoking functions directly:
144+
> **Advanced use case.** For most scenarios, use [`invoke_function`](#function-invocation) with `use_function_auth=True`, which handles token exchange automatically. The method below is for cases where you need full control over function invocation outside of the SDK.
145+
146+
`get_function_token()` exchanges the current identity token for a short-lived token scoped to a specific function (audience `"<namespace>:<function-name>"`). Use this token when invoking the function directly without going through the SDK:
164147

165148
```python
166149
token = client.get_function_token("my-func", "openfaas-fn")
@@ -202,9 +185,6 @@ client.deploy(spec)
202185
spec.image = "ghcr.io/openfaas/env:0.2.0"
203186
client.update(spec)
204187

205-
# Scale a function
206-
client.scale_function("env", replicas=3, namespace="openfaas-fn")
207-
208188
# Delete a function
209189
client.delete_function("env", "openfaas-fn")
210190
```
@@ -274,12 +254,17 @@ for msg in client.get_logs("env", namespace="openfaas-fn", since=since):
274254

275255
### Function invocation
276256

277-
`invoke_function` returns the raw `requests.Response` from the function. Non-2xx responses are **not** raised as exceptions — function responses are application-level and the caller decides how to interpret them.
257+
#### Synchronous invocation
258+
259+
`invoke_function` returns the raw `requests.Response` from the function. Unlike gateway API calls, non-2xx status codes are not raised as exceptions, function responses are application-level and it is up to the caller to interpret them.
278260

279261
```python
280262
# POST with a bytes or str payload
281263
resp = client.invoke_function("env", method="POST", payload=b"hello")
282-
print(resp.status_code, resp.text)
264+
if resp.ok:
265+
print(resp.text)
266+
else:
267+
print(f"Error: {resp.status_code} - {resp.text}")
283268

284269
# GET with no payload
285270
resp = client.invoke_function("env", method="GET")
@@ -297,9 +282,9 @@ resp = client.invoke_function(
297282
)
298283
```
299284

300-
### Async (queued) invocation
285+
#### Asynchronous invocation
301286

302-
`invoke_function_async` queues the invocation via the gateway's `/async-function/` route and returns immediately with a `202 Accepted` response. The function result is not returned synchronously.
287+
`invoke_function_async` queues the invocation via the gateway's `/async-function/` route and returns immediately with a `202 Accepted` response. The function result is not returned synchronously.
303288

304289
```python
305290
# Async invocation — returns 202 immediately
@@ -313,12 +298,9 @@ client.invoke_function_async(
313298
)
314299
```
315300

316-
#### IAM-scoped function invocation
301+
#### IAM function authentication
317302

318-
When OpenFaaS IAM is enabled, use `use_function_auth=True` to automatically
319-
obtain a per-function scoped token and attach it as a Bearer token. This
320-
requires the client to be configured with a `TokenAuth` (or any
321-
`function_token_source`):
303+
When OpenFaaS IAM is enabled, use `use_function_auth=True` to automatically obtain a per-function scoped token and attach it as a Bearer token. This requires the client to be configured with a `TokenAuth` (or an explicit [`function_token_source`](#using-a-separate-identity-provider-for-function-invocations)):
322304

323305
```python
324306
from openfaas_sdk import Client, TokenAuth, ServiceAccountTokenSource
@@ -385,19 +367,15 @@ session.proxies = {"https": "http://proxy.corp.example.com"}
385367
client = Client("https://gateway.example.com", auth=auth, http_client=session)
386368
```
387369

388-
### Debug logging
370+
### Using a separate identity provider for function invocations
389371

390-
Set `FAAS_DEBUG=1` to log all requests and responses. The `Authorization` header is automatically redacted.
391-
392-
```bash
393-
FAAS_DEBUG=1 python my_script.py
394-
```
395-
396-
Configure the log level in your application to see the output:
372+
When using `use_function_auth=True` on function invocations, the client needs a token source to obtain per-function scoped tokens. If the client is configured with `TokenAuth`, it is used automatically. You can also pass a `TokenAuth` instance explicitly as `function_token_source` to use a different identity provider for function invocations than the one used for gateway API calls:
397373

398374
```python
399-
import logging
400-
logging.basicConfig(level=logging.DEBUG)
375+
client = Client(
376+
"https://gateway.example.com",
377+
function_token_source=TokenAuth(...),
378+
)
401379
```
402380

403381
## Function Builder

0 commit comments

Comments
 (0)