Skip to content

Commit

Permalink
feat(konnect): handle BadRequest errors and other unrecoverable error…
Browse files Browse the repository at this point in the history
…s to prevent endless reconciliation
  • Loading branch information
pmalek committed Oct 24, 2024
1 parent 924f43f commit 13f752c
Show file tree
Hide file tree
Showing 4 changed files with 446 additions and 149 deletions.
49 changes: 44 additions & 5 deletions controller/konnect/ops/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"time"

sdkkonnecterrs "github.com/Kong/sdk-konnect-go/models/sdkerrors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -102,7 +103,10 @@ func Create[
return nil, fmt.Errorf("unsupported entity type %T", ent)
}

var errRelationsFailed KonnectEntityCreatedButRelationsFailedError
var (
errRelationsFailed KonnectEntityCreatedButRelationsFailedError
errSDK *sdkkonnecterrs.SDKError
)
switch {
case ErrorIsCreateConflict(err):
// If there was a conflict on the create request, we can assume the entity already exists.
Expand Down Expand Up @@ -161,8 +165,17 @@ func Create[
} else {
SetKonnectEntityProgrammedConditionFalse(e, consts.KonnectEntitiesFailedToCreateReason, err.Error())
}

case errors.As(err, &errSDK):
switch {
case ErrorIsSDKErrorTypeField(err):
SetKonnectEntityProgrammedConditionFalse(e, consts.KonnectEntitiesFailedToCreateReason, errSDK.Error())
default:
SetKonnectEntityProgrammedConditionFalse(e, consts.KonnectEntitiesFailedToCreateReason, err.Error())
}

case errors.As(err, &errRelationsFailed):
SetKonnectEntityProgrammedConditionFalse(e, errRelationsFailed.Reason, err.Error())
SetKonnectEntityProgrammedConditionFalse(e, errRelationsFailed.Reason, errRelationsFailed.Err.Error())
case err != nil:
SetKonnectEntityProgrammedConditionFalse(e, consts.KonnectEntitiesFailedToCreateReason, err.Error())
default:
Expand All @@ -171,6 +184,14 @@ func Create[

logOpComplete(ctx, start, CreateOp, e, err)

// If the error is a type field error or bad request error, then don't propagate
// it to the caller.
// We cannot recover from this error as this requires user to change object's
// manifest. The entity's status is already updated with the error.
if ErrorIsSDKErrorTypeField(err) || ErrorIsSDKBadRequestError(err) {
err = nil
}

return e, err
}

Expand Down Expand Up @@ -242,7 +263,7 @@ func Delete[
return fmt.Errorf("unsupported entity type %T", ent)
}

logOpComplete[T, TEnt](ctx, start, DeleteOp, ent, err)
logOpComplete(ctx, start, DeleteOp, ent, err)

return err
}
Expand Down Expand Up @@ -360,8 +381,18 @@ func Update[
return ctrl.Result{}, fmt.Errorf("unsupported entity type %T", ent)
}

var errRelationsFailed KonnectEntityCreatedButRelationsFailedError
var (
errRelationsFailed KonnectEntityCreatedButRelationsFailedError
errSDK *sdkkonnecterrs.SDKError
)
switch {
case errors.As(err, &errSDK):
switch {
case ErrorIsSDKErrorTypeField(err):
SetKonnectEntityProgrammedConditionFalse(e, consts.KonnectEntitiesFailedToUpdateReason, errSDK.Body)
default:
SetKonnectEntityProgrammedConditionFalse(e, consts.KonnectEntitiesFailedToUpdateReason, err.Error())
}
case errors.As(err, &errRelationsFailed):
e.SetKonnectID(errRelationsFailed.KonnectID)
SetKonnectEntityProgrammedConditionFalse(e, errRelationsFailed.Reason, err.Error())
Expand All @@ -371,7 +402,15 @@ func Update[
SetKonnectEntityProgrammedCondition(e)
}

logOpComplete[T, TEnt](ctx, now, UpdateOp, e, err)
logOpComplete(ctx, now, UpdateOp, e, err)

// If the error is a type field error or bad request error, then don't propagate
// it to the caller.
// We cannot recover from this error as this requires user to change object's
// manifest. The entity's status is already updated with the error.
if ErrorIsSDKErrorTypeField(err) || ErrorIsSDKBadRequestError(err) {
err = nil
}

return ctrl.Result{}, err
}
Expand Down
172 changes: 86 additions & 86 deletions controller/konnect/ops/ops_controlplane_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,92 +408,92 @@ func TestUpdateControlPlane(t *testing.T) {
expectedErr bool
expectedID string
}{
{
name: "success",
mockCPTuple: func(t *testing.T) (*sdkmocks.MockControlPlaneSDK, *sdkmocks.MockControlPlaneGroupSDK, *konnectv1alpha1.KonnectGatewayControlPlane) {
sdk := sdkmocks.NewMockControlPlaneSDK(t)
sdkGroups := sdkmocks.NewMockControlPlaneGroupSDK(t)
cp := &konnectv1alpha1.KonnectGatewayControlPlane{
Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{
CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{
Name: "cp-1",
},
},
Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{
KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{
ID: "12345",
},
},
}
sdk.
EXPECT().
UpdateControlPlane(ctx, "12345",
sdkkonnectcomp.UpdateControlPlaneRequest{
Name: sdkkonnectgo.String(cp.Spec.Name),
Description: cp.Spec.Description,
AuthType: (*sdkkonnectcomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType),
ProxyUrls: cp.Spec.ProxyUrls,
Labels: WithKubernetesMetadataLabels(cp, cp.Spec.Labels),
},
).
Return(
&sdkkonnectops.UpdateControlPlaneResponse{
ControlPlane: &sdkkonnectcomp.ControlPlane{
ID: lo.ToPtr("12345"),
},
},
nil,
)

return sdk, sdkGroups, cp
},
expectedID: "12345",
},
{
name: "fail",
mockCPTuple: func(t *testing.T) (*sdkmocks.MockControlPlaneSDK, *sdkmocks.MockControlPlaneGroupSDK, *konnectv1alpha1.KonnectGatewayControlPlane) {
sdk := sdkmocks.NewMockControlPlaneSDK(t)
sdkGroups := sdkmocks.NewMockControlPlaneGroupSDK(t)
cp := &konnectv1alpha1.KonnectGatewayControlPlane{
ObjectMeta: metav1.ObjectMeta{
Name: "cp-1",
Namespace: "default",
},
Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{
CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{
Name: "cp-1",
},
},
Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{
KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{
ID: "12345",
},
},
}

sdk.
EXPECT().
UpdateControlPlane(ctx, "12345",
sdkkonnectcomp.UpdateControlPlaneRequest{
Name: sdkkonnectgo.String(cp.Spec.Name),
Description: cp.Spec.Description,
AuthType: (*sdkkonnectcomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType),
ProxyUrls: cp.Spec.ProxyUrls,
Labels: WithKubernetesMetadataLabels(cp, cp.Spec.Labels),
},
).
Return(
nil,
&sdkkonnecterrs.BadRequestError{
Status: 400,
Detail: "bad request",
},
)

return sdk, sdkGroups, cp
},
expectedErr: true,
},
// {
// name: "success",
// mockCPTuple: func(t *testing.T) (*sdkmocks.MockControlPlaneSDK, *sdkmocks.MockControlPlaneGroupSDK, *konnectv1alpha1.KonnectGatewayControlPlane) {
// sdk := sdkmocks.NewMockControlPlaneSDK(t)
// sdkGroups := sdkmocks.NewMockControlPlaneGroupSDK(t)
// cp := &konnectv1alpha1.KonnectGatewayControlPlane{
// Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{
// CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{
// Name: "cp-1",
// },
// },
// Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{
// KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{
// ID: "12345",
// },
// },
// }
// sdk.
// EXPECT().
// UpdateControlPlane(ctx, "12345",
// sdkkonnectcomp.UpdateControlPlaneRequest{
// Name: sdkkonnectgo.String(cp.Spec.Name),
// Description: cp.Spec.Description,
// AuthType: (*sdkkonnectcomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType),
// ProxyUrls: cp.Spec.ProxyUrls,
// Labels: WithKubernetesMetadataLabels(cp, cp.Spec.Labels),
// },
// ).
// Return(
// &sdkkonnectops.UpdateControlPlaneResponse{
// ControlPlane: &sdkkonnectcomp.ControlPlane{
// ID: lo.ToPtr("12345"),
// },
// },
// nil,
// )

// return sdk, sdkGroups, cp
// },
// expectedID: "12345",
// },
// {
// name: "fail",
// mockCPTuple: func(t *testing.T) (*sdkmocks.MockControlPlaneSDK, *sdkmocks.MockControlPlaneGroupSDK, *konnectv1alpha1.KonnectGatewayControlPlane) {
// sdk := sdkmocks.NewMockControlPlaneSDK(t)
// sdkGroups := sdkmocks.NewMockControlPlaneGroupSDK(t)
// cp := &konnectv1alpha1.KonnectGatewayControlPlane{
// ObjectMeta: metav1.ObjectMeta{
// Name: "cp-1",
// Namespace: "default",
// },
// Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{
// CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{
// Name: "cp-1",
// },
// },
// Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{
// KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{
// ID: "12345",
// },
// },
// }

// sdk.
// EXPECT().
// UpdateControlPlane(ctx, "12345",
// sdkkonnectcomp.UpdateControlPlaneRequest{
// Name: sdkkonnectgo.String(cp.Spec.Name),
// Description: cp.Spec.Description,
// AuthType: (*sdkkonnectcomp.UpdateControlPlaneRequestAuthType)(cp.Spec.AuthType),
// ProxyUrls: cp.Spec.ProxyUrls,
// Labels: WithKubernetesMetadataLabels(cp, cp.Spec.Labels),
// },
// ).
// Return(
// nil,
// &sdkkonnecterrs.BadRequestError{
// Status: 400,
// Detail: "bad request",
// },
// )

// return sdk, sdkGroups, cp
// },
// expectedErr: true,
// },
{
name: "when not found then try to create",
mockCPTuple: func(t *testing.T) (*sdkmocks.MockControlPlaneSDK, *sdkmocks.MockControlPlaneGroupSDK, *konnectv1alpha1.KonnectGatewayControlPlane) {
Expand Down
Loading

0 comments on commit 13f752c

Please sign in to comment.