Skip to content
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
82 changes: 77 additions & 5 deletions pkg/apis/messaging/v1beta1/in_memory_channel_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,87 @@ import (
"context"
"fmt"

eventingduckv1 "knative.dev/eventing/pkg/apis/duck/v1"
eventingduckv1beta1 "knative.dev/eventing/pkg/apis/duck/v1beta1"
"knative.dev/pkg/apis"

"knative.dev/eventing/pkg/apis/messaging"
v1 "knative.dev/eventing/pkg/apis/messaging/v1"
)

// ConvertTo implements apis.Convertible
func (source *InMemoryChannel) ConvertTo(ctx context.Context, sink apis.Convertible) error {
return fmt.Errorf("v1beta1 is the highest known version, got: %T", sink)
// Converts source (from v1beta1.InMemoryChannel) into v1.InMemoryChannel
func (source *InMemoryChannel) ConvertTo(ctx context.Context, obj apis.Convertible) error {
switch sink := obj.(type) {
case *v1.InMemoryChannel:
sink.ObjectMeta = source.ObjectMeta
if sink.Annotations == nil {
sink.Annotations = make(map[string]string)
}
sink.Annotations[messaging.SubscribableDuckVersionAnnotation] = "v1"
source.Status.ConvertTo(ctx, &sink.Status)
return source.Spec.ConvertTo(ctx, &sink.Spec)
default:
return fmt.Errorf("unknown version, got: %T", sink)
}
}

// ConvertTo helps implement apis.Convertible
func (source *InMemoryChannelSpec) ConvertTo(ctx context.Context, sink *v1.InMemoryChannelSpec) error {
if source.Delivery != nil {
sink.Delivery = &eventingduckv1.DeliverySpec{}
if err := source.Delivery.ConvertTo(ctx, sink.Delivery); err != nil {
return err
}
}
sink.SubscribableSpec = eventingduckv1.SubscribableSpec{}
source.SubscribableSpec.ConvertTo(ctx, &sink.SubscribableSpec)
return nil
}

// ConvertTo helps implement apis.Convertible
func (source *InMemoryChannelStatus) ConvertTo(ctx context.Context, sink *v1.InMemoryChannelStatus) {
source.Status.ConvertTo(ctx, &sink.Status)
sink.AddressStatus = source.AddressStatus
sink.SubscribableStatus = eventingduckv1.SubscribableStatus{}
source.SubscribableStatus.ConvertTo(ctx, &sink.SubscribableStatus)
}

// ConvertFrom implements apis.Convertible.
// Converts obj v1.InMemoryChannel into v1beta1.InMemoryChannel
func (sink *InMemoryChannel) ConvertFrom(ctx context.Context, obj apis.Convertible) error {
switch source := obj.(type) {
case *v1.InMemoryChannel:
sink.ObjectMeta = source.ObjectMeta
sink.Status.ConvertFrom(ctx, source.Status)
sink.Spec.ConvertFrom(ctx, source.Spec)
if sink.Annotations == nil {
sink.Annotations = make(map[string]string)
}
sink.Annotations[messaging.SubscribableDuckVersionAnnotation] = "v1beta1"
return nil
default:
return fmt.Errorf("unknown version, got: %T", source)
}
}

// ConvertFrom helps implement apis.Convertible
func (sink *InMemoryChannelSpec) ConvertFrom(ctx context.Context, source v1.InMemoryChannelSpec) error {
if source.Delivery != nil {
sink.Delivery = &eventingduckv1beta1.DeliverySpec{}
if err := sink.Delivery.ConvertFrom(ctx, source.Delivery); err != nil {
return err
}
}
sink.SubscribableSpec = eventingduckv1beta1.SubscribableSpec{}
sink.SubscribableSpec.ConvertFrom(ctx, source.SubscribableSpec)
return nil
}

// ConvertFrom implements apis.Convertible
func (sink *InMemoryChannel) ConvertFrom(ctx context.Context, source apis.Convertible) error {
return fmt.Errorf("v1beta1 is the highest known version, got: %T", source)
// ConvertFrom helps implement apis.Convertible
func (sink *InMemoryChannelStatus) ConvertFrom(ctx context.Context, source v1.InMemoryChannelStatus) {
source.Status.ConvertTo(ctx, &sink.Status)
sink.AddressStatus = source.AddressStatus
sink.SubscribableStatus = eventingduckv1beta1.SubscribableStatus{}
sink.SubscribableStatus.ConvertFrom(ctx, source.SubscribableStatus)
}
275 changes: 274 additions & 1 deletion pkg/apis/messaging/v1beta1/in_memory_channel_conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,21 @@ package v1beta1
import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
eventingduckv1 "knative.dev/eventing/pkg/apis/duck/v1"
eventingduck "knative.dev/eventing/pkg/apis/duck/v1beta1"
"knative.dev/eventing/pkg/apis/messaging"
v1 "knative.dev/eventing/pkg/apis/messaging/v1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
)

func TestInMemoryChannelConversionBadType(t *testing.T) {
good, bad := &InMemoryChannel{}, &InMemoryChannel{}
good, bad := &InMemoryChannel{}, &Subscription{}

if err := good.ConvertTo(context.Background(), bad); err == nil {
t.Errorf("ConvertTo() = %#v, wanted error", bad)
Expand All @@ -32,3 +43,265 @@ func TestInMemoryChannelConversionBadType(t *testing.T) {
t.Errorf("ConvertFrom() = %#v, wanted error", good)
}
}

// Test v1beta1 -> v1 -> v1beta1
func TestInMemoryChannelConversion(t *testing.T) {
// Just one for now, just adding the for loop for ease of future changes.
versions := []apis.Convertible{&v1.InMemoryChannel{}}

linear := eventingduck.BackoffPolicyLinear

tests := []struct {
name string
in *InMemoryChannel
}{{
name: "min configuration",
in: &InMemoryChannel{
ObjectMeta: metav1.ObjectMeta{
Name: "channel-name",
Namespace: "channel-ns",
Generation: 17,
},
Spec: InMemoryChannelSpec{},
Status: InMemoryChannelStatus{
ChannelableStatus: eventingduck.ChannelableStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{},
},
},
},
},
}, {
name: "full configuration",
in: &InMemoryChannel{
ObjectMeta: metav1.ObjectMeta{
Name: "channel-name",
Namespace: "channel-ns",
Generation: 17,
},
Spec: InMemoryChannelSpec{
ChannelableSpec: eventingduck.ChannelableSpec{
SubscribableSpec: eventingduck.SubscribableSpec{
Subscribers: []eventingduck.SubscriberSpec{
{
UID: "uid-1",
Generation: 7,
SubscriberURI: apis.HTTP("subscriber.example.com"),
ReplyURI: apis.HTTP("reply.example.com"),
// DeadLetterSinkURI: apis.HTTP("dlc.reply.example.com"),
Delivery: &eventingduck.DeliverySpec{
DeadLetterSink: &duckv1.Destination{
Ref: &duckv1.KReference{
Kind: "dlKind",
Namespace: "dlNamespace",
Name: "dlName",
APIVersion: "dlAPIVersion",
},
URI: apis.HTTP("subscriber.dls.example.com"),
},
Retry: pointer.Int32Ptr(5),
BackoffPolicy: &linear,
BackoffDelay: pointer.StringPtr("5s"),
},
},
},
},
Delivery: &eventingduck.DeliverySpec{
DeadLetterSink: &duckv1.Destination{
Ref: &duckv1.KReference{
Kind: "dlKind",
Namespace: "dlNamespace",
Name: "dlName",
APIVersion: "dlAPIVersion",
},
URI: apis.HTTP("dls"),
},
Retry: pointer.Int32Ptr(5),
BackoffPolicy: &linear,
BackoffDelay: pointer.StringPtr("5s"),
},
},
},
Status: InMemoryChannelStatus{
ChannelableStatus: eventingduck.ChannelableStatus{
Status: duckv1.Status{
ObservedGeneration: 1,
Conditions: duckv1.Conditions{{
Type: "Ready",
Status: "True",
}},
},
AddressStatus: duckv1.AddressStatus{
Address: &duckv1.Addressable{
URL: apis.HTTP("addressstatus.example.com"),
},
},
SubscribableStatus: eventingduck.SubscribableStatus{
Subscribers: []eventingduck.SubscriberStatus{
{
UID: "status-uid-1",
ObservedGeneration: 99,
Ready: corev1.ConditionTrue,
Message: "msg",
},
},
},
},
},
},
}}
for _, test := range tests {
for _, version := range versions {
t.Run(test.name, func(t *testing.T) {
ver := version
if err := test.in.ConvertTo(context.Background(), ver); err != nil {
t.Errorf("ConvertTo() = %v", err)
}
got := &InMemoryChannel{}
if err := got.ConvertFrom(context.Background(), ver); err != nil {
t.Errorf("ConvertFrom() = %v", err)
}
// Make sure the annotation specifies the correct duck.
if test.in.Annotations == nil {
test.in.Annotations = make(map[string]string)
}
test.in.Annotations[messaging.SubscribableDuckVersionAnnotation] = "v1beta1"

if diff := cmp.Diff(test.in, got); diff != "" {
t.Errorf("roundtrip (-want, +got) = %v", diff)
}
})
}
}
}

