diff --git a/controller/konnect/ops/kongsni.go b/controller/konnect/ops/kongsni.go index 39550659..530111e7 100644 --- a/controller/konnect/ops/kongsni.go +++ b/controller/konnect/ops/kongsni.go @@ -11,4 +11,5 @@ type SNIsSDK interface { CreateSniWithCertificate(context.Context, sdkkonnectops.CreateSniWithCertificateRequest, ...sdkkonnectops.Option) (*sdkkonnectops.CreateSniWithCertificateResponse, error) UpsertSniWithCertificate(ctx context.Context, request sdkkonnectops.UpsertSniWithCertificateRequest, opts ...sdkkonnectops.Option) (*sdkkonnectops.UpsertSniWithCertificateResponse, error) DeleteSniWithCertificate(ctx context.Context, request sdkkonnectops.DeleteSniWithCertificateRequest, opts ...sdkkonnectops.Option) (*sdkkonnectops.DeleteSniWithCertificateResponse, error) + ListSni(ctx context.Context, request sdkkonnectops.ListSniRequest, opts ...sdkkonnectops.Option) (*sdkkonnectops.ListSniResponse, error) } diff --git a/controller/konnect/ops/kongsni_mock.go b/controller/konnect/ops/kongsni_mock.go index 275f0c79..8aae92f1 100644 --- a/controller/konnect/ops/kongsni_mock.go +++ b/controller/konnect/ops/kongsni_mock.go @@ -170,6 +170,80 @@ func (_c *MockSNIsSDK_DeleteSniWithCertificate_Call) RunAndReturn(run func(conte return _c } +// ListSni provides a mock function with given fields: ctx, request, opts +func (_m *MockSNIsSDK) ListSni(ctx context.Context, request operations.ListSniRequest, opts ...operations.Option) (*operations.ListSniResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, request) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for ListSni") + } + + var r0 *operations.ListSniResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, operations.ListSniRequest, ...operations.Option) (*operations.ListSniResponse, error)); ok { + return rf(ctx, request, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, operations.ListSniRequest, ...operations.Option) *operations.ListSniResponse); ok { + r0 = rf(ctx, request, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*operations.ListSniResponse) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, operations.ListSniRequest, ...operations.Option) error); ok { + r1 = rf(ctx, request, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockSNIsSDK_ListSni_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListSni' +type MockSNIsSDK_ListSni_Call struct { + *mock.Call +} + +// ListSni is a helper method to define mock.On call +// - ctx context.Context +// - request operations.ListSniRequest +// - opts ...operations.Option +func (_e *MockSNIsSDK_Expecter) ListSni(ctx interface{}, request interface{}, opts ...interface{}) *MockSNIsSDK_ListSni_Call { + return &MockSNIsSDK_ListSni_Call{Call: _e.mock.On("ListSni", + append([]interface{}{ctx, request}, opts...)...)} +} + +func (_c *MockSNIsSDK_ListSni_Call) Run(run func(ctx context.Context, request operations.ListSniRequest, opts ...operations.Option)) *MockSNIsSDK_ListSni_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]operations.Option, len(args)-2) + for i, a := range args[2:] { + if a != nil { + variadicArgs[i] = a.(operations.Option) + } + } + run(args[0].(context.Context), args[1].(operations.ListSniRequest), variadicArgs...) + }) + return _c +} + +func (_c *MockSNIsSDK_ListSni_Call) Return(_a0 *operations.ListSniResponse, _a1 error) *MockSNIsSDK_ListSni_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockSNIsSDK_ListSni_Call) RunAndReturn(run func(context.Context, operations.ListSniRequest, ...operations.Option) (*operations.ListSniResponse, error)) *MockSNIsSDK_ListSni_Call { + _c.Call.Return(run) + return _c +} + // UpsertSniWithCertificate provides a mock function with given fields: ctx, request, opts func (_m *MockSNIsSDK) UpsertSniWithCertificate(ctx context.Context, request operations.UpsertSniWithCertificateRequest, opts ...operations.Option) (*operations.UpsertSniWithCertificateResponse, error) { _va := make([]interface{}, len(opts)) diff --git a/controller/konnect/ops/ops.go b/controller/konnect/ops/ops.go index ab12e036..9a00fde9 100644 --- a/controller/konnect/ops/ops.go +++ b/controller/konnect/ops/ops.go @@ -136,6 +136,8 @@ func Create[ id, err = getControlPlaneForUID(ctx, sdk.GetControlPlaneSDK(), ent) case *configurationv1alpha1.KongService: id, err = getKongServiceForUID(ctx, sdk.GetServicesSDK(), ent) + case *configurationv1alpha1.KongSNI: + id, err = getSNIForUID(ctx, sdk.GetSNIsSDK(), ent) // --------------------------------------------------------------------- // TODO: add other Konnect types default: @@ -152,7 +154,6 @@ func Create[ } case errors.As(err, &errRelationsFailed): SetKonnectEntityProgrammedConditionFalse(e, errRelationsFailed.Reason, err.Error()) - e.SetKonnectID(errRelationsFailed.KonnectID) case err != nil: SetKonnectEntityProgrammedConditionFalse(e, consts.KonnectEntitiesFailedToCreateReason, err.Error()) default: diff --git a/controller/konnect/ops/ops_kongsni.go b/controller/konnect/ops/ops_kongsni.go index 4977dec5..15ed4b2f 100644 --- a/controller/konnect/ops/ops_kongsni.go +++ b/controller/konnect/ops/ops_kongsni.go @@ -9,6 +9,7 @@ import ( sdkkonnectcomp "github.com/Kong/sdk-konnect-go/models/components" sdkkonnectops "github.com/Kong/sdk-konnect-go/models/operations" sdkkonnecterrs "github.com/Kong/sdk-konnect-go/models/sdkerrors" + "github.com/samber/lo" "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" @@ -33,14 +34,16 @@ func createSNI( CertificateID: sni.Status.Konnect.CertificateID, SNIWithoutParents: kongSNIToSNIWithoutParents(sni), }) - if errWrapped := wrapErrIfKonnectOpFailed(err, CreateOp, sni); errWrapped != nil { - SetKonnectEntityProgrammedConditionFalse(sni, "FailedToCreate", errWrapped.Error()) return errWrapped } + if resp == nil || resp.Sni == nil || resp.Sni.ID == nil || *resp.Sni.ID == "" { + return fmt.Errorf("failed creating %s: %w", sni.GetTypeName(), ErrNilResponse) + } + + // At this point, the SNI has been created successfully. sni.Status.Konnect.SetKonnectID(*resp.Sni.ID) - SetKonnectEntityProgrammedCondition(sni) return nil } @@ -75,6 +78,7 @@ func updateSNI( if errors.As(errWrap, &sdkError) { switch sdkError.StatusCode { case 404: + logEntityNotFoundRecreating(ctx, sni, id) if err := createSNI(ctx, sdk, sni); err != nil { return FailedKonnectOpError[configurationv1alpha1.KongSNI]{ Op: UpdateOp, @@ -92,11 +96,9 @@ func updateSNI( } } - SetKonnectEntityProgrammedConditionFalse(sni, "FailedToUpdate", errWrap.Error()) return errWrap } - SetKonnectEntityProgrammedCondition(sni) return nil } @@ -151,3 +153,26 @@ func kongSNIToSNIWithoutParents(sni *configurationv1alpha1.KongSNI) sdkkonnectco Tags: GenerateTagsForObject(sni, sni.Spec.Tags...), } } + +func getSNIForUID(ctx context.Context, sdk SNIsSDK, sni *configurationv1alpha1.KongSNI) (string, error) { + resp, err := sdk.ListSni(ctx, sdkkonnectops.ListSniRequest{ + ControlPlaneID: sni.GetControlPlaneID(), + Tags: lo.ToPtr(UIDLabelForObject(sni)), + }) + if err != nil { + return "", fmt.Errorf("failed to list SNI entities: %w", err) + } + if resp == nil || resp.Object == nil { + return "", fmt.Errorf("failed listing SNIs: %w", ErrNilResponse) + } + + for _, item := range resp.Object.Data { + if item.Name == sni.Spec.Name { + return *item.ID, nil + } + } + + return "", EntityWithMatchingUIDNotFoundError{ + Entity: sni, + } +}