faas-clinet

This commit is contained in:
2025-08-31 17:19:13 -04:00
parent e3e6a4460b
commit ac51f75b5c
5 changed files with 1039 additions and 0 deletions

297
faas-client/README.md Normal file
View File

@ -0,0 +1,297 @@
# Skybridge FaaS Client
A lightweight Go client library for interacting with the Skybridge Function-as-a-Service (FaaS) platform.
## Installation
```bash
go get github.com/RyanCopley/skybridge/faas-client
```
## Usage
### Basic Client Setup
```go
package main
import (
"context"
"log"
faasclient "github.com/RyanCopley/skybridge/faas-client"
)
func main() {
// Create a new client
client := faasclient.NewClient(
"http://localhost:8080", // FaaS API base URL
faasclient.WithUserEmail("admin@example.com"), // Authentication
)
// Use the client...
}
```
### Authentication Options
```go
// Using user email header (for development)
client := faasclient.NewClient(baseURL,
faasclient.WithUserEmail("user@example.com"))
// Using custom auth headers
client := faasclient.NewClient(baseURL,
faasclient.WithAuth(map[string]string{
"Authorization": "Bearer " + token,
"X-User-Email": "user@example.com",
}))
// Using custom HTTP client
httpClient := &http.Client{Timeout: 30 * time.Second}
client := faasclient.NewClient(baseURL,
faasclient.WithHTTPClient(httpClient))
```
### Function Management
#### Creating a Function
```go
req := &faasclient.CreateFunctionRequest{
Name: "my-function",
AppID: "my-app",
Runtime: faasclient.RuntimeNodeJS18,
Image: "node:18-alpine", // Optional, auto-selected if not provided
Handler: "index.handler",
Code: "exports.handler = async (event) => { return 'Hello World'; }",
Environment: map[string]string{
"NODE_ENV": "production",
},
Timeout: faasclient.Duration(30 * time.Second),
Memory: 512,
Owner: faasclient.Owner{
Type: faasclient.OwnerTypeIndividual,
Name: "John Doe",
Owner: "john@example.com",
},
}
function, err := client.CreateFunction(context.Background(), req)
if err != nil {
log.Fatal(err)
}
log.Printf("Created function: %s (ID: %s)", function.Name, function.ID)
```
#### Getting a Function
```go
function, err := client.GetFunction(context.Background(), functionID)
if err != nil {
log.Fatal(err)
}
log.Printf("Function: %s, Runtime: %s", function.Name, function.Runtime)
```
#### Updating a Function
```go
newTimeout := faasclient.Duration(60 * time.Second)
req := &faasclient.UpdateFunctionRequest{
Timeout: &newTimeout,
Environment: map[string]string{
"NODE_ENV": "development",
},
}
function, err := client.UpdateFunction(context.Background(), functionID, req)
if err != nil {
log.Fatal(err)
}
```
#### Listing Functions
```go
response, err := client.ListFunctions(context.Background(), "my-app", 50, 0)
if err != nil {
log.Fatal(err)
}
for _, fn := range response.Functions {
log.Printf("Function: %s (ID: %s)", fn.Name, fn.ID)
}
```
#### Deleting a Function
```go
err := client.DeleteFunction(context.Background(), functionID)
if err != nil {
log.Fatal(err)
}
```
### Function Deployment
```go
// Deploy with default options
resp, err := client.DeployFunction(context.Background(), functionID, nil)
if err != nil {
log.Fatal(err)
}
// Force deployment
req := &faasclient.DeployFunctionRequest{
Force: true,
}
resp, err = client.DeployFunction(context.Background(), functionID, req)
if err != nil {
log.Fatal(err)
}
log.Printf("Deployment status: %s", resp.Status)
```
### Function Execution
#### Synchronous Execution
```go
input := json.RawMessage(`{"name": "World"}`)
req := &faasclient.ExecuteFunctionRequest{
FunctionID: functionID,
Input: input,
Async: false,
}
response, err := client.ExecuteFunction(context.Background(), req)
if err != nil {
log.Fatal(err)
}
log.Printf("Result: %s", string(response.Output))
log.Printf("Duration: %v", response.Duration)
```
#### Asynchronous Execution
```go
input := json.RawMessage(`{"name": "World"}`)
response, err := client.InvokeFunction(context.Background(), functionID, input)
if err != nil {
log.Fatal(err)
}
log.Printf("Execution ID: %s", response.ExecutionID)
log.Printf("Status: %s", response.Status)
```
### Execution Management
#### Getting Execution Details
```go
execution, err := client.GetExecution(context.Background(), executionID)
if err != nil {
log.Fatal(err)
}
log.Printf("Status: %s", execution.Status)
log.Printf("Duration: %v", execution.Duration)
if execution.Error != "" {
log.Printf("Error: %s", execution.Error)
}
```
#### Listing Executions
```go
// List all executions
response, err := client.ListExecutions(context.Background(), nil, 50, 0)
if err != nil {
log.Fatal(err)
}
// List executions for a specific function
response, err = client.ListExecutions(context.Background(), &functionID, 50, 0)
if err != nil {
log.Fatal(err)
}
for _, exec := range response.Executions {
log.Printf("Execution: %s, Status: %s", exec.ID, exec.Status)
}
```
#### Canceling an Execution
```go
err := client.CancelExecution(context.Background(), executionID)
if err != nil {
log.Fatal(err)
}
```
#### Getting Execution Logs
```go
logs, err := client.GetExecutionLogs(context.Background(), executionID)
if err != nil {
log.Fatal(err)
}
for _, logLine := range logs.Logs {
log.Printf("Log: %s", logLine)
}
```
#### Getting Running Executions
```go
response, err := client.GetRunningExecutions(context.Background())
if err != nil {
log.Fatal(err)
}
log.Printf("Running executions: %d", response.Count)
for _, exec := range response.Executions {
log.Printf("Running: %s (Function: %s)", exec.ID, exec.FunctionID)
}
```
## Types
The client provides comprehensive type definitions that match the FaaS API:
- `FunctionDefinition` - Complete function metadata
- `FunctionExecution` - Execution details and results
- `RuntimeType` - Supported runtimes (NodeJS18, Python39, Go120, Custom)
- `ExecutionStatus` - Execution states (Pending, Running, Completed, Failed, etc.)
- `Owner` - Ownership information
- Request/Response types for all operations
## Error Handling
The client provides detailed error messages that include HTTP status codes and response bodies:
```go
function, err := client.GetFunction(ctx, nonExistentID)
if err != nil {
// Error will include status code and details
log.Printf("Error: %v", err) // "get function failed with status 404: Function not found"
}
```
## Architecture Benefits
This client package is designed as a lightweight, standalone library that:
- **No heavy dependencies**: Only requires `google/uuid`
- **Zero coupling**: Doesn't import the entire FaaS service
- **Modular**: Can be used by any service in your monolith
- **Type-safe**: Comprehensive Go types for all API operations
- **Flexible auth**: Supports multiple authentication methods

396
faas-client/client.go Normal file
View File

@ -0,0 +1,396 @@
package faasclient
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"github.com/google/uuid"
)
// Client represents the FaaS API client
type Client struct {
baseURL string
httpClient *http.Client
authHeader map[string]string
}
// NewClient creates a new FaaS client
func NewClient(baseURL string, options ...ClientOption) *Client {
client := &Client{
baseURL: baseURL,
httpClient: http.DefaultClient,
authHeader: make(map[string]string),
}
for _, option := range options {
option(client)
}
return client
}
// ClientOption represents a configuration option for the client
type ClientOption func(*Client)
// WithHTTPClient sets a custom HTTP client
func WithHTTPClient(httpClient *http.Client) ClientOption {
return func(c *Client) {
c.httpClient = httpClient
}
}
// WithAuth sets authentication headers
func WithAuth(headers map[string]string) ClientOption {
return func(c *Client) {
for k, v := range headers {
c.authHeader[k] = v
}
}
}
// WithUserEmail sets the X-User-Email header for authentication
func WithUserEmail(email string) ClientOption {
return func(c *Client) {
c.authHeader["X-User-Email"] = email
}
}
// doRequest performs an HTTP request
func (c *Client) doRequest(ctx context.Context, method, path string, body interface{}) (*http.Response, error) {
var reqBody io.Reader
if body != nil {
jsonData, err := json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("failed to marshal request body: %w", err)
}
reqBody = bytes.NewBuffer(jsonData)
}
req, err := http.NewRequestWithContext(ctx, method, c.baseURL+path, reqBody)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
// Add authentication headers
for k, v := range c.authHeader {
req.Header.Set(k, v)
}
return c.httpClient.Do(req)
}
// CreateFunction creates a new function
func (c *Client) CreateFunction(ctx context.Context, req *CreateFunctionRequest) (*FunctionDefinition, error) {
resp, err := c.doRequest(ctx, "POST", "/api/v1/functions", req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("create function failed with status %d: %s", resp.StatusCode, string(body))
}
var function FunctionDefinition
if err := json.NewDecoder(resp.Body).Decode(&function); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &function, nil
}
// GetFunction retrieves a function by ID
func (c *Client) GetFunction(ctx context.Context, id uuid.UUID) (*FunctionDefinition, error) {
resp, err := c.doRequest(ctx, "GET", "/api/v1/functions/"+id.String(), nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("get function failed with status %d: %s", resp.StatusCode, string(body))
}
var function FunctionDefinition
if err := json.NewDecoder(resp.Body).Decode(&function); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &function, nil
}
// UpdateFunction updates an existing function
func (c *Client) UpdateFunction(ctx context.Context, id uuid.UUID, req *UpdateFunctionRequest) (*FunctionDefinition, error) {
resp, err := c.doRequest(ctx, "PUT", "/api/v1/functions/"+id.String(), req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("update function failed with status %d: %s", resp.StatusCode, string(body))
}
var function FunctionDefinition
if err := json.NewDecoder(resp.Body).Decode(&function); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &function, nil
}
// DeleteFunction deletes a function
func (c *Client) DeleteFunction(ctx context.Context, id uuid.UUID) error {
resp, err := c.doRequest(ctx, "DELETE", "/api/v1/functions/"+id.String(), nil)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("delete function failed with status %d: %s", resp.StatusCode, string(body))
}
return nil
}
// ListFunctions lists functions with optional filtering
func (c *Client) ListFunctions(ctx context.Context, appID string, limit, offset int) (*ListFunctionsResponse, error) {
params := url.Values{}
if appID != "" {
params.Set("app_id", appID)
}
if limit > 0 {
params.Set("limit", strconv.Itoa(limit))
}
if offset > 0 {
params.Set("offset", strconv.Itoa(offset))
}
path := "/api/v1/functions"
if len(params) > 0 {
path += "?" + params.Encode()
}
resp, err := c.doRequest(ctx, "GET", path, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("list functions failed with status %d: %s", resp.StatusCode, string(body))
}
var response ListFunctionsResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &response, nil
}
// DeployFunction deploys a function
func (c *Client) DeployFunction(ctx context.Context, id uuid.UUID, req *DeployFunctionRequest) (*DeployFunctionResponse, error) {
if req == nil {
req = &DeployFunctionRequest{FunctionID: id}
}
req.FunctionID = id
resp, err := c.doRequest(ctx, "POST", "/api/v1/functions/"+id.String()+"/deploy", req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("deploy function failed with status %d: %s", resp.StatusCode, string(body))
}
var response DeployFunctionResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &response, nil
}
// ExecuteFunction executes a function synchronously or asynchronously
func (c *Client) ExecuteFunction(ctx context.Context, req *ExecuteFunctionRequest) (*ExecuteFunctionResponse, error) {
resp, err := c.doRequest(ctx, "POST", "/api/v1/functions/"+req.FunctionID.String()+"/execute", req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("execute function failed with status %d: %s", resp.StatusCode, string(body))
}
var response ExecuteFunctionResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &response, nil
}
// InvokeFunction invokes a function asynchronously
func (c *Client) InvokeFunction(ctx context.Context, functionID uuid.UUID, input json.RawMessage) (*ExecuteFunctionResponse, error) {
req := &ExecuteFunctionRequest{
FunctionID: functionID,
Input: input,
Async: true,
}
resp, err := c.doRequest(ctx, "POST", "/api/v1/functions/"+functionID.String()+"/invoke", req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusAccepted {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("invoke function failed with status %d: %s", resp.StatusCode, string(body))
}
var response ExecuteFunctionResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &response, nil
}
// GetExecution retrieves an execution by ID
func (c *Client) GetExecution(ctx context.Context, id uuid.UUID) (*FunctionExecution, error) {
resp, err := c.doRequest(ctx, "GET", "/api/v1/executions/"+id.String(), nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("get execution failed with status %d: %s", resp.StatusCode, string(body))
}
var execution FunctionExecution
if err := json.NewDecoder(resp.Body).Decode(&execution); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &execution, nil
}
// ListExecutions lists executions with optional filtering
func (c *Client) ListExecutions(ctx context.Context, functionID *uuid.UUID, limit, offset int) (*ListExecutionsResponse, error) {
params := url.Values{}
if functionID != nil {
params.Set("function_id", functionID.String())
}
if limit > 0 {
params.Set("limit", strconv.Itoa(limit))
}
if offset > 0 {
params.Set("offset", strconv.Itoa(offset))
}
path := "/api/v1/executions"
if len(params) > 0 {
path += "?" + params.Encode()
}
resp, err := c.doRequest(ctx, "GET", path, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("list executions failed with status %d: %s", resp.StatusCode, string(body))
}
var response ListExecutionsResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &response, nil
}
// CancelExecution cancels a running execution
func (c *Client) CancelExecution(ctx context.Context, id uuid.UUID) error {
resp, err := c.doRequest(ctx, "POST", "/api/v1/executions/"+id.String()+"/cancel", nil)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("cancel execution failed with status %d: %s", resp.StatusCode, string(body))
}
return nil
}
// GetExecutionLogs retrieves logs for an execution
func (c *Client) GetExecutionLogs(ctx context.Context, id uuid.UUID) (*GetLogsResponse, error) {
resp, err := c.doRequest(ctx, "GET", "/api/v1/executions/"+id.String()+"/logs", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("get execution logs failed with status %d: %s", resp.StatusCode, string(body))
}
var response GetLogsResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &response, nil
}
// GetRunningExecutions retrieves all currently running executions
func (c *Client) GetRunningExecutions(ctx context.Context) (*GetRunningExecutionsResponse, error) {
resp, err := c.doRequest(ctx, "GET", "/api/v1/executions/running", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("get running executions failed with status %d: %s", resp.StatusCode, string(body))
}
var response GetRunningExecutionsResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return &response, nil
}

148
faas-client/example/main.go Normal file
View File

@ -0,0 +1,148 @@
package main
import (
"context"
"encoding/json"
"log"
"time"
"github.com/google/uuid"
faasclient "github.com/RyanCopley/skybridge/faas-client"
)
func main() {
// Create FaaS client
client := faasclient.NewClient(
"http://localhost:8080",
faasclient.WithUserEmail("admin@example.com"),
)
ctx := context.Background()
// Create a simple Node.js function
log.Println("Creating function...")
createReq := &faasclient.CreateFunctionRequest{
Name: "hello-world-example",
AppID: "example-app",
Runtime: faasclient.RuntimeNodeJS18,
Handler: "index.handler",
Code: "exports.handler = async (event) => { return { message: 'Hello, ' + (event.name || 'World') + '!' }; }",
Environment: map[string]string{
"NODE_ENV": "production",
},
Timeout: faasclient.Duration(30 * time.Second),
Memory: 256,
Owner: faasclient.Owner{
Type: faasclient.OwnerTypeIndividual,
Name: "Example User",
Owner: "admin@example.com",
},
}
function, err := client.CreateFunction(ctx, createReq)
if err != nil {
log.Fatalf("Failed to create function: %v", err)
}
log.Printf("Created function: %s (ID: %s)", function.Name, function.ID)
// Deploy the function
log.Println("Deploying function...")
deployResp, err := client.DeployFunction(ctx, function.ID, nil)
if err != nil {
log.Fatalf("Failed to deploy function: %v", err)
}
log.Printf("Deployment status: %s - %s", deployResp.Status, deployResp.Message)
// Execute function synchronously
log.Println("Executing function synchronously...")
input := json.RawMessage(`{"name": "Skybridge"}`)
executeReq := &faasclient.ExecuteFunctionRequest{
FunctionID: function.ID,
Input: input,
Async: false,
}
execResp, err := client.ExecuteFunction(ctx, executeReq)
if err != nil {
log.Fatalf("Failed to execute function: %v", err)
}
log.Printf("Sync execution result: %s", string(execResp.Output))
log.Printf("Duration: %v, Memory used: %d MB", execResp.Duration, execResp.MemoryUsed)
// Execute function asynchronously
log.Println("Invoking function asynchronously...")
asyncResp, err := client.InvokeFunction(ctx, function.ID, input)
if err != nil {
log.Fatalf("Failed to invoke function: %v", err)
}
log.Printf("Async execution ID: %s, Status: %s", asyncResp.ExecutionID, asyncResp.Status)
// Wait a moment for async execution to complete
time.Sleep(2 * time.Second)
// Get execution details
log.Println("Getting execution details...")
execution, err := client.GetExecution(ctx, asyncResp.ExecutionID)
if err != nil {
log.Fatalf("Failed to get execution: %v", err)
}
log.Printf("Execution status: %s", execution.Status)
if execution.Status == faasclient.StatusCompleted {
log.Printf("Async execution result: %s", string(execution.Output))
log.Printf("Duration: %v, Memory used: %d MB", execution.Duration, execution.MemoryUsed)
}
// Get execution logs
log.Println("Getting execution logs...")
logs, err := client.GetExecutionLogs(ctx, asyncResp.ExecutionID)
if err != nil {
log.Printf("Failed to get logs: %v", err)
} else {
log.Printf("Logs (%d entries):", len(logs.Logs))
for _, logLine := range logs.Logs {
log.Printf(" %s", logLine)
}
}
// List functions
log.Println("Listing functions...")
listResp, err := client.ListFunctions(ctx, "example-app", 10, 0)
if err != nil {
log.Fatalf("Failed to list functions: %v", err)
}
log.Printf("Found %d functions:", len(listResp.Functions))
for _, fn := range listResp.Functions {
log.Printf(" - %s (%s) - Runtime: %s", fn.Name, fn.ID, fn.Runtime)
}
// List executions for this function
log.Println("Listing executions...")
execListResp, err := client.ListExecutions(ctx, &function.ID, 10, 0)
if err != nil {
log.Fatalf("Failed to list executions: %v", err)
}
log.Printf("Found %d executions:", len(execListResp.Executions))
for _, exec := range execListResp.Executions {
status := string(exec.Status)
log.Printf(" - %s: %s (Duration: %v)", exec.ID, status, exec.Duration)
}
// Clean up - delete the function
log.Println("Cleaning up...")
err = client.DeleteFunction(ctx, function.ID)
if err != nil {
log.Fatalf("Failed to delete function: %v", err)
}
log.Printf("Deleted function: %s", function.ID)
log.Println("Example completed successfully!")
}
// Helper function to create a UUID from string (for testing)
func mustParseUUID(s string) uuid.UUID {
id, err := uuid.Parse(s)
if err != nil {
panic(err)
}
return id
}

7
faas-client/go.mod Normal file
View File

@ -0,0 +1,7 @@
module github.com/RyanCopley/skybridge/faas-client
go 1.23
require (
github.com/google/uuid v1.6.0
)

191
faas-client/types.go Normal file
View File

@ -0,0 +1,191 @@
package faasclient
import (
"encoding/json"
"time"
"github.com/google/uuid"
)
// RuntimeType represents supported function runtimes
type RuntimeType string
const (
RuntimeNodeJS18 RuntimeType = "nodejs18"
RuntimePython39 RuntimeType = "python3.9"
RuntimeGo120 RuntimeType = "go1.20"
RuntimeCustom RuntimeType = "custom"
)
// ExecutionStatus represents the status of function execution
type ExecutionStatus string
const (
StatusPending ExecutionStatus = "pending"
StatusRunning ExecutionStatus = "running"
StatusCompleted ExecutionStatus = "completed"
StatusFailed ExecutionStatus = "failed"
StatusTimeout ExecutionStatus = "timeout"
StatusCanceled ExecutionStatus = "canceled"
)
// OwnerType represents the type of owner
type OwnerType string
const (
OwnerTypeIndividual OwnerType = "individual"
OwnerTypeTeam OwnerType = "team"
)
// Owner represents ownership information
type Owner struct {
Type OwnerType `json:"type"`
Name string `json:"name"`
Owner string `json:"owner"`
}
// Duration wraps time.Duration for JSON marshaling
type Duration time.Duration
func (d Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Duration(d).String())
}
func (d *Duration) UnmarshalJSON(b []byte) error {
var v interface{}
if err := json.Unmarshal(b, &v); err != nil {
return err
}
switch value := v.(type) {
case float64:
*d = Duration(time.Duration(value))
return nil
case string:
tmp, err := time.ParseDuration(value)
if err != nil {
return err
}
*d = Duration(tmp)
return nil
default:
return nil
}
}
// FunctionDefinition represents a serverless function
type FunctionDefinition struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
AppID string `json:"app_id"`
Runtime RuntimeType `json:"runtime"`
Image string `json:"image"`
Handler string `json:"handler"`
Code string `json:"code,omitempty"`
Environment map[string]string `json:"environment,omitempty"`
Timeout Duration `json:"timeout"`
Memory int `json:"memory"`
Owner Owner `json:"owner"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// FunctionExecution represents a function execution
type FunctionExecution struct {
ID uuid.UUID `json:"id"`
FunctionID uuid.UUID `json:"function_id"`
Status ExecutionStatus `json:"status"`
Input json.RawMessage `json:"input,omitempty"`
Output json.RawMessage `json:"output,omitempty"`
Error string `json:"error,omitempty"`
Duration time.Duration `json:"duration"`
MemoryUsed int `json:"memory_used"`
Logs []string `json:"logs,omitempty"`
ContainerID string `json:"container_id,omitempty"`
ExecutorID string `json:"executor_id"`
CreatedAt time.Time `json:"created_at"`
StartedAt *time.Time `json:"started_at,omitempty"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
}
// CreateFunctionRequest represents a request to create a new function
type CreateFunctionRequest struct {
Name string `json:"name"`
AppID string `json:"app_id"`
Runtime RuntimeType `json:"runtime"`
Image string `json:"image"`
Handler string `json:"handler"`
Code string `json:"code,omitempty"`
Environment map[string]string `json:"environment,omitempty"`
Timeout Duration `json:"timeout"`
Memory int `json:"memory"`
Owner Owner `json:"owner"`
}
// UpdateFunctionRequest represents a request to update an existing function
type UpdateFunctionRequest struct {
Name *string `json:"name,omitempty"`
Runtime *RuntimeType `json:"runtime,omitempty"`
Image *string `json:"image,omitempty"`
Handler *string `json:"handler,omitempty"`
Code *string `json:"code,omitempty"`
Environment map[string]string `json:"environment,omitempty"`
Timeout *Duration `json:"timeout,omitempty"`
Memory *int `json:"memory,omitempty"`
Owner *Owner `json:"owner,omitempty"`
}
// ExecuteFunctionRequest represents a request to execute a function
type ExecuteFunctionRequest struct {
FunctionID uuid.UUID `json:"function_id"`
Input json.RawMessage `json:"input,omitempty"`
Async bool `json:"async,omitempty"`
}
// ExecuteFunctionResponse represents a response for function execution
type ExecuteFunctionResponse struct {
ExecutionID uuid.UUID `json:"execution_id"`
Status ExecutionStatus `json:"status"`
Output json.RawMessage `json:"output,omitempty"`
Error string `json:"error,omitempty"`
Duration time.Duration `json:"duration,omitempty"`
MemoryUsed int `json:"memory_used,omitempty"`
}
// DeployFunctionRequest represents a request to deploy a function
type DeployFunctionRequest struct {
FunctionID uuid.UUID `json:"function_id"`
Force bool `json:"force,omitempty"`
}
// DeployFunctionResponse represents a response for function deployment
type DeployFunctionResponse struct {
Status string `json:"status"`
Message string `json:"message,omitempty"`
Image string `json:"image,omitempty"`
ImageID string `json:"image_id,omitempty"`
}
// ListFunctionsResponse represents the response for listing functions
type ListFunctionsResponse struct {
Functions []FunctionDefinition `json:"functions"`
Limit int `json:"limit"`
Offset int `json:"offset"`
}
// ListExecutionsResponse represents the response for listing executions
type ListExecutionsResponse struct {
Executions []FunctionExecution `json:"executions"`
Limit int `json:"limit"`
Offset int `json:"offset"`
}
// GetLogsResponse represents the response for getting execution logs
type GetLogsResponse struct {
Logs []string `json:"logs"`
}
// GetRunningExecutionsResponse represents the response for getting running executions
type GetRunningExecutionsResponse struct {
Executions []FunctionExecution `json:"executions"`
Count int `json:"count"`
}