From bf193b984505903e821919c468c8be5009ac1116 Mon Sep 17 00:00:00 2001 From: Onur Solmaz Date: Mon, 30 Mar 2026 23:58:27 +0200 Subject: [PATCH] fix(runtime): include instance creation time in runtime bindings --- api/runtime_bindings.go | 7 +++++++ api/runtime_bindings_test.go | 22 ++++++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/api/runtime_bindings.go b/api/runtime_bindings.go index 492eb45..9a913a9 100644 --- a/api/runtime_bindings.go +++ b/api/runtime_bindings.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "strings" + "time" "github.com/labstack/echo/v4" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -25,6 +26,7 @@ type runtimeBindingRuntimePrincipal struct { type runtimeBindingResponse struct { InstanceID string `json:"instanceId"` Namespace string `json:"namespace"` + CreatedAt string `json:"createdAt"` OwnerPrincipal runtimeBindingOwnerPrincipal `json:"ownerPrincipal"` RuntimePrincipal runtimeBindingRuntimePrincipal `json:"runtimePrincipal"` PresetID string `json:"presetId"` @@ -72,6 +74,10 @@ func buildRuntimeBindingResponse(spritz *spritzv1.Spritz) (runtimeBindingRespons if namespace == "" { return runtimeBindingResponse{}, fmt.Errorf("instance namespace is required") } + if spritz.CreationTimestamp.IsZero() { + return runtimeBindingResponse{}, fmt.Errorf("instance creation timestamp is required") + } + createdAt := spritz.CreationTimestamp.Time.UTC().Format(time.RFC3339Nano) ownerID := strings.TrimSpace(spritz.Spec.Owner.ID) if ownerID == "" { @@ -96,6 +102,7 @@ func buildRuntimeBindingResponse(spritz *spritzv1.Spritz) (runtimeBindingRespons return runtimeBindingResponse{ InstanceID: instanceID, Namespace: namespace, + CreatedAt: createdAt, OwnerPrincipal: runtimeBindingOwnerPrincipal{ ID: ownerID, Type: "user", diff --git a/api/runtime_bindings_test.go b/api/runtime_bindings_test.go index a9d6f3a..589c39d 100644 --- a/api/runtime_bindings_test.go +++ b/api/runtime_bindings_test.go @@ -5,6 +5,7 @@ import ( "net/http/httptest" "strings" "testing" + "time" "github.com/labstack/echo/v4" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -37,8 +38,9 @@ func newRuntimeBindingsTestServer(t *testing.T, objects ...*spritzv1.Spritz) *se func TestGetRuntimeBindingReturnsCanonicalFacts(t *testing.T) { spritz := &spritzv1.Spritz{ ObjectMeta: metav1.ObjectMeta{ - Name: "zeno-delta-breeze", - Namespace: "spritz-production", + Name: "zeno-delta-breeze", + Namespace: "spritz-production", + CreationTimestamp: metav1.NewTime(time.Date(2026, time.March, 30, 12, 34, 56, 0, time.UTC)), Annotations: map[string]string{ presetIDAnnotationKey: "zeno", instanceClassAnnotationKey: "personal-agent", @@ -73,6 +75,7 @@ func TestGetRuntimeBindingReturnsCanonicalFacts(t *testing.T) { `"serviceAccountName":"zeno-agent-abcd1234"`, `"presetId":"zeno"`, `"instanceClassId":"personal-agent"`, + `"createdAt":"2026-03-30T12:34:56Z"`, } for _, fragment := range expectedFragments { if !strings.Contains(rec.Body.String(), fragment) { @@ -84,8 +87,9 @@ func TestGetRuntimeBindingReturnsCanonicalFacts(t *testing.T) { func TestGetRuntimeBindingRejectsMissingServiceAccountName(t *testing.T) { spritz := &spritzv1.Spritz{ ObjectMeta: metav1.ObjectMeta{ - Name: "openclaw-morning-sky", - Namespace: "spritz-production", + Name: "openclaw-morning-sky", + Namespace: "spritz-production", + CreationTimestamp: metav1.NewTime(time.Date(2026, time.March, 30, 12, 34, 56, 0, time.UTC)), Annotations: map[string]string{ presetIDAnnotationKey: "openclaw", instanceClassAnnotationKey: "assistant-runtime", @@ -112,8 +116,9 @@ func TestGetRuntimeBindingRejectsMissingServiceAccountName(t *testing.T) { func TestGetRuntimeBindingRejectsIncompleteBinding(t *testing.T) { spritz := &spritzv1.Spritz{ ObjectMeta: metav1.ObjectMeta{ - Name: "zeno-delta-breeze", - Namespace: "spritz-production", + Name: "zeno-delta-breeze", + Namespace: "spritz-production", + CreationTimestamp: metav1.NewTime(time.Date(2026, time.March, 30, 12, 34, 56, 0, time.UTC)), Annotations: map[string]string{ presetIDAnnotationKey: "zeno", }, @@ -141,8 +146,9 @@ func TestGetRuntimeBindingRejectsIncompleteBinding(t *testing.T) { func TestGetRuntimeBindingRejectsNamespaceOutsideServerScope(t *testing.T) { spritz := &spritzv1.Spritz{ ObjectMeta: metav1.ObjectMeta{ - Name: "zeno-delta-breeze", - Namespace: "spritz-production", + Name: "zeno-delta-breeze", + Namespace: "spritz-production", + CreationTimestamp: metav1.NewTime(time.Date(2026, time.March, 30, 12, 34, 56, 0, time.UTC)), Annotations: map[string]string{ presetIDAnnotationKey: "zeno", instanceClassAnnotationKey: "personal-agent",