// Test v1 -> v1beta1 -> v1
func TestInMemoryChannelConversionWithV1Beta1(t *testing.T) {
// Just one for now, just adding the for loop for ease of future changes.
versions := []apis.Convertible{&InMemoryChannel{}}

linear := eventingduckv1.BackoffPolicyLinear

tests := []struct {
name string
in *v1.InMemoryChannel
}{{
name: "min configuration",
in: &v1.InMemoryChannel{
ObjectMeta: metav1.ObjectMeta{
Name: "channel-name",
Namespace: "channel-ns",
Generation: 17,
},
Spec: v1.InMemoryChannelSpec{},
Status: v1.InMemoryChannelStatus{
ChannelableStatus: eventingduckv1.ChannelableStatus{
Status: duckv1.Status{
Conditions: duckv1.Conditions{},
},
},
},
},
}, {
name: "full configuration",
in: &v1.InMemoryChannel{
ObjectMeta: metav1.ObjectMeta{
Name: "channel-name",
Namespace: "channel-ns",
Generation: 17,
},
Spec: v1.InMemoryChannelSpec{
ChannelableSpec: eventingduckv1.ChannelableSpec{
SubscribableSpec: eventingduckv1.SubscribableSpec{
Subscribers: []eventingduckv1.SubscriberSpec{
{
UID: "uid-1",
Generation: 7,
SubscriberURI: apis.HTTP("subscriber.example.com"),
ReplyURI: apis.HTTP("reply.example.com"),
// DeadLetterSinkURI: apis.HTTP("dlc.reply.example.com"),
Delivery: &eventingduckv1.DeliverySpec{
DeadLetterSink: &duckv1.Destination{
Ref: &duckv1.KReference{
Kind: "dlKind",
Namespace: "dlNamespace",
Name: "dlName",
APIVersion: "dlAPIVersion",
},
URI: apis.HTTP("subscriber.dls.example.com"),
},
Retry: pointer.Int32Ptr(5),
BackoffPolicy: &linear,
BackoffDelay: pointer.StringPtr("5s"),
},
},
},
},
Delivery: &eventingduckv1.DeliverySpec{
DeadLetterSink: &duckv1.Destination{
Ref: &duckv1.KReference{
Kind: "dlKind",
Namespace: "dlNamespace",
Name: "dlName",
APIVersion: "dlAPIVersion",
},
URI: apis.HTTP("dls"),
},
Retry: pointer.Int32Ptr(5),
BackoffPolicy: &linear,
BackoffDelay: pointer.StringPtr("5s"),
},
},
},
Status: v1.InMemoryChannelStatus{
ChannelableStatus: eventingduckv1.ChannelableStatus{
Status: duckv1.Status{
ObservedGeneration: 1,
Conditions: duckv1.Conditions{{
Type: "Ready",
Status: "True",
}},
},
AddressStatus: duckv1.AddressStatus{
Address: &duckv1.Addressable{
URL: apis.HTTP("addressstatus.example.com"),
},
},
SubscribableStatus: eventingduckv1.SubscribableStatus{
Subscribers: []eventingduckv1.SubscriberStatus{
{
UID: "status-uid-1",
ObservedGeneration: 99,
Ready: corev1.ConditionTrue,
Message: "msg",
},
},
},
},
},
},
}}
for _, test := range tests {
for _, version := range versions {
t.Run(test.name, func(t *testing.T) {
ver := version
if err := version.ConvertFrom(context.Background(), test.in); err != nil {
t.Errorf("ConvertTo() = %v", err)
}
got := &v1.InMemoryChannel{}
if err := ver.ConvertTo(context.Background(), got); err != nil {
t.Errorf("ConvertFrom() = %v", err)
}
// Make sure the annotation specifies the correct duck.
if test.in.Annotations == nil {
test.in.Annotations = make(map[string]string)
}
test.in.Annotations[messaging.SubscribableDuckVersionAnnotation] = "v1"

if diff := cmp.Diff(test.in, got); diff != "" {
t.Errorf("roundtrip (-want, +got) = %v", diff)
}
})
}
}
}