Skip to content

Commit

Permalink
Add ngrok enriched errors
Browse files Browse the repository at this point in the history
  • Loading branch information
hjkatz committed Dec 17, 2024
1 parent f7dd866 commit 3707f6d
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 23 deletions.
1 change: 0 additions & 1 deletion api/bindings/v1alpha1/boundendpoint_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ type BindingEndpoint struct {
// ErrorCode is the ngrok API error code if the status is error
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Pattern=`^ERR_NGROK_\d+$`
// TODO(hkatz) Define error codes and implement in the API
ErrorCode string `json:"errorCode,omitempty"`

// ErrorMessage is a free-form error message if the status is error
Expand Down

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

38 changes: 21 additions & 17 deletions internal/controller/bindings/boundendpoint_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"github.com/go-logr/logr"
"github.com/ngrok/ngrok-api-go/v6"
bindingsv1alpha1 "github.com/ngrok/ngrok-operator/api/bindings/v1alpha1"
"github.com/ngrok/ngrok-operator/internal/controller"
"github.com/ngrok/ngrok-operator/internal/ngrokapi"
"github.com/ngrok/ngrok-operator/internal/util"
)

Expand All @@ -61,12 +63,6 @@ const (
// Used for indexing BoundEndpoints by their target namespace. Not an actual
// field on the BoundEndpoint object.
BoundEndpointTargetNamespacePath = ".spec.targetNamespace"

// TODO(hkatz) ngrok-error-codes
NgrokErrorUpstreamServiceCreateFailed = "ERR_NGROK_0001"
NgrokErrorTargetServiceCreateFailed = "ERR_NGROK_0002"
NgrokErrorFailedToBind = "ERR_NGROK_003"
NgrokErrorNotAllowed = "ERR_NGROK_004"
)

