From 49c08947f798be85dad135db8c0c240f84f5bacb Mon Sep 17 00:00:00 2001 From: Han Kang Date: Wed, 12 Oct 2022 08:30:06 -0700 Subject: [PATCH 1/4] add support for parsing gauge func Change-Id: Id0b9cd51dead5ee9f4adac804d62f5d9742320a7 --- test/instrumentation/decode_metric.go | 6 +++--- .../testdata/pkg/kubelet/metrics/metrics.go | 14 ++++++++++++++ .../testdata/test-stable-metrics-list.yaml | 7 +++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/test/instrumentation/decode_metric.go b/test/instrumentation/decode_metric.go index 81bb878c4b3..4ceeda7b516 100644 --- a/test/instrumentation/decode_metric.go +++ b/test/instrumentation/decode_metric.go @@ -70,7 +70,7 @@ func (c *metricDecoder) decodeNewMetricCall(fc *ast.CallExpr) (*metric, error) { return nil, nil } switch functionName { - case "NewCounter", "NewGauge", "NewHistogram", "NewSummary", "NewTimingHistogram": + case "NewCounter", "NewGauge", "NewHistogram", "NewSummary", "NewTimingHistogram", "NewGaugeFunc": m, err = c.decodeMetric(fc) case "NewCounterVec", "NewGaugeVec", "NewHistogramVec", "NewSummaryVec", "NewTimingHistogramVec": m, err = c.decodeMetricVec(fc) @@ -90,7 +90,7 @@ func getMetricType(functionName string) string { switch functionName { case "NewCounter", "NewCounterVec": return counterMetricType - case "NewGauge", "NewGaugeVec": + case "NewGauge", "NewGaugeVec", "NewGaugeFunc": return gaugeMetricType case "NewHistogram", "NewHistogramVec": return histogramMetricType @@ -104,7 +104,7 @@ func getMetricType(functionName string) string { } func (c *metricDecoder) decodeMetric(call *ast.CallExpr) (metric, error) { - if len(call.Args) != 1 { + if len(call.Args) > 2 { return metric{}, newDecodeErrorf(call, errInvalidNewMetricCall) } return c.decodeOpts(call.Args[0]) diff --git a/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go b/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go index 45bbe077938..da29941fb30 100644 --- a/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go +++ b/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go @@ -481,6 +481,20 @@ func Register(collectors ...metrics.StableCollector) { for _, collector := range collectors { legacyregistry.CustomMustRegister(collector) } + legacyregistry.RawMustRegister(metrics.NewGaugeFunc( + &metrics.GaugeOpts{ + Subsystem: "kubelet", + Name: "certificate_manager_client_ttl_seconds", + Help: "Gauge of the TTL (time-to-live) of the Kubelet's client certificate. " + + "The value is in seconds until certificate expiry (negative if already expired). " + + "If client certificate is invalid or unused, the value will be +INF.", + StabilityLevel: metrics.BETA, + }, + func() float64 { + return 0 + }, + )) + }) } diff --git a/test/instrumentation/testdata/test-stable-metrics-list.yaml b/test/instrumentation/testdata/test-stable-metrics-list.yaml index 4fbe7ee07c1..668daa62d7b 100644 --- a/test/instrumentation/testdata/test-stable-metrics-list.yaml +++ b/test/instrumentation/testdata/test-stable-metrics-list.yaml @@ -1,3 +1,10 @@ +- name: certificate_manager_client_ttl_seconds + subsystem: kubelet + help: Gauge of the TTL (time-to-live) of the Kubelet's client certificate. The value + is in seconds until certificate expiry (negative if already expired). If client + certificate is invalid or unused, the value will be +INF. + type: Gauge + stabilityLevel: BETA - name: device_plugin_alloc_duration_seconds subsystem: kubelet help: Duration in seconds to serve a device plugin Allocation request. Broken down From 0e7814a64795b8a50cb8c5038ae795c613b9d071 Mon Sep 17 00:00:00 2001 From: Han Kang Date: Wed, 12 Oct 2022 09:25:43 -0700 Subject: [PATCH 2/4] fix parsing error on labels Change-Id: I990967b93b10dbfa9a564ca4286ffbd051c69697 --- test/instrumentation/find_stable_metric.go | 3 ++- .../testdata/pkg/kubelet/metrics/metrics.go | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/instrumentation/find_stable_metric.go b/test/instrumentation/find_stable_metric.go index 03605017946..41d3b23a610 100644 --- a/test/instrumentation/find_stable_metric.go +++ b/test/instrumentation/find_stable_metric.go @@ -29,6 +29,7 @@ var metricsOptionStructuresNames = []string{ "GaugeOpts", "HistogramOpts", "SummaryOpts", + "TimingHistogramOpts", } func findStableMetricDeclaration(tree ast.Node, metricsImportName string) ([]*ast.CallExpr, []error) { @@ -98,7 +99,7 @@ func (f *stableMetricFinder) Visit(node ast.Node) (w ast.Visitor) { func isMetricOps(name string) bool { var found = false for _, optsName := range metricsOptionStructuresNames { - if name != optsName { + if name == optsName { found = true break } diff --git a/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go b/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go index da29941fb30..1ebd1acac04 100644 --- a/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go +++ b/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go @@ -494,7 +494,13 @@ func Register(collectors ...metrics.StableCollector) { return 0 }, )) - + _ = metrics.Labels{ + "probe_type": "1", + "container": "2", + "pod": "podName", + "namespace": "space", + "pod_uid": "123", + } }) } From 658d7a184e07460c78fc5acb843e83f7148b2efd Mon Sep 17 00:00:00 2001 From: Han Kang Date: Wed, 12 Oct 2022 09:33:43 -0700 Subject: [PATCH 3/4] parse time signatures for maxAge Change-Id: I91e330d82c4ebbfa38bc52889beb64e6689bfb77 --- test/instrumentation/decode_metric.go | 56 ++++++++++++++++++- .../testdata/pkg/kubelet/metrics/metrics.go | 25 +++++++++ .../testdata/test-stable-metrics-list.yaml | 14 +++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/test/instrumentation/decode_metric.go b/test/instrumentation/decode_metric.go index 4ceeda7b516..c7e1f364148 100644 --- a/test/instrumentation/decode_metric.go +++ b/test/instrumentation/decode_metric.go @@ -24,6 +24,7 @@ import ( "sort" "strconv" "strings" + "time" "k8s.io/component-base/metrics" ) @@ -326,7 +327,6 @@ func (c *metricDecoder) decodeOpts(expr ast.Expr) (metric, error) { case "MaxAge": int64Val, err := c.decodeInt64(kv.Value) if err != nil { - print(key) return m, err } m.MaxAge = int64Val @@ -443,6 +443,7 @@ func (c *metricDecoder) decodeUint32(expr ast.Expr) (uint32, error) { func (c *metricDecoder) decodeInt64(expr ast.Expr) (int64, error) { switch v := expr.(type) { case *ast.BasicLit: + println("BasicLit") if v.Kind != token.FLOAT && v.Kind != token.INT { print(v.Kind) } @@ -453,8 +454,10 @@ func (c *metricDecoder) decodeInt64(expr ast.Expr) (int64, error) { } return value, nil case *ast.SelectorExpr: + println("SelectorExpr") variableName := v.Sel.String() importName, ok := v.X.(*ast.Ident) + println(variableName) if ok && importName.String() == c.kubeMetricsImportName { if variableName == "DefMaxAge" { // hardcode this for now. This is a duration but we'll output it as @@ -462,16 +465,67 @@ func (c *metricDecoder) decodeInt64(expr ast.Expr) (int64, error) { return 1000 * 1000 * 1000 * 60 * 10, nil } } + case *ast.Ident: + variableExpr, found := c.variables[v.Name] + if found { + be, ok := variableExpr.(*ast.BinaryExpr) + if ok { + i, err2, done := c.extractTimeExpression(be) + if done { + return i, err2 + } + } + + } + case *ast.CallExpr: + println("CallExpr") _, ok := v.Fun.(*ast.SelectorExpr) if !ok { return 0, newDecodeErrorf(v, errDecodeInt64) } return 0, nil + case *ast.BinaryExpr: + + i, err2, done := c.extractTimeExpression(v) + if done { + return i, err2 + } + } return 0, newDecodeErrorf(expr, errDecodeInt64) } +func (c *metricDecoder) extractTimeExpression(v *ast.BinaryExpr) (int64, error, bool) { + x := v.X.(*ast.BasicLit) + if x.Kind != token.FLOAT && x.Kind != token.INT { + print(x.Kind) + } + + xValue, err := strconv.ParseInt(x.Value, 10, 64) + if err != nil { + return 0, err, true + } + + switch y := v.Y.(type) { + case *ast.SelectorExpr: + variableName := y.Sel.String() + importName, ok := y.X.(*ast.Ident) + if ok && importName.String() == "time" { + if variableName == "Hour" { + return xValue * int64(time.Hour), nil, true + } + if variableName == "Minute" { + return xValue * int64(time.Minute), nil, true + } + if variableName == "Second" { + return xValue * int64(time.Second), nil, true + } + } + } + return 0, nil, false +} + func decodeFloatMap(exprs []ast.Expr) (map[float64]float64, error) { buckets := map[float64]float64{} for _, elt := range exprs { diff --git a/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go b/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go index 1ebd1acac04..5a8181c6ab5 100644 --- a/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go +++ b/test/instrumentation/testdata/pkg/kubelet/metrics/metrics.go @@ -77,6 +77,8 @@ var ( defObjectives = map[float64]float64{0.5: 0.5, 0.75: 0.75} testBuckets = []float64{0, 0.5, 1.0} testLabels = []string{"a", "b", "c"} + maxAge = 2 * time.Minute + // NodeName is a Gauge that tracks the ode's name. The count is always 1. NodeName = metrics.NewGaugeVec( &metrics.GaugeOpts{ @@ -109,6 +111,29 @@ var ( }, testLabels, ) + // PodWorkerDuration is a Histogram that tracks the duration (in seconds) in takes to sync a single pod. + // Broken down by the operation type. + SummaryMaxAge = metrics.NewSummary( + &metrics.SummaryOpts{ + Subsystem: KubeletSubsystem, + Name: "max_age", + Help: "Duration in seconds to sync a single pod. Broken down by operation type: create, update, or sync", + StabilityLevel: metrics.BETA, + MaxAge: 2 * time.Hour, + }, + ) + + // PodWorkerDuration is a Histogram that tracks the duration (in seconds) in takes to sync a single pod. + // Broken down by the operation type. + SummaryMaxAgeConst = metrics.NewSummary( + &metrics.SummaryOpts{ + Subsystem: KubeletSubsystem, + Name: "max_age_const", + Help: "Duration in seconds to sync a single pod. Broken down by operation type: create, update, or sync", + StabilityLevel: metrics.BETA, + MaxAge: maxAge, + }, + ) // PodStartDuration is a Histogram that tracks the duration (in seconds) it takes for a single pod to go from pending to running. PodStartDuration = metrics.NewHistogram( &metrics.HistogramOpts{ diff --git a/test/instrumentation/testdata/test-stable-metrics-list.yaml b/test/instrumentation/testdata/test-stable-metrics-list.yaml index 668daa62d7b..d6cc66b59aa 100644 --- a/test/instrumentation/testdata/test-stable-metrics-list.yaml +++ b/test/instrumentation/testdata/test-stable-metrics-list.yaml @@ -25,6 +25,20 @@ - 2.5 - 5 - 10 +- name: max_age + subsystem: kubelet + help: 'Duration in seconds to sync a single pod. Broken down by operation type: + create, update, or sync' + type: Summary + stabilityLevel: BETA + maxAge: 7200000000000 +- name: max_age_const + subsystem: kubelet + help: 'Duration in seconds to sync a single pod. Broken down by operation type: + create, update, or sync' + type: Summary + stabilityLevel: BETA + maxAge: 120000000000 - name: multiline subsystem: kubelet help: Cumulative number of pod preemptions by preemption resource asdf asdf asdf From f3cb90461830e96f495d8e1313767f140dea513d Mon Sep 17 00:00:00 2001 From: Han Kang Date: Wed, 12 Oct 2022 09:47:49 -0700 Subject: [PATCH 4/4] cleanup printlns Change-Id: I49a48446029ba2e66b09f138a1477b837d55766a --- test/instrumentation/decode_metric.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/instrumentation/decode_metric.go b/test/instrumentation/decode_metric.go index c7e1f364148..2100e3cd6b3 100644 --- a/test/instrumentation/decode_metric.go +++ b/test/instrumentation/decode_metric.go @@ -443,7 +443,6 @@ func (c *metricDecoder) decodeUint32(expr ast.Expr) (uint32, error) { func (c *metricDecoder) decodeInt64(expr ast.Expr) (int64, error) { switch v := expr.(type) { case *ast.BasicLit: - println("BasicLit") if v.Kind != token.FLOAT && v.Kind != token.INT { print(v.Kind) } @@ -454,10 +453,8 @@ func (c *metricDecoder) decodeInt64(expr ast.Expr) (int64, error) { } return value, nil case *ast.SelectorExpr: - println("SelectorExpr") variableName := v.Sel.String() importName, ok := v.X.(*ast.Ident) - println(variableName) if ok && importName.String() == c.kubeMetricsImportName { if variableName == "DefMaxAge" { // hardcode this for now. This is a duration but we'll output it as @@ -479,19 +476,16 @@ func (c *metricDecoder) decodeInt64(expr ast.Expr) (int64, error) { } case *ast.CallExpr: - println("CallExpr") _, ok := v.Fun.(*ast.SelectorExpr) if !ok { return 0, newDecodeErrorf(v, errDecodeInt64) } return 0, nil case *ast.BinaryExpr: - i, err2, done := c.extractTimeExpression(v) if done { return i, err2 } - } return 0, newDecodeErrorf(expr, errDecodeInt64) }