Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Circuit Breaker Module support #171

Merged
merged 1 commit into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api/v1alpha1/httpsedge_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ type HTTPSEdgeRouteSpec struct {
// +kubebuilder:validation:Required
Backend TunnelGroupBackend `json:"backend,omitempty"`

// CircuitBreaker is a circuit breaker configuration to apply to this route
CircuitBreaker *EndpointCircuitBreaker `json:"circuitBreaker,omitempty"`

// Compression is whether or not to enable compression for this route
Compression *EndpointCompression `json:"compression,omitempty"`

Expand Down
24 changes: 24 additions & 0 deletions api/v1alpha1/ngrok_common.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package v1alpha1

import "k8s.io/apimachinery/pkg/api/resource"

// common ngrok API/Dashboard fields
type ngrokAPICommon struct {
// Description is a human-readable description of the object in the ngrok API/Dashboard
Expand Down Expand Up @@ -71,3 +73,25 @@ type EndpointWebhookVerification struct {
// requests from the given provider. All providers except AWS SNS require a secret
SecretRef *SecretKeyRef `json:"secret,omitempty"`
}

type EndpointCircuitBreaker struct {
// Integer number of seconds after which the circuit is tripped to wait before
// re-evaluating upstream health
TrippedDuration uint32 `json:"trippedDuration,omitempty"`

// Integer number of seconds in the statistical rolling window that metrics are
// retained for.
RollingWindow uint32 `json:"rollingWindow,omitempty"`

// Integer number of buckets into which metrics are retained. Max 128.
//+kubebuilder:validation:Minimum=1
//+kubebuilder:validation:Maximum=128
NumBuckets uint32 `json:"numBuckets,omitempty"`

// Integer number of requests in a rolling window that will trip the circuit.
// Helpful if traffic volume is low.
VolumeThreshold uint32 `json:"volumeThreshold,omitempty"`

// Error threshold percentage should be between 0 - 1.0, not 0-100.0
ErrorThresholdPercentage resource.Quantity `json:"errorThresholdPercentage,omitempty"`
}
15 changes: 10 additions & 5 deletions api/v1alpha1/ngrokmoduleset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@ import (
)

type NgrokModuleSetModules struct {
// Compression configuration for this module
// CircuitBreaker configuration for this module set
CircuitBreaker *EndpointCircuitBreaker `json:"circuitBreaker,omitempty"`
// Compression configuration for this module set
Compression *EndpointCompression `json:"compression,omitempty"`
// Header configuration for this module
// Header configuration for this module set
Headers *EndpointHeaders `json:"headers,omitempty"`
// IPRestriction configuration for this module
// IPRestriction configuration for this module set
IPRestriction *EndpointIPPolicy `json:"ipRestriction,omitempty"`
// TLSTermination configuration for this module
// TLSTermination configuration for this module set
TLSTermination *EndpointTLSTerminationAtEdge `json:"tlsTermination,omitempty"`
// WebhookVerification configuration for this module
// WebhookVerification configuration for this module set
WebhookVerification *EndpointWebhookVerification `json:"webhookVerification,omitempty"`
}

Expand All @@ -60,6 +62,9 @@ func (ms *NgrokModuleSet) Merge(o *NgrokModuleSet) {
msmod := &ms.Modules
omod := o.Modules

if omod.CircuitBreaker != nil {
msmod.CircuitBreaker = omod.CircuitBreaker
}
if omod.Compression != nil {
msmod.Compression = omod.Compression
}
Expand Down
26 changes: 26 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions internal/controllers/httpsedge_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ type edgeRouteModuleUpdater struct {

func (u *edgeRouteModuleUpdater) updateModulesForRoute(ctx context.Context, route *ngrok.HTTPSEdgeRoute, routeSpec *ingressv1alpha1.HTTPSEdgeRouteSpec) error {
funcs := []func(context.Context, *ngrok.HTTPSEdgeRoute, *ingressv1alpha1.HTTPSEdgeRouteSpec) error{
u.setEdgeRouteCircuitBreaker,
u.setEdgeRouteCompression,
u.setEdgeRouteIPRestriction,
u.setEdgeRouteRequestHeaders,
Expand All @@ -462,6 +463,42 @@ func (u *edgeRouteModuleUpdater) edgeRouteItem(route *ngrok.HTTPSEdgeRoute) *ngr
}
}

func (u *edgeRouteModuleUpdater) setEdgeRouteCircuitBreaker(ctx context.Context, route *ngrok.HTTPSEdgeRoute, routeSpec *ingressv1alpha1.HTTPSEdgeRouteSpec) error {
circuitBreaker := routeSpec.CircuitBreaker

client := u.clientset.CircuitBreaker()

// Early return if nothing to be done
if circuitBreaker == nil {
if route.CircuitBreaker == nil {
u.log.Info("CircuitBreaker matches desired state, skipping update")
return nil
}

return client.Delete(ctx, u.edgeRouteItem(route))
}

module := ngrok.EndpointCircuitBreaker{
TrippedDuration: circuitBreaker.TrippedDuration,
RollingWindow: circuitBreaker.RollingWindow,
NumBuckets: circuitBreaker.NumBuckets,
VolumeThreshold: circuitBreaker.VolumeThreshold,
ErrorThresholdPercentage: circuitBreaker.ErrorThresholdPercentage.AsApproximateFloat64(),
}

if reflect.DeepEqual(module, route.CircuitBreaker) {
u.log.Info("CircuitBreaker matches desired state, skipping update")
return nil
}

_, err := client.Replace(ctx, &ngrok.EdgeRouteCircuitBreakerReplace{
EdgeID: route.EdgeID,
ID: route.ID,
Module: module,
})
return err
}

func (u *edgeRouteModuleUpdater) setEdgeRouteCompression(ctx context.Context, route *ngrok.HTTPSEdgeRoute, routeSpec *ingressv1alpha1.HTTPSEdgeRouteSpec) error {
compression := routeSpec.Compression

Expand Down
1 change: 1 addition & 0 deletions internal/store/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ func (d *Driver) calculateHTTPSEdges() []ingressv1alpha1.HTTPSEdge {
Backend: ingressv1alpha1.TunnelGroupBackend{
Labels: backendToLabelMap(httpIngressPath.Backend, ingress.Namespace),
},
CircuitBreaker: modSet.Modules.CircuitBreaker,
Compression: modSet.Modules.Compression,
IPRestriction: modSet.Modules.IPRestriction,
Headers: modSet.Modules.Headers,
Expand Down