var (
Expand Down Expand Up @@ -228,13 +224,15 @@ func (r *BoundEndpointReconciler) createTargetService(ctx context.Context, owner
r.Recorder.Event(owner, v1.EventTypeWarning, "Created", "Failed to create Target Service")
log.Error(err, "Failed to create Target Service")

ngrokErr := ngrokapi.NewNgrokError(err, ngrokapi.NgrokOpErrFailedToCreateTargetService, "Failed to create Target Service")

setEndpointsStatus(owner, &bindingsv1alpha1.BindingEndpoint{
Status: bindingsv1alpha1.StatusError,
ErrorCode: NgrokErrorTargetServiceCreateFailed,
ErrorMessage: fmt.Sprintf("Failed to create Target Service: %s", err),
ErrorCode: ngrokErr.ErrorCode,
ErrorMessage: ngrokErr.Error(),
})

return err
return ngrokErr
}

r.Recorder.Event(service, v1.EventTypeNormal, "Created", "Created Target Service")
Expand All @@ -250,13 +248,15 @@ func (r *BoundEndpointReconciler) createUpstreamService(ctx context.Context, own
r.Recorder.Event(owner, v1.EventTypeWarning, "Created", "Failed to create Upstream Service")
log.Error(err, "Failed to create Upstream Service")

ngrokErr := ngrokapi.NewNgrokError(err, ngrokapi.NgrokOpErrFailedToCreateUpstreamService, "Failed to create Upstream Service")

setEndpointsStatus(owner, &bindingsv1alpha1.BindingEndpoint{
Status: bindingsv1alpha1.StatusError,
ErrorCode: NgrokErrorUpstreamServiceCreateFailed,
ErrorMessage: fmt.Sprintf("Failed to create Upstream Service: %s", err),
ErrorCode: ngrokErr.ErrorCode,
ErrorMessage: ngrokErr.Error(),
})

return err
return ngrokErr
}

r.Recorder.Event(service, v1.EventTypeNormal, "Created", "Created Upstream Service")
Expand Down Expand Up @@ -603,13 +603,15 @@ func (r *BoundEndpointReconciler) tryToBindEndpoint(ctx context.Context, boundEn

// update statuses
var desired *bindingsv1alpha1.BindingEndpoint
var ngrokErr *ngrok.Error
if bindErr != nil {
// error
log.Error(bindErr, "Failed to bind BoundEndpoint, moving to error")
ngrokErr = ngrokapi.NewNgrokError(bindErr, ngrokapi.NgrokOpErrFailedToConnectServices, "Failed to bind BoundEndpoint")
desired = &bindingsv1alpha1.BindingEndpoint{
Status: bindingsv1alpha1.StatusError,
ErrorCode: NgrokErrorFailedToBind,
ErrorMessage: fmt.Sprintf("Failed to bind BoundEndpoint: %s", bindErr),
ErrorCode: ngrokErr.ErrorCode,
ErrorMessage: ngrokErr.Error(),
}
} else {
// success
Expand All @@ -624,17 +626,19 @@ func (r *BoundEndpointReconciler) tryToBindEndpoint(ctx context.Context, boundEn

// set status
setEndpointsStatus(boundEndpoint, desired)
return bindErr
return ngrokErr
}

// denyBoundEndpoint sets the status of the BoundEndpoint to denied
func (r *BoundEndpointReconciler) denyBoundEndpoint(ctx context.Context, boundEndpoint *bindingsv1alpha1.BoundEndpoint) error {
reason := "Endpoint URI is not allowed by KubernetesOperator allowedURLs configuration"

ngrokErr := ngrokapi.NewNgrokError(fmt.Errorf("endpoint denied"), ngrokapi.NgrokOpErrEndpointDenied, reason)

setEndpointsStatus(boundEndpoint, &bindingsv1alpha1.BindingEndpoint{
Status: bindingsv1alpha1.StatusDenied,
ErrorCode: NgrokErrorNotAllowed,
ErrorMessage: reason,
ErrorCode: ngrokErr.ErrorCode,
ErrorMessage: ngrokErr.Error(),
})

r.Recorder.Event(boundEndpoint, v1.EventTypeWarning, "Denied", reason)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func Test_setEndpointsStatus(t *testing.T) {
desired: &bindingsv1alpha1.BindingEndpoint{
Status: bindingsv1alpha1.StatusError,
ErrorCode: "ERR_NGROK_1234",
ErrorMessage: "Exampl Error Message",
ErrorMessage: "Example Error Message",
},
},
}
Expand Down
6 changes: 5 additions & 1 deletion internal/controller/ngrok/kubernetesoperator_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ var featureMap = map[string]string{
ngrokv1alpha1.KubernetesOperatorFeatureGateway: "Gateway",
}

const (
NgrokErrorFailedToCreateCSR = "ERR_NGROK_20006"
)

// KubernetesOperatorReconciler reconciles a KubernetesOperator object
type KubernetesOperatorReconciler struct {
client.Client
Expand Down Expand Up @@ -149,7 +153,7 @@ func (r *KubernetesOperatorReconciler) create(ctx context.Context, ko *ngrokv1al
if bindingsEnabled {
tlsSecret, err := r.findOrCreateTLSSecret(ctx, ko)
if err != nil {
return err
return ngrokapi.NewNgrokError(err, ngrokapi.NgrokOpErrFailedToCreateCSR, "failed to create TLS secret for CSR")
}

createParams.Binding = &ngrok.KubernetesOperatorBindingCreate{
Expand Down
44 changes: 44 additions & 0 deletions internal/ngrokapi/enriched_errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ngrokapi

import (
"fmt"
"net/http"

"github.com/ngrok/ngrok-api-go/v6"
)

// Enriched Errors utilities to help interact with ngrok.Error and ngrok.IsErrorCode() returned by the ngrok API
// For right now these are all manually defined

type enriched struct {
name string
statusCode int32
}

// list of supported ngrok error codes
var (
NgrokOpErrInternalServerError = enriched{"ERR_NGROK_20000", http.StatusInternalServerError}
NgrokOpErrConfigurationError = enriched{"ERR_NGROK_20001", http.StatusBadRequest}
NgrokOpErrFailedToCreateUpstreamService = enriched{"ERR_NGROK_20002", http.StatusServiceUnavailable}
NgrokOpErrFailedToCreateTargetService = enriched{"ERR_NGROK_20003", http.StatusServiceUnavailable}
NgrokOpErrFailedToConnectServices = enriched{"ERR_NGROK_20004", http.StatusServiceUnavailable}
NgrokOpErrEndpointDenied = enriched{"ERR_NGROK_20005", http.StatusForbidden}
NgrokOpErrFailedToCreateCSR = enriched{"ERR_NGROK_20006", http.StatusInternalServerError}
)

func NewNgrokError(origErr error, ee enriched, msg string) *ngrok.Error {
if ngrokErr, ok := origErr.(*ngrok.Error); ok {
// already have an ngrok.Error
// overwrite the message and return
return &ngrok.Error{
Msg: ngrokErr.Msg,
Details: ngrokErr.Details,
}
}

return &ngrok.Error{
Msg: fmt.Sprintf("%s: %s", msg, origErr),
ErrorCode: ee.name,
StatusCode: ee.statusCode,
}
}

0 comments on commit 3707f6d

Please sign in to comment.