Skip to content

Commit

Permalink
feat: add metrics instrumentations for status updater
Browse files Browse the repository at this point in the history
Signed-off-by: bitliu <bitliu@tencent.com>
  • Loading branch information
Xunzhuo committed Oct 31, 2023
1 parent 57e1aec commit 3fc9ff7
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 4 deletions.
19 changes: 19 additions & 0 deletions internal/status/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright Envoy Gateway Authors
// SPDX-License-Identifier: Apache-2.0
// The full text of the Apache license is available in the LICENSE file at
// the root of the repo.

package status

import "github.com/envoyproxy/gateway/internal/metrics"

var (
statusUpdateTotal = metrics.NewCounter("status_update_total", "Total number of status updates by object kind.")
statusUpdateFailed = metrics.NewCounter("status_update_failed_total", "Number of status updates that failed by object kind.")
statusUpdateConflict = metrics.NewCounter("status_update_conflict_total", "Number of status update conflicts encountered by object kind.")
statusUpdateSuccess = metrics.NewCounter("status_update_success_total", "Number of status updates that succeeded by object kind.")
statusUpdateNoop = metrics.NewCounter("status_update_noop_total", "Number of status updates that are no-ops by object kind. This is a subset of successful status updates.")
statusUpdateDurationSeconds = metrics.NewHistogram("status_update_duration_seconds", "How long a status update takes to finish.", []float64{0.001, 0.01, 0.1, 1, 5, 10})

kindLabel = metrics.NewLabel("kind")
)
70 changes: 66 additions & 4 deletions internal/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package status

import (
"context"
"time"

"github.com/go-logr/logr"
"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -73,9 +74,32 @@ func NewUpdateHandler(log logr.Logger, client client.Client) *UpdateHandler {
}

func (u *UpdateHandler) apply(update Update) {
if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
obj := update.Resource
var statusUpdateErr error
obj := update.Resource
objKind := kindOf(obj)

Check warning on line 79 in internal/status/status.go

View check run for this annotation

Codecov / codecov/patch

internal/status/status.go#L77-L79

Added lines #L77 - L79 were not covered by tests

startTime := time.Now()

statusUpdateTotal.With(kindLabel.Value(objKind)).Increment()

defer func() {
updateDuration := time.Since(startTime)
if statusUpdateErr != nil {
statusUpdateDurationSeconds.With(kindLabel.Value(objKind)).Record(updateDuration.Seconds())
statusUpdateFailed.With(kindLabel.Value(objKind)).Increment()
} else {
statusUpdateDurationSeconds.With(kindLabel.Value(objKind)).Record(updateDuration.Seconds())
statusUpdateSuccess.With(kindLabel.Value(objKind)).Increment()
}

Check warning on line 93 in internal/status/status.go

View check run for this annotation

Codecov / codecov/patch

internal/status/status.go#L81-L93

Added lines #L81 - L93 were not covered by tests
}()

if statusUpdateErr = retry.OnError(retry.DefaultBackoff, func(err error) bool {
if kerrors.IsConflict(err) {
statusUpdateConflict.With(kindLabel.Value(objKind)).Increment()
return true
}
return false
}, func() error {

Check warning on line 102 in internal/status/status.go

View check run for this annotation

Codecov / codecov/patch

internal/status/status.go#L96-L102

Added lines #L96 - L102 were not covered by tests
// Get the resource.
if err := u.client.Get(context.Background(), update.NamespacedName, obj); err != nil {
if kerrors.IsNotFound(err) {
Expand All @@ -90,14 +114,15 @@ func (u *UpdateHandler) apply(update Update) {
u.log.WithName(update.NamespacedName.Name).
WithName(update.NamespacedName.Namespace).
Info("status unchanged, bypassing update")
statusUpdateNoop.With(kindLabel.Value(objKind)).Increment()

Check warning on line 117 in internal/status/status.go

View check run for this annotation

Codecov / codecov/patch

internal/status/status.go#L117

Added line #L117 was not covered by tests
return nil
}

newObj.SetUID(obj.GetUID())

return u.client.Status().Update(context.Background(), newObj)
}); err != nil {
u.log.Error(err, "unable to update status", "name", update.NamespacedName.Name,
}); statusUpdateErr != nil {
u.log.Error(statusUpdateErr, "unable to update status", "name", update.NamespacedName.Name,

Check warning on line 125 in internal/status/status.go

View check run for this annotation

Codecov / codecov/patch

internal/status/status.go#L124-L125

Added lines #L124 - L125 were not covered by tests
"namespace", update.NamespacedName.Namespace)
}
}
Expand Down Expand Up @@ -169,6 +194,7 @@ func (u *UpdateWriter) Send(update Update) {
// GRPCRoute
// EnvoyPatchPolicy
// ClientTrafficPolicy
// SecurityPolicy
func isStatusEqual(objA, objB interface{}) bool {
opts := cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime")
switch a := objA.(type) {
Expand Down Expand Up @@ -232,7 +258,43 @@ func isStatusEqual(objA, objB interface{}) bool {
return true
}
}
case *egv1a1.SecurityPolicy:
if b, ok := objB.(*egv1a1.SecurityPolicy); ok {
if cmp.Equal(a.Status, b.Status, opts) {
return true
}

Check warning on line 265 in internal/status/status.go

View check run for this annotation

Codecov / codecov/patch

internal/status/status.go#L261-L265

Added lines #L261 - L265 were not covered by tests
}
}

return false
}

// kindOf returns the kind string for the given Kubernetes object.
func kindOf(obj interface{}) string {
switch obj.(type) {
case *gwapiv1.GatewayClass:
return "GatewayClass"
case *gwapiv1.Gateway:
return "Gateway"
case *gwapiv1.HTTPRoute:
return "HTTPRoute"
case *gwapiv1a2.TLSRoute:
return "TLSRoute"
case *gwapiv1a2.TCPRoute:
return "TCPRoute"
case *gwapiv1a2.UDPRoute:
return "UDPRoute"
case *gwapiv1a2.GRPCRoute:
return "GRPCRoute"
case *egv1a1.EnvoyPatchPolicy:
return "EnvoyPatchPolicy"
case *egv1a1.ClientTrafficPolicy:
return "ClientTrafficPolicy"
case *egv1a1.BackendTrafficPolicy:
return "BackendTrafficPolicy"
case *egv1a1.SecurityPolicy:
return "SecurityPolicy"

Check warning on line 296 in internal/status/status.go

View check run for this annotation

Codecov / codecov/patch

internal/status/status.go#L273-L296

Added lines #L273 - L296 were not covered by tests
}

return ""

Check warning on line 299 in internal/status/status.go

View check run for this annotation

Codecov / codecov/patch

internal/status/status.go#L299

Added line #L299 was not covered by tests
}

0 comments on commit 3fc9ff7

Please sign in to comment.