Skip to content

Commit

Permalink
feat: add metrics for konnect entity operations
Browse files Browse the repository at this point in the history
  • Loading branch information
randmonkey committed Dec 24, 2024
1 parent dd397cf commit 29675f9
Show file tree
Hide file tree
Showing 7 changed files with 349 additions and 10 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@
and preview deployments are now customized with Konnect-related settings.
[#910](https://github.com/Kong/gateway-operator/pull/910)

### Added

- Added prometheus metrics for Konnect entity operations in the metrics server:
- `gateway_operator_konnect_entity_operation_count` for number of operations
- `gateway_operator_konnect_entity_operation_duration` for duration of operations
[#953](https://github.com/Kong/gateway-operator/pull/953)

## [v1.4.1]

> Release date: 2024-11-28
Expand Down
157 changes: 154 additions & 3 deletions controller/konnect/ops/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/kong/gateway-operator/controller/konnect/constraints"
sdkops "github.com/kong/gateway-operator/controller/konnect/ops/sdk"
"github.com/kong/gateway-operator/controller/pkg/log"
"github.com/kong/gateway-operator/internal/metrics"
"github.com/kong/gateway-operator/pkg/consts"
k8sutils "github.com/kong/gateway-operator/pkg/utils/kubernetes"

Expand All @@ -37,6 +38,35 @@ const (
DeleteOp Op = "delete"
)

// EntityTypeName is the type of the Konnect entity name used for distinguish operations on different types of entities in the prometheus metrics.
type EntityTypeName string

// Entity type names for Konnect entity as labels in metrics.
const (
// Entity type names used in metrics.
// REVIEW: Should we use the path inside the API as the type names? These are not very consistent in
EntityTypeControlPlane EntityTypeName = "control_planes"
EntityTypeService EntityTypeName = "services"
EntityTypeRoute EntityTypeName = "routes"
EntityTypeConsumer EntityTypeName = "consumers"
EntityTypeConsumerGroup EntityTypeName = "consumer_groups"
EntityTypePlugin EntityTypeName = "plugins"
EntityTypeUpstream EntityTypeName = "upstreams"
EntityTypeTarget EntityTypeName = "targets"
EntityTypeBasicAuthCredential EntityTypeName = "basic_auth_credentials" //nolint:gosec
EntityTypeAPIKeyCredential EntityTypeName = "api_key_credentials" //nolint:gosec
EntityTypeACLCredential EntityTypeName = "acl_credentials"
EntityTypeJWTCredential EntityTypeName = "jwt_credentials"
EntityTypeHMACCredential EntityTypeName = "hmac_credentials"
EntityTypeCACertificate EntityTypeName = "ca_certificates"
EntityTypeCertificate EntityTypeName = "certificates"
EntityTypeSNI EntityTypeName = "snis"
EntityTypeKey EntityTypeName = "keys"
EntityTypeKeySet EntityTypeName = "key_sets"
EntityTypeVault EntityTypeName = "vaults"
EntityTypeDataPlaneCertificate EntityTypeName = "data_plane_certificates"
)

// Create creates a Konnect entity.
func Create[
T constraints.SupportedKonnectEntityType,
Expand All @@ -45,52 +75,76 @@ func Create[
ctx context.Context,
sdk sdkops.SDKWrapper,
cl client.Client,
metricRecorder metrics.Recorder,
e TEnt,
) (*T, error) {
var (
err error
start = time.Now()

entityType EntityTypeName
statusCode int
)
switch ent := any(e).(type) {
case *konnectv1alpha1.KonnectGatewayControlPlane:
entityType = EntityTypeControlPlane
err = createControlPlane(ctx, sdk.GetControlPlaneSDK(), sdk.GetControlPlaneGroupSDK(), cl, ent)
case *configurationv1alpha1.KongService:
entityType = EntityTypeService
err = createService(ctx, sdk.GetServicesSDK(), ent)
case *configurationv1alpha1.KongRoute:
entityType = EntityTypeRoute
err = createRoute(ctx, sdk.GetRoutesSDK(), ent)
case *configurationv1.KongConsumer:
entityType = EntityTypeConsumer
err = createConsumer(ctx, sdk.GetConsumersSDK(), sdk.GetConsumerGroupsSDK(), cl, ent)
case *configurationv1beta1.KongConsumerGroup:
entityType = EntityTypeConsumerGroup
err = createConsumerGroup(ctx, sdk.GetConsumerGroupsSDK(), ent)
case *configurationv1alpha1.KongPluginBinding:
entityType = EntityTypePlugin
err = createPlugin(ctx, cl, sdk.GetPluginSDK(), ent)
case *configurationv1alpha1.KongUpstream:
entityType = EntityTypeUpstream
err = createUpstream(ctx, sdk.GetUpstreamsSDK(), ent)
case *configurationv1alpha1.KongCredentialBasicAuth:
entityType = EntityTypeBasicAuthCredential
err = createKongCredentialBasicAuth(ctx, sdk.GetBasicAuthCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialAPIKey:
entityType = EntityTypeAPIKeyCredential
err = createKongCredentialAPIKey(ctx, sdk.GetAPIKeyCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialACL:
entityType = EntityTypeACLCredential
err = createKongCredentialACL(ctx, sdk.GetACLCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialJWT:
entityType = EntityTypeJWTCredential
err = createKongCredentialJWT(ctx, sdk.GetJWTCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialHMAC:
entityType = EntityTypeHMACCredential
err = createKongCredentialHMAC(ctx, sdk.GetHMACCredentialsSDK(), ent)
case *configurationv1alpha1.KongCACertificate:
entityType = EntityTypeCACertificate
err = createCACertificate(ctx, sdk.GetCACertificatesSDK(), ent)
case *configurationv1alpha1.KongCertificate:
entityType = EntityTypeCertificate
err = createCertificate(ctx, sdk.GetCertificatesSDK(), ent)
case *configurationv1alpha1.KongTarget:
entityType = EntityTypeTarget
err = createTarget(ctx, sdk.GetTargetsSDK(), ent)
case *configurationv1alpha1.KongVault:
entityType = EntityTypeVault
err = createVault(ctx, sdk.GetVaultSDK(), ent)
case *configurationv1alpha1.KongKey:
entityType = EntityTypeKey
err = createKey(ctx, sdk.GetKeysSDK(), ent)
case *configurationv1alpha1.KongKeySet:
entityType = EntityTypeKeySet
err = createKeySet(ctx, sdk.GetKeySetsSDK(), ent)
case *configurationv1alpha1.KongSNI:
entityType = EntityTypeSNI
err = createSNI(ctx, sdk.GetSNIsSDK(), ent)
case *configurationv1alpha1.KongDataPlaneClientCertificate:
entityType = EntityTypeDataPlaneCertificate
err = createKongDataPlaneClientCertificate(ctx, sdk.GetDataPlaneCertificatesSDK(), ent)
// ---------------------------------------------------------------------
// TODO: add other Konnect types
Expand Down Expand Up @@ -162,6 +216,7 @@ func Create[
}

case errors.As(err, &errSDK):
statusCode = errSDK.StatusCode
SetKonnectEntityProgrammedConditionFalse(e, consts.KonnectEntitiesFailedToCreateReason, errSDK.Error())
case errors.As(err, &errRelationsFailed):
e.SetKonnectID(errRelationsFailed.KonnectID)
Expand All @@ -172,6 +227,20 @@ func Create[
SetKonnectEntityProgrammedCondition(e)
}

if err != nil {
metricRecorder.RecordKonnectEntityOperationFailure(
metrics.KonnectEntityOperationCreate,
string(entityType),
time.Since(start),
statusCode,
)
} else {
metricRecorder.RecordKonnectEntityOperationSuccess(
metrics.KonnectEntityOperationCreate,
string(entityType),
time.Since(start),
)
}
logOpComplete(ctx, start, CreateOp, e, err)

return e, IgnoreUnrecoverableAPIErr(err, loggerForEntity(ctx, e, CreateOp))
Expand All @@ -182,7 +251,7 @@ func Create[
func Delete[
T constraints.SupportedKonnectEntityType,
TEnt constraints.EntityType[T],
](ctx context.Context, sdk sdkops.SDKWrapper, cl client.Client, ent TEnt) error {
](ctx context.Context, sdk sdkops.SDKWrapper, cl client.Client, metricRecorder metrics.Recorder, ent TEnt) error {
if ent.GetKonnectStatus().GetKonnectID() == "" {
cond, ok := k8sutils.GetCondition(konnectv1alpha1.KonnectEntityProgrammedConditionType, ent)
if ok && cond.Status == metav1.ConditionTrue {
Expand All @@ -197,54 +266,95 @@ func Delete[
var (
err error
start = time.Now()

entityType EntityTypeName
statusCode int
)
switch ent := any(ent).(type) {
case *konnectv1alpha1.KonnectGatewayControlPlane:
entityType = EntityTypeControlPlane
err = deleteControlPlane(ctx, sdk.GetControlPlaneSDK(), ent)
case *configurationv1alpha1.KongService:
entityType = EntityTypeService
err = deleteService(ctx, sdk.GetServicesSDK(), ent)
case *configurationv1alpha1.KongRoute:
entityType = EntityTypeRoute
err = deleteRoute(ctx, sdk.GetRoutesSDK(), ent)
case *configurationv1.KongConsumer:
entityType = EntityTypeConsumer
err = deleteConsumer(ctx, sdk.GetConsumersSDK(), ent)
case *configurationv1beta1.KongConsumerGroup:
entityType = EntityTypeConsumerGroup
err = deleteConsumerGroup(ctx, sdk.GetConsumerGroupsSDK(), ent)
case *configurationv1alpha1.KongPluginBinding:
entityType = EntityTypePlugin
err = deletePlugin(ctx, sdk.GetPluginSDK(), ent)
case *configurationv1alpha1.KongUpstream:
entityType = EntityTypeUpstream
err = deleteUpstream(ctx, sdk.GetUpstreamsSDK(), ent)
case *configurationv1alpha1.KongCredentialBasicAuth:
entityType = EntityTypeBasicAuthCredential
err = deleteKongCredentialBasicAuth(ctx, sdk.GetBasicAuthCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialAPIKey:
entityType = EntityTypeAPIKeyCredential
err = deleteKongCredentialAPIKey(ctx, sdk.GetAPIKeyCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialACL:
entityType = EntityTypeACLCredential
err = deleteKongCredentialACL(ctx, sdk.GetACLCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialJWT:
entityType = EntityTypeJWTCredential
err = deleteKongCredentialJWT(ctx, sdk.GetJWTCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialHMAC:
entityType = EntityTypeHMACCredential
err = deleteKongCredentialHMAC(ctx, sdk.GetHMACCredentialsSDK(), ent)
case *configurationv1alpha1.KongCACertificate:
entityType = EntityTypeCACertificate
err = deleteCACertificate(ctx, sdk.GetCACertificatesSDK(), ent)
case *configurationv1alpha1.KongCertificate:
entityType = EntityTypeCertificate
err = deleteCertificate(ctx, sdk.GetCertificatesSDK(), ent)
case *configurationv1alpha1.KongTarget:
entityType = EntityTypeTarget
err = deleteTarget(ctx, sdk.GetTargetsSDK(), ent)
case *configurationv1alpha1.KongVault:
entityType = EntityTypeVault
err = deleteVault(ctx, sdk.GetVaultSDK(), ent)
case *configurationv1alpha1.KongKey:
entityType = EntityTypeKey
err = deleteKey(ctx, sdk.GetKeysSDK(), ent)
case *configurationv1alpha1.KongKeySet:
entityType = EntityTypeKeySet
err = deleteKeySet(ctx, sdk.GetKeySetsSDK(), ent)
case *configurationv1alpha1.KongSNI:
entityType = EntityTypeSNI
err = deleteSNI(ctx, sdk.GetSNIsSDK(), ent)
case *configurationv1alpha1.KongDataPlaneClientCertificate:
entityType = EntityTypeDataPlaneCertificate
err = deleteKongDataPlaneClientCertificate(ctx, sdk.GetDataPlaneCertificatesSDK(), ent)
// ---------------------------------------------------------------------
// TODO: add other Konnect types
default:
return fmt.Errorf("unsupported entity type %T", ent)
}

if err != nil {
var errSDK *sdkkonnecterrs.SDKError
if errors.As(err, &errSDK) {
statusCode = errSDK.StatusCode
}
metricRecorder.RecordKonnectEntityOperationFailure(
metrics.KonnectEntityOperationDelete,
string(entityType),
time.Since(start),
statusCode,
)
} else {
metricRecorder.RecordKonnectEntityOperationSuccess(
metrics.KonnectEntityOperationDelete,
string(entityType),
time.Since(start),
)
}
logOpComplete(ctx, start, DeleteOp, ent, err)

return err
Expand Down Expand Up @@ -297,6 +407,7 @@ func Update[
sdk sdkops.SDKWrapper,
syncPeriod time.Duration,
cl client.Client,
metricRecorder metrics.Recorder,
e TEnt,
) (ctrl.Result, error) {
now := time.Now()
Expand All @@ -312,45 +423,70 @@ func Update[
)
}

var err error
var (
err error

entityType EntityTypeName
statusCode int
start = time.Now()
)
switch ent := any(e).(type) {
case *konnectv1alpha1.KonnectGatewayControlPlane:
entityType = EntityTypeControlPlane
err = updateControlPlane(ctx, sdk.GetControlPlaneSDK(), sdk.GetControlPlaneGroupSDK(), cl, ent)
case *configurationv1alpha1.KongService:
entityType = EntityTypeService
err = updateService(ctx, sdk.GetServicesSDK(), ent)
case *configurationv1alpha1.KongRoute:
entityType = EntityTypeRoute
err = updateRoute(ctx, sdk.GetRoutesSDK(), ent)
case *configurationv1.KongConsumer:
entityType = EntityTypeConsumer
err = updateConsumer(ctx, sdk.GetConsumersSDK(), sdk.GetConsumerGroupsSDK(), cl, ent)
case *configurationv1beta1.KongConsumerGroup:
entityType = EntityTypeConsumerGroup
err = updateConsumerGroup(ctx, sdk.GetConsumerGroupsSDK(), ent)
case *configurationv1alpha1.KongPluginBinding:
entityType = EntityTypePlugin
err = updatePlugin(ctx, sdk.GetPluginSDK(), cl, ent)
case *configurationv1alpha1.KongUpstream:
entityType = EntityTypeUpstream
err = updateUpstream(ctx, sdk.GetUpstreamsSDK(), ent)
case *configurationv1alpha1.KongCredentialBasicAuth:
entityType = EntityTypeBasicAuthCredential
err = updateKongCredentialBasicAuth(ctx, sdk.GetBasicAuthCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialAPIKey:
entityType = EntityTypeAPIKeyCredential
err = updateKongCredentialAPIKey(ctx, sdk.GetAPIKeyCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialACL:
entityType = EntityTypeACLCredential
err = updateKongCredentialACL(ctx, sdk.GetACLCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialJWT:
entityType = EntityTypeJWTCredential
err = updateKongCredentialJWT(ctx, sdk.GetJWTCredentialsSDK(), ent)
case *configurationv1alpha1.KongCredentialHMAC:
entityType = EntityTypeJWTCredential
err = updateKongCredentialHMAC(ctx, sdk.GetHMACCredentialsSDK(), ent)
case *configurationv1alpha1.KongCACertificate:
entityType = EntityTypeCACertificate
err = updateCACertificate(ctx, sdk.GetCACertificatesSDK(), ent)
case *configurationv1alpha1.KongCertificate:
entityType = EntityTypeCertificate
err = updateCertificate(ctx, sdk.GetCertificatesSDK(), ent)
case *configurationv1alpha1.KongTarget:
entityType = EntityTypeTarget
err = updateTarget(ctx, sdk.GetTargetsSDK(), ent)
case *configurationv1alpha1.KongVault:
entityType = EntityTypeVault
err = updateVault(ctx, sdk.GetVaultSDK(), ent)
case *configurationv1alpha1.KongKey:
entityType = EntityTypeKey
err = updateKey(ctx, sdk.GetKeysSDK(), ent)
case *configurationv1alpha1.KongKeySet:
entityType = EntityTypeKeySet
err = updateKeySet(ctx, sdk.GetKeySetsSDK(), ent)
case *configurationv1alpha1.KongSNI:
entityType = EntityTypeSNI
err = updateSNI(ctx, sdk.GetSNIsSDK(), ent)
case *configurationv1alpha1.KongDataPlaneClientCertificate:
err = nil // DataPlaneCertificates are immutable.
Expand All @@ -367,6 +503,7 @@ func Update[
)
switch {
case errors.As(err, &errSDK):
statusCode = errSDK.StatusCode
SetKonnectEntityProgrammedConditionFalse(e, consts.KonnectEntitiesFailedToUpdateReason, errSDK.Body)
case errors.As(err, &errRelationsFailed):
e.SetKonnectID(errRelationsFailed.KonnectID)
Expand All @@ -377,7 +514,21 @@ func Update[
SetKonnectEntityProgrammedCondition(e)
}

logOpComplete(ctx, now, UpdateOp, e, err)
if err != nil {
metricRecorder.RecordKonnectEntityOperationFailure(
metrics.KonnectEnttiyOperationUpdate,
string(entityType),
time.Since(start),
statusCode,
)
} else {
metricRecorder.RecordKonnectEntityOperationSuccess(
metrics.KonnectEnttiyOperationUpdate,
string(entityType),
time.Since(start),
)
}
logOpComplete(ctx, start, UpdateOp, e, err)

return ctrl.Result{}, IgnoreUnrecoverableAPIErr(err, loggerForEntity(ctx, e, UpdateOp))
}
Expand Down
Loading

0 comments on commit 29675f9

Please sign in to comment.