feat(sessions): SandboxFleet integration — fleet controller primary, SandboxManager fallback
FleetClient calls tool-definition-controller:8788 /sessions/{id}/assign
as the primary sandbox create path. Falls back to direct k8s SandboxManager
if fleet controller is unreachable.
handleSandboxCreate:
1. fleet.Assign() → podSvcDns from pre-warmed fleet pod (fast, no wait)
2. fallback: sandbox.CreateOrResume() → dedicated pod (slow, 30s wait)
handleSandboxDelete: fleet.Release() + sandbox.Delete() both run (non-fatal).
updateSandboxSummary: extracted helper, updates session memory_entity with
pod_name / svc_dns / last_active_at.
NetworkPolicy: added port 8788 to poimen egress (fleet controller REST API).
Response adds fleet:true field when served by fleet.
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
// Package sessions — FleetClient calls the SandboxFleet controller REST API
|
||||
// to assign sessions to fleet pods. This is the primary sandbox path;
|
||||
// the raw-k8s SandboxManager is the fallback when the fleet controller
|
||||
// is unavailable (e.g. during fleet rollout).
|
||||
package sessions
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultFleetControllerURL = "http://tool-definition-controller.poimen.svc.cluster.local:8788"
|
||||
)
|
||||
|
||||
// FleetClient calls the SandboxFleet controller REST API.
|
||||
type FleetClient struct {
|
||||
baseURL string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// FleetAssignResult is returned by the controller assign endpoint.
|
||||
type FleetAssignResult struct {
|
||||
PodSvcDns string `json:"podSvcDns"`
|
||||
PodName string `json:"podName"`
|
||||
}
|
||||
|
||||
// NewFleetClient creates a FleetClient from env or default URL.
|
||||
// FLEET_CONTROLLER_URL — default: defaultFleetControllerURL
|
||||
func NewFleetClient() *FleetClient {
|
||||
url := os.Getenv("FLEET_CONTROLLER_URL")
|
||||
if url == "" {
|
||||
url = defaultFleetControllerURL
|
||||
}
|
||||
return &FleetClient{
|
||||
baseURL: url,
|
||||
client: &http.Client{Timeout: 35 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
// Assign asks the fleet controller to assign sessionID to a fleet pod.
|
||||
// Returns the pod's ClusterIP service DNS and pod name.
|
||||
func (f *FleetClient) Assign(ctx context.Context, sessionID string) (*FleetAssignResult, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
|
||||
f.baseURL+"/sessions/"+sessionID+"/assign", nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("build request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := f.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fleet controller unreachable: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 300 {
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 512))
|
||||
return nil, fmt.Errorf("fleet controller returned %d: %s", resp.StatusCode, body)
|
||||
}
|
||||
|
||||
var result FleetAssignResult
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
return nil, fmt.Errorf("decode: %w", err)
|
||||
}
|
||||
if result.PodSvcDns == "" {
|
||||
return nil, fmt.Errorf("fleet controller returned empty podSvcDns")
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// Release notifies the fleet controller that a session has ended.
|
||||
// Cleans up the per-session ServiceAccount (audit trail SA).
|
||||
// Non-fatal: fleet controller handles this async.
|
||||
func (f *FleetClient) Release(ctx context.Context, sessionID string) error {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete,
|
||||
f.baseURL+"/sessions/"+sessionID, bytes.NewReader(nil))
|
||||
if err != nil {
|
||||
return fmt.Errorf("build request: %w", err)
|
||||
}
|
||||
resp, err := f.client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fleet controller unreachable: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= 300 {
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 512))
|
||||
return fmt.Errorf("fleet controller returned %d: %s", resp.StatusCode, body)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user