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
1 change: 1 addition & 0 deletions Gopkg.lock

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

9 changes: 9 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ required = [
"knative.dev/pkg/testutils/clustermanager/perf-tests",
"knative.dev/test-infra/scripts",
"knative.dev/test-infra/tools/dep-collector",
"contrib.go.opencensus.io/exporter/ocagent",
]

[prune]
Expand Down Expand Up @@ -79,6 +80,10 @@ required = [
# https://github.com/kubernetes/kubernetes/blob/v1.16.4/go.mod#L81
version = "v1.1.7"

[[override]]
name = "contrib.go.opencensus.io/exporter/ocagent"
version = "v0.6.0"

# TODO why is this overridden?
[[override]]
name = "github.com/Shopify/sarama"
Expand Down Expand Up @@ -116,3 +121,7 @@ required = [
[[constraint]]
name = "github.com/tsenart/vegeta"
version = "12.7.0"

[[constraint]]
name = "contrib.go.opencensus.io/exporter/ocagent"
version = "0.6.0"
6 changes: 3 additions & 3 deletions hack/update-codegen.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ KNATIVE_CODEGEN_PKG=${KNATIVE_CODEGEN_PKG:-$(cd ${REPO_ROOT_DIR}; ls -d -1 $(dir
# instead of the $GOPATH directly. For normal projects this can be dropped.
${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \
knative.dev/eventing/pkg/client knative.dev/eventing/pkg/apis \
"eventing:v1alpha1 messaging:v1alpha1 flows:v1alpha1 sources:v1alpha1" \
"eventing:v1alpha1 messaging:v1alpha1 messaging:v1beta1 flows:v1alpha1 sources:v1alpha1" \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt

# TODO(#2312): Remove this after v0.13.
Expand All @@ -42,13 +42,13 @@ ${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \
# Only deepcopy the Duck types, as they are not real resources.
${CODEGEN_PKG}/generate-groups.sh "deepcopy" \
knative.dev/eventing/pkg/client knative.dev/eventing/pkg/apis \
"duck:v1alpha1" \
"duck:v1alpha1 duck:v1beta1" \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt

# Knative Injection
${KNATIVE_CODEGEN_PKG}/hack/generate-knative.sh "injection" \
knative.dev/eventing/pkg/client knative.dev/eventing/pkg/apis \
"eventing:v1alpha1 messaging:v1alpha1 flows:v1alpha1 sources:v1alpha1 duck:v1alpha1" \
"eventing:v1alpha1 messaging:v1alpha1 messaging:v1beta1 flows:v1alpha1 sources:v1alpha1 duck:v1alpha1 duck:v1beta1" \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt

# TODO(#2312): Remove this after v0.13.
Expand Down
152 changes: 152 additions & 0 deletions pkg/apis/duck/v1beta1/channelable_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
Copyright 2020 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1beta1

import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"knative.dev/pkg/apis"
"knative.dev/pkg/apis/duck"
duckv1 "knative.dev/pkg/apis/duck/v1"
)

// +genduck
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// Channelable is a skeleton type wrapping Subscribable and Addressable in the manner we expect resource writers
// defining compatible resources to embed it. We will typically use this type to deserialize
// Channelable ObjectReferences and access their subscription and address data. This is not a real resource.
type Channelable struct {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Delivery moved into Subscribable, then we can drop Channelable all together and a channel becomes

Addressable + Subscribable.

What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sense to me. Big change though....

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides removing one duck type (which I kind of think adds clarity), are there other benefits to removing it? I think being able to operate on a Channel ducktype as a single object in the consuming code base is kind of nice (instead of operating on Addressable / Subscribable) separately.
Not against it, just not sure what the concrete benefits of it are.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, I meant that actually having the Channelable as a separate duck type adds clarity, not removing it. So, what are the benefits for removing it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplify the number of things, channelable does not add anything new, just the union of two other ducks

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the code using it now needs to set up two watches if it cares about channels (one for addressable and one for subscribable) and extra serializations / etc.? Am I just confused here? Seems like it has downsides and I'm not seeing the upside of removing the duck.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the discussion above about Delivery NOT moving to Subscribable (since it needs to be available per Subscription) but moving to SubscriberSpec and the fact that there's also a DeadLetterChannel in the Channel, I don't see this happening. But perhaps I'm missing something here? So it's not only Subscribable, Addressable but also has DeadLetterChannel in the status.

metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// Spec is the part where the Channelable fulfills the Subscribable contract.
Spec ChannelableSpec `json:"spec,omitempty"`

Status ChannelableStatus `json:"status,omitempty"`
}

// ChannelableSpec contains Spec of the Channelable object
type ChannelableSpec struct {
SubscribableSpec `json:",inline"`

// DeliverySpec contains options controlling the event delivery
// +optional
Delivery *DeliverySpec `json:"delivery,omitempty"`
Comment thread
vaikas marked this conversation as resolved.
}

// ChannelableStatus contains the Status of a Channelable object.
type ChannelableStatus struct {
// inherits duck/v1 Status, which currently provides:
// * ObservedGeneration - the 'Generation' of the Service that was last processed by the controller.
// * Conditions - the latest available observations of a resource's current state.
duckv1.Status `json:",inline"`
// AddressStatus is the part where the Channelable fulfills the Addressable contract.
duckv1.AddressStatus `json:",inline"`
// Subscribers is populated with the statuses of each of the Channelable's subscribers.
SubscribableStatus `json:",inline"`
// DeadLetterChannel is set by the channel when it supports native error handling via a channel
// Failed messages are delivered here.
// +optional
DeadLetterChannel *corev1.ObjectReference `json:"deadLetterChannel,omitempty"`
}

var (
// Verify Channelable resources meet duck contracts.
_ duck.Populatable = (*Channelable)(nil)
_ duck.Implementable = (*Channelable)(nil)
_ apis.Listable = (*Channelable)(nil)
)

// Populate implements duck.Populatable
func (c *Channelable) Populate() {
c.Spec.SubscribableSpec = SubscribableSpec{
// Populate ALL fields
Subscribers: []SubscriberSpec{{
UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1",
Generation: 1,
SubscriberURI: apis.HTTP("call1"),
ReplyURI: apis.HTTP("sink2"),
}, {
UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1",
Generation: 2,
SubscriberURI: apis.HTTP("call2"),
ReplyURI: apis.HTTP("sink2"),
}},
}
retry := int32(5)
linear := BackoffPolicyLinear
delay := "5s"
c.Spec.Delivery = &DeliverySpec{
DeadLetterSink: &duckv1.Destination{
Ref: &corev1.ObjectReference{
Name: "aname",
},
URI: &apis.URL{
Scheme: "http",
Host: "test-error-domain",
},
},
Retry: &retry,
BackoffPolicy: &linear,
BackoffDelay: &delay,
}
c.Status = ChannelableStatus{
AddressStatus: duckv1.AddressStatus{
Address: &duckv1.Addressable{
URL: &apis.URL{
Scheme: "http",
Host: "test-domain",
},
},
},
SubscribableStatus: SubscribableStatus{
Subscribers: []SubscriberStatus{{
UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1",
ObservedGeneration: 1,
Ready: corev1.ConditionTrue,
Message: "Some message",
}, {
UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1",
ObservedGeneration: 2,
Ready: corev1.ConditionFalse,
Message: "Some message",
}},
},
}
}

// GetFullType implements duck.Implementable
func (s *Channelable) GetFullType() duck.Populatable {
return &Channelable{}
}

// GetListType implements apis.Listable
func (c *Channelable) GetListType() runtime.Object {
return &ChannelableList{}
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ChannelableList is a list of Channelable resources.
type ChannelableList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`

Items []Channelable `json:"items"`
}
107 changes: 107 additions & 0 deletions pkg/apis/duck/v1beta1/channelable_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
Copyright 2020 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1beta1

import (
"testing"

corev1 "k8s.io/api/core/v1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"

"github.com/google/go-cmp/cmp"
)

func TestChannelableGetListType(t *testing.T) {
c := &Channelable{}
switch c.GetListType().(type) {
case *ChannelableList:
// expected
default:
t.Errorf("expected GetListType to return *ChannelableList, got %T", c.GetListType())
}
}

func TestChannelablePopulate(t *testing.T) {
got := &Channelable{}

retry := int32(5)
linear := BackoffPolicyLinear
delay := "5s"
want := &Channelable{
Spec: ChannelableSpec{
SubscribableSpec: SubscribableSpec{
Subscribers: []SubscriberSpec{{
UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1",
Generation: 1,
SubscriberURI: apis.HTTP("call1"),
ReplyURI: apis.HTTP("sink2"),
}, {
UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1",
Generation: 2,
SubscriberURI: apis.HTTP("call2"),
ReplyURI: apis.HTTP("sink2"),
}},
},
Delivery: &DeliverySpec{
DeadLetterSink: &duckv1.Destination{
Ref: &corev1.ObjectReference{
Name: "aname",
},
URI: &apis.URL{
Scheme: "http",
Host: "test-error-domain",
},
},
Retry: &retry,
BackoffPolicy: &linear,
BackoffDelay: &delay,
},
},

Status: ChannelableStatus{
AddressStatus: duckv1.AddressStatus{
Address: &duckv1.Addressable{
URL: &apis.URL{
Scheme: "http",
Host: "test-domain",
},
},
},
SubscribableStatus: SubscribableStatus{
Subscribers: []SubscriberStatus{{
UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1",
ObservedGeneration: 1,
Ready: corev1.ConditionTrue,
Message: "Some message",
}, {
UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1",
ObservedGeneration: 2,
Ready: corev1.ConditionFalse,
Message: "Some message",
}},
},
},
}

got.Populate()

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

}
67 changes: 67 additions & 0 deletions pkg/apis/duck/v1beta1/delivery_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright 2020 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1beta1

import (
corev1 "k8s.io/api/core/v1"
duckv1 "knative.dev/pkg/apis/duck/v1"
)

// DeliverySpec contains the delivery options for event senders,
// such as channelable and source.
type DeliverySpec struct {
// DeadLetterSink is the sink receiving event that couldn't be sent to
// a destination.
// +optional
DeadLetterSink *duckv1.Destination `json:"deadLetterSink,omitempty"`
Comment thread
vaikas marked this conversation as resolved.

// Retry is the minimum number of retries the sender should attempt when
// sending an event before moving it to the dead letter sink.
// +optional
Retry *int32 `json:"retry,omitempty"`

// BackoffPolicy is the retry backoff policy (linear, exponential)
// +optional
BackoffPolicy *BackoffPolicyType `json:"backoffPolicy,omitempty"`

// BackoffDelay is the delay before retrying.
// More information on Duration format: https://www.ietf.org/rfc/rfc3339.txt
//
// For linear policy, backoff delay is the time interval between retries.
// For exponential policy , backoff delay is backoffDelay*2^<numberOfRetries>
// +optional
BackoffDelay *string `json:"backoffDelay,omitempty"`
}

// BackoffPolicyType is the type for backoff policies
type BackoffPolicyType string

const (
// Linear backoff policy
BackoffPolicyLinear BackoffPolicyType = "linear"

// Exponential backoff policy
BackoffPolicyExponential BackoffPolicyType = "exponential"
)

// DeliveryStatus contains the Status of an object supporting delivery options.
type DeliveryStatus struct {
// DeadLetterChannel is the reference to the native, platform specific channel
// where failed events are sent to.
// +optional
DeadLetterChannel *corev1.ObjectReference `json:"deadLetterChannel,omitempty"`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe the type should be api.URL.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the status, it should be a reference to a Channel (well, really, Subscribable). That way you can Subscribe to the events that have failed. Make sense?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the same thought until I realized that this really is a ref to a channel that the owning channel created. I am +1 on DeadLetterChannel

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this object also needs a deadlettersinkuri? If the resource has a delivery spec in the spec there is currently no place to show a resolved delivery deadleattersink.

Or there are two kinds of status objects, one where the channel makes you a deadletter channel, and ones that use the resolved deadletter destination

}
Loading