Skip to content

Commit d388234

Browse files
committed
feat: add custom metric to track Sandbox creation time latency
1 parent e568f6c commit d388234

File tree

4 files changed

+77
-5
lines changed

4 files changed

+77
-5
lines changed

cmd/agent-sandbox-controller/main.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@ import (
2222
// to ensure that exec-entrypoint and run can make use of them.
2323
_ "k8s.io/client-go/plugin/pkg/client/auth"
2424

25+
"github.com/prometheus/client_golang/prometheus"
2526
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2627
ctrl "sigs.k8s.io/controller-runtime"
2728
"sigs.k8s.io/controller-runtime/pkg/healthz"
2829
"sigs.k8s.io/controller-runtime/pkg/log/zap"
30+
"sigs.k8s.io/controller-runtime/pkg/metrics"
31+
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
2932

3033
"sigs.k8s.io/agent-sandbox/controllers"
3134
extensionsv1alpha1 "sigs.k8s.io/agent-sandbox/extensions/api/v1alpha1"
@@ -35,8 +38,21 @@ import (
3538

3639
var (
3740
setupLog = ctrl.Log.WithName("setup")
41+
// sandboxCreationLatency measures the time it takes for a sandbox to be created and ready.
42+
sandboxCreationLatency = prometheus.NewHistogram(
43+
prometheus.HistogramOpts{
44+
Name: "sandbox_creation_latency_seconds",
45+
Help: "Time it takes for a sandbox to be created and ready.",
46+
Buckets: []float64{1, 2, 5, 10, 15, 20, 30, 60},
47+
},
48+
)
3849
)
3950

51+
func init() {
52+
// Register custom metrics with the global prometheus registry
53+
metrics.Registry.MustRegister(sandboxCreationLatency)
54+
}
55+
4056
func main() {
4157
var metricsAddr string
4258
var enableLeaderElection bool
@@ -62,7 +78,10 @@ func main() {
6278
}
6379

6480
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
65-
Scheme: scheme,
81+
Scheme: scheme,
82+
Metrics: metricsserver.Options{
83+
BindAddress: metricsAddr,
84+
},
6685
HealthProbeBindAddress: probeAddr,
6786
LeaderElection: enableLeaderElection,
6887
LeaderElectionID: "a3317529.x-k8s.io",
@@ -73,8 +92,9 @@ func main() {
7392
}
7493

7594
if err = (&controllers.SandboxReconciler{
76-
Client: mgr.GetClient(),
77-
Scheme: mgr.GetScheme(),
95+
Client: mgr.GetClient(),
96+
Scheme: mgr.GetScheme(),
97+
SandboxCreationLatency: sandboxCreationLatency,
7898
}).SetupWithManager(mgr); err != nil {
7999
setupLog.Error(err, "unable to create controller", "controller", "Sandbox")
80100
os.Exit(1)

controllers/sandbox_controller.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"reflect"
2323
"time"
2424

25+
"github.com/prometheus/client_golang/prometheus"
2526
corev1 "k8s.io/api/core/v1"
2627
k8serrors "k8s.io/apimachinery/pkg/api/errors"
2728
"k8s.io/apimachinery/pkg/api/meta"
@@ -58,7 +59,8 @@ func init() {
5859
// SandboxReconciler reconciles a Sandbox object
5960
type SandboxReconciler struct {
6061
client.Client
61-
Scheme *runtime.Scheme
62+
Scheme *runtime.Scheme
63+
SandboxCreationLatency prometheus.Histogram
6264
}
6365

6466
//+kubebuilder:rbac:groups=agents.x-k8s.io,resources=sandboxes,verbs=get;list;watch;create;update;patch;delete
@@ -225,6 +227,16 @@ func (r *SandboxReconciler) updateStatus(ctx context.Context, oldStatus *sandbox
225227
return err
226228
}
227229

230+
// If the sandbox is ready, record the creation latency.
231+
if meta.IsStatusConditionTrue(sandbox.Status.Conditions, string(sandboxv1alpha1.SandboxConditionReady)) {
232+
// Check if the old status was not ready.
233+
if !meta.IsStatusConditionTrue(oldStatus.Conditions, string(sandboxv1alpha1.SandboxConditionReady)) {
234+
latency := time.Since(sandbox.CreationTimestamp.Time).Seconds()
235+
r.SandboxCreationLatency.Observe(latency)
236+
log.Info("sandbox is ready, recorded creation latency", "latency", latency)
237+
}
238+
}
239+
228240
// Surface error
229241
return nil
230242
}

controllers/sandbox_controller_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121

2222
"github.com/google/go-cmp/cmp"
2323
"github.com/google/go-cmp/cmp/cmpopts"
24+
"github.com/prometheus/client_golang/prometheus"
25+
"github.com/prometheus/client_golang/prometheus/testutil"
2426
"github.com/stretchr/testify/require"
2527
corev1 "k8s.io/api/core/v1"
2628
k8serrors "k8s.io/apimachinery/pkg/api/errors"
@@ -649,3 +651,40 @@ func TestSandboxExpiry(t *testing.T) {
649651
})
650652
}
651653
}
654+
655+
func TestRecordCreationLatency(t *testing.T) {
656+
sandboxName := "sandbox-name"
657+
sandboxNs := "sandbox-ns"
658+
creationLatencyMetric := prometheus.NewHistogram(
659+
prometheus.HistogramOpts{
660+
Name: "sandbox_creation_latency_seconds",
661+
Help: "Time it takes for a sandbox to be created and ready.",
662+
Buckets: []float64{1, 2, 5, 10, 15, 20, 30, 60},
663+
},
664+
)
665+
666+
sb := &sandboxv1alpha1.Sandbox{}
667+
sb.Name = sandboxName
668+
sb.Namespace = sandboxNs
669+
sb.Generation = 1
670+
sb.CreationTimestamp = metav1.NewTime(time.Now().Add(-10 * time.Second))
671+
672+
r := SandboxReconciler{
673+
Client: newFakeClient(sb),
674+
Scheme: Scheme,
675+
SandboxCreationLatency: creationLatencyMetric,
676+
}
677+
678+
oldStatus := &sandboxv1alpha1.SandboxStatus{}
679+
sb.Status.Conditions = []metav1.Condition{
680+
{
681+
Type: string(sandboxv1alpha1.SandboxConditionReady),
682+
Status: metav1.ConditionTrue,
683+
},
684+
}
685+
686+
err := r.updateStatus(t.Context(), oldStatus, sb)
687+
require.NoError(t, err)
688+
689+
require.Equal(t, 1, testutil.CollectAndCount(creationLatencyMetric))
690+
}

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.24.4
44

55
require (
66
github.com/google/go-cmp v0.7.0
7+
github.com/prometheus/client_golang v1.23.2
78
github.com/stretchr/testify v1.11.1
89
k8s.io/api v0.34.1
910
k8s.io/apiextensions-apiserver v0.34.1
@@ -43,13 +44,13 @@ require (
4344
github.com/google/gnostic-models v0.7.0 // indirect
4445
github.com/google/uuid v1.6.0 // indirect
4546
github.com/json-iterator/go v1.1.12 // indirect
47+
github.com/kylelemons/godebug v1.1.0 // indirect
4648
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
4749
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
4850
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
4951
github.com/onsi/ginkgo/v2 v2.23.3 // indirect
5052
github.com/onsi/gomega v1.37.0 // indirect
5153
github.com/pmezard/go-difflib v1.0.0 // indirect
52-
github.com/prometheus/client_golang v1.23.2 // indirect
5354
github.com/prometheus/client_model v0.6.2 // indirect
5455
github.com/prometheus/common v0.67.1 // indirect
5556
github.com/prometheus/procfs v0.17.0 // indirect

0 commit comments

Comments
 (0)