-
Notifications
You must be signed in to change notification settings - Fork 600
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create new Subscription in pkg/apis/eventing as per the new spec. #421
Conversation
👍 on the general direction. One idea worth considering. Instead of using strings for these properties we could use ObjectReferences. The corresponding resolved values for Advantages:
Disadvantages:
|
// Processor is the processor service DNS name. Events | ||
// from the From channel will be delivered here and replies are sent | ||
// to To channel. | ||
Processor string `json:"processor,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about Call
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since kubernetes resource yaml describes the desired declarative state to be reconciled by a controller, I personally prefer to avoid using verbs that suggest the yaml is defining the imperative logic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
calls
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From conversation in Slack, I'd like to see names which provide the following:
- Clearly unambiguous about the sequence (
from
,processor
,to
seems ambiguous whetherto
orprocessor
is last). - Work cleanly for all three cases (
from
,processor
,to
), (from
,processor
), (from
,to
).
Mostly, we seem to think from
is clear. Here are some options for the other two:
processor
:call
,invoke
,through
,subscriber
,apply
to
:finally
(collides with exception handling),then
(may not be strong enough),result
,out
,continue
(may collide with loops and not imply last item).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's an option where there's always a to/from pair at top-level. If both processor
and channel
are included in the to
, the processor
will always be invoked prior to sending its result to the channel.
from: x
to:
processor: foo
channel: y
from: x
to:
processor: foo
from: x
to:
channel: y
The downside is that it adds a level of nesting in the config, but that might be worth the tradeoff since these resources would typically be generated from higher level resources and/or CLIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the upside of adding this extra level of nesting, since to: block will still have the same behaviour as if we kept it the top level?
// Target service DNS name for replies returned by the subscriber. | ||
ReplyTo string `json:"replyTo,omitempty"` | ||
// To is the name of the channel to send transformed events | ||
To string `json:"to,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about Continue
?
@scothis making them references is appealing for sure. I was left wondering how to actually then configure the underlying 'real channel' to wire things together. It's one thing to resolve the reference, but then once the wiring happens, how do you see that happening. Seems like once you resolve the referenced object, you'd need to modify that object to actually create the subscription. So, if a BetterChannel CRD was created and subscription resolved that it was the underlying channel, how would the SubscriptionController then know how to modify the BetterChannel resource? |
@vaikas-google It may help to talk about the The I don't think the SubscriptionController would ever modify the Channel resource, it would only be responsible for updating the SubcriptionStatus for the values it resolves. The Channel Provisioner is responsible for provisioning/reconciling the Subscription with the resolved properties. |
But the SubscriptionController needs to somehow communicate to the resolved channel that there's a new subscription?
Ideally (I think) Channel would have a subresource that the SubscriptionController would twiddle, but in lieue of that, we could make it so that the "contract" (given that we do refs, and we need to have some commonality there in the future) would be that a target of a ChannelRef resolve to an object that in it's spec has a Subscriptions List (or something like that), and the SubscriptionController then can just Read Modify Write the list by adding (or removing on deletion) the new (or deleted) subscriber. And yes, rejecting anything other than channels.eventing.knative.dev/v1alpha1 for now is fine, just thinking how this could look like for superduperchannel that comes next and what that contract may look like. Lastly, as per the document discussed, this will move to: |
@vaikas-google Ah, I understand your proposal for Subscriptions now. The doc so far has been quiet about how Channel Provisioners discover Subscriptions. Setting the resolved Subscriptions on the Channel resource should work so long as the subscriptions are fairly stable. It will create more work for the channel provisioner to tease apart which subscription was mutated/added/removed when the channel resource is updated. With the benefit of watching a single resource instead of two. I'll need to think about this a bit more.
Yes, I was merging the kind and apiVersion in my head. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only renames the fields, and does not update our samples or existing code.
My understanding is that this will be moved to the eventing.knative.dev
package before submit, so as not to break current users.
name: "missing to", | ||
c: &SubscriptionSpec{ | ||
From: "fromChannel", | ||
To: "toChannel", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be "processor", or should "to" be left off? (This matches the next test case right now.)
// Processor is the processor service DNS name. Events | ||
// from the From channel will be delivered here and replies are sent | ||
// to To channel. | ||
Processor string `json:"processor,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From conversation in Slack, I'd like to see names which provide the following:
- Clearly unambiguous about the sequence (
from
,processor
,to
seems ambiguous whetherto
orprocessor
is last). - Work cleanly for all three cases (
from
,processor
,to
), (from
,processor
), (from
,to
).
Mostly, we seem to think from
is clear. Here are some options for the other two:
processor
:call
,invoke
,through
,subscriber
,apply
to
:finally
(collides with exception handling),then
(may not be strong enough),result
,out
,continue
(may collide with loops and not imply last item).
to->follower? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation logic from the previous commit will need to be updated now that these are ObjectRefs instead of Strings
// to these events for further processing. | ||
// | ||
// For example, this could be a reference to a Route resource | ||
// or a Configuration resource. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Configuration/Service/
Configuration is not targetable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
// - Kind | ||
// - APIVersion | ||
// - Name | ||
From *corev1.ObjectReference `json:"from,omitempty"` | ||
|
||
// Processor is the processor service DNS name. Events |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no longer a DNS name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@scothis yes, validation needs to change and things need to move to right location. Just trying to minimize churn and only focus on the API definition and semantics for now. Once we agree on that, I'll move this to eventing/pkg/apis/eventing/v1alpha1/ and do all the remaining work there. |
return fe | ||
} | ||
|
||
if equality.Semantic.DeepEqual(ss.Processor, &corev1.ObjectReference{}) && equality.Semantic.DeepEqual(ss.To, &corev1.ObjectReference{}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could also cause issues if one is nil and the other is empty
@@ -49,7 +57,7 @@ func (current *Subscription) CheckImmutableFields(og apis.Immutable) *apis.Field | |||
return nil | |||
} | |||
|
|||
ignoreArguments := cmpopts.IgnoreFields(SubscriptionSpec{}, "Subscriber", "Arguments") | |||
ignoreArguments := cmpopts.IgnoreFields(SubscriptionSpec{}, "Processor", "Arguments") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To
should also allow mutation, only From
should be immutable. It was an oversight that ReplyTo
is immutable right now.
ProcessorName = "processor" | ||
) | ||
|
||
func getValidFromRef() *corev1.ObjectReference { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should also test invalid From
and To
ObjectReferences:
- non channel
Kind
andAPIVersion
- fields other than
Kind
,APIVersion
andName
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Working on it... Just want to resolve the names...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I'm not commenting on the naming beyond the 🚲 🏠 I did previously, but I'm not a big fan of the current from
/processor
/to
names.)
// You can specify only the following fields of the ObjectReference: | ||
// - Kind | ||
// - APIVersion | ||
// - Name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about UID?
I'm assuming that Namespace is specifically excluded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about UID?
Is there a good way to lookup a resource by UID?
I'm assuming that Namespace is specifically excluded?
Keeping subscriptions within a single namespace is a good idea until we understand fully understand the tenancy consequences of allowing them to cross namespaces.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's start with names (no UIDs), and a single namespace until we have more understanding on what cases this does not handel?
fe.Details = "the Subscription must reference a Channel" | ||
if ss.From == nil || equality.Semantic.DeepEqual(ss.From, &corev1.ObjectReference{}) { | ||
fe := apis.ErrMissingField("from") | ||
fe.Details = "the Subscription must reference a from channel" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably also ensure that the ss.From.Name
and ss.From.Kind
(at least) are non-empty.
(In general, it feels like there should be a function to validate an ObjectReference
in the context of Subscription.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
le sigh :) Yes, there's bunch of more work to be done here :) Thanks for the comments, I just don't want to keep churning these things until we settle on the shape and the names. I'm going to add validation for now that it can only be a Channel for now as discussed in Slack yesterday, and later on we can relax.
// - APIVersion | ||
// - Name | ||
// +optional | ||
Processor *corev1.ObjectReference `json:"processor,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the idea that you would need to create a k8s Service to be able to call something which is not on the k8s cluster (e.g. a VM)? Previously we had the idea that a targetable was either a DNS name or an ObjectReference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, so, this is a bit interesting in a sense, that I'd expect that if you're hitting something that's outside the cluster, just a DNS entry is probably not going to cut it. I'd expect things like credentials to be involved on that, so as not to just be wide open to everything. I could hoist this into a higher level object to give us some future proofing for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option is to allow the reference for a targetable to also include a k8s Service. It doesn't implement the targetable interface, but we know how to shoehorn it into what we need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@scothis when we talked about this wrt Targetable, thinking was that a service was not enough, that it would actually have to be a URI. So, I'm hoisting that up.
// - APIVersion | ||
// - Name | ||
// +optional | ||
To *corev1.ObjectReference `json:"to,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't comment on other parts of this CRD, so leaving the comment here:
- Can we remove
Arguments
until we need them, and prefer to have Subscription-specific arguments rather than Channel-specificArguments
? (See alsos/[]Arguments/runtime.RawExtension/
.) - What conditions do we need? I think we probably want at least 2 Types:
Ready
andReferencesResolved
, whereReady
is the canonical "happy state" type.- Do we need
LastUpdateTime
andLastTransitionTime
? I'd be inclined to leave off that bookkeeping until we have evidence we need it.
- Do we need
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove
Arguments
until we need them
Yes, we haven't come across a use case for argument/params with the current buses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed.
A subscription triggers sending events from a channel to a subscriber; the subscriber's replies (if any) are forwarded to a channel for subsequent processing. spec:
channel: <channel-ref>
subscriber: <targetable-ref>
replyStrategy:
channel: <channel-ref> The key elements of the SubscriptionSpec are the channel being subscribed to and the subscriber being targeted. Handling replies from the subscriber are managed by a strategy for which we have an two implementations:
As discussed in Slack we want the ability to define other strategies for mapping or dynamic resolution. |
@scothis this was the idea that you'd have Subscription coordinate with the Function what the return headers are and then dispatchers will dynamically route to different channels based on that? |
I'd like to make one last push for preferring nouns since the resource is describing desired state in a declarative way, not imperative logic. I had proposed "channel"->"subscriber" as the essence of what a Subscription describes, and the "replyStrategy" would describe how to handle a reply from the subscriber. That said, if we are going to stick with "call" then I would prefer consistency for what is currently "result" (i.e. if one is a verb, they both should be). The latter could be "forward" or "reply" (I would probably choose "reply" since that is commonly used for describing this role in messaging systems - e.g. via "replyTo" headers - even if it's not immediately replying back to an original caller but instead continuing to flow through a sequence of subsequent channels/subscribers). Also, if we are going to use a preposition, then "from" is actually not very clear, given that one subscribes to a channel (this was even more confusing when we also had "to" for the other direction). |
I want to be clear that I do not think we should support "channel-like things". The thinking was that we may have other kinds of channels in the future. The only concrete thought I have in this direction is a ClusterChannel to enable eventing between namespaces in a clearly defined way. Same behavior, but a separate CRD because that's how k8s works. |
I don't think @n3wscott is proposing that "channel-like things" don't implement the Channel contract; rather ClusterChannel is a perfect example of a "channel-like thing". I think we are in agreement. :) |
"github.com/knative/pkg/webhook" | ||
"k8s.io/api/core/v1" | ||
corev1 "k8s.io/api/core/v1" | ||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-blocking nit: can this be renamed to metav1
for consistency?
Ok, I think this is ready for a review. My most sincere apologies if I missed some comments in the flurry of discussions, here, doc and slack. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/approve
The naming seems good enough to me, just some minor nits. Glad to see that the only file with any real logic has 100% test coverage. Thank you!
} | ||
|
||
func (ss *SubscriptionSpec) SetDefaults() { | ||
// TODO anything? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this file if we don't default anything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with other files, I'm leaving this in for now. If you feel strongly, I can remove.
} | ||
|
||
// Callable specifies the reference to an object that's expected to | ||
// provide the resolved target of the action. Currently we inspect |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Paragraph before "Currently"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
// This ensures that we can support external targets and for ease of use | ||
// we also allow for an URI to be specified. | ||
// There of course is also a requirement for the resolved Callable to | ||
// behave properly at the data plane level. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to document what this means?
I think we mean "Receive an event payload, and respond with one of: success and an optional response event, or failure. Delivery failures may be retries by the from Channel."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done. I felt like we should have a pointer to a spec and have a link to it here. We don't, so added what you proposed.
// we can utilize a dynamic client instead of having to understand all | ||
// kinds of different types of objects. As long as they adhere to this | ||
// particular contract, they can be used as a Target. | ||
// This ensures that we can support external targets and for ease of use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Paragraph (blank line) here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
type SubscriptionConditionType string | ||
|
||
const ( | ||
// SubscriptionReady is when the From,Channel and Result have been reconciled successfully. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"From,Channel and Result"?
"From, Call, and Result"?
Call seems to have both noun and verb definitions in computing (per Google), so I'm okay with these as declarative.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@evankanderson sure "call" can be used as a noun, but IMO "call" is going to be interpreted as a verb by anyone who uses this, especially when combined with "from", which is a preposition and the logical starting point among the components involved thereby setting an imperative context (also as I mentioned earlier, when creating a subscription you are actually subscribing TO a channel, not from).
I think all 3 should be nouns, and not ambiguous ones. I'm personally not even convinced the current "channel" and "subscriber" should change, since those are the most straightforwardly descriptive names for the 2 primary roles. Everything related to the handling of a subscriber's response is a side-effect, except in the (rare) "bridge" case, and for that I think it's best to view as a no-op subscriber since a channel should not be interpreted as the direct subscriber of another channel (even if the "bridge" is logical with no physical resource, it's enforcing the tinker-toy conceptual model, a.k.a. pipes-and-filters). I do see how "replyChannel" or something similar would be a better alternative to "replyTo" since the latter presents the same problem of an imperative context, and if we are going to support dynamic channel resolution in addition to hardwired literal values, then "replyStrategy" could be the top-level as @scothis proposed earlier (with the "mapping" example).
Sorry if this seems like bike-shedding. I do not view it that way, since these are not names used in an internal implementation. When defining the resources that users interact with, the names of those resources and their properties are among the most important things to get right.
} | ||
|
||
// We require always From | ||
// Also at least one of 'call' and 'result' must be defined (non-nill and non-empty) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(You mention "result", but we have no JSON element with that name.)
} | ||
|
||
func isValidCallable(c Callable) *apis.FieldError { | ||
if c.TargetURI != nil && c.Target != nil && !equality.Semantic.DeepEqual(c.Target, &corev1.ObjectReference{}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to check for TargetURI != ""
as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need to check both c.Target != nil
and !equality.Semantic.DeepEqual(c.Target, &corev1.ObjectReference{})
? I'd expect that nil would not be equal to the other object reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something in my readings led me to believe you needed to do this. I could totally be wrong about this however.
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: evankanderson, vaikas-google The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
The following is the coverage report on pkg/.
|
I'd like to unblock this work and suggest that we take the naming
conversation to another thread. We still have tons of work to do before we
can have a working end-to-end implementation and to unblock controller
development, and provisioner implementation and so forth, I'd like to
suggest that if the shape is right, we'll check this in and continue
naming discussion and have an issue to revisit names before the release.
Sound good?
…On Fri, Sep 14, 2018 at 6:05 AM Mark Fisher ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In pkg/apis/eventing/v1alpha1/subscription_types.go
<#421 (comment)>:
> + // This object must fulfill the Sinkable contract.
+ //
+ // TODO: Specify the required fields the target object must
+ // have in the status.
+ // You can specify only the following fields of the ObjectReference:
+ // - Kind
+ // - APIVersion
+ // - Name
+ // +optional
+ Target *corev1.ObjectReference `json:"target,omitempty"`
+}
+
+type SubscriptionConditionType string
+
+const (
+ // SubscriptionReady is when the From,Channel and Result have been reconciled successfully.
@evankanderson <https://github.com/evankanderson> sure "call" *can* be
used as a noun, but IMO "call" is going to be interpreted as a verb by
anyone who uses this, especially when combined with "from", which is a
preposition and the logical starting point among the components involved
thereby setting an imperative context (also as I mentioned earlier, when
creating a subscription you are actually subscribing TO a channel, not
from).
I think all 3 should be nouns, and not ambiguous ones. I'm personally not
even convinced the current "channel" and "subscriber" should change, since
those are the most straightforwardly descriptive names for the 2 primary
roles. Everything related to the handling of a subscriber's response is a
side-effect, except in the (rare) "bridge" case, and for that I think it's
best to view as a no-op subscriber since a channel should not be
interpreted as the direct subscriber of another channel (even if the
"bridge" is logical with no physical resource, it's enforcing the
tinker-toy conceptual model). I do see how "replyChannel" or something
similar would be a better alternative to "replyTo" since the latter
presents the same problem of an imperative context, and if we are going to
support dynamic channel resolution in addition to hardwired literal values,
then "replyStrategy" could be the top-level as @scothis
<https://github.com/scothis> proposed earlier (with the "mapping"
example).
Sorry if this seems like bike-shedding. I do not view it that way, since
these are not names used in an internal implementation. When defining the
resources that users interact with, the names of those resources and their
properties are among the most important things to get right.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#421 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AKwedMPjlgNXqmUORoWJjV8Z1xrefFDNks5ua6mRgaJpZM4WfoH6>
.
|
@vaikas-google that is fine with me. In fact I think having a dedicated discussion about the names in a WG meeting with some proposals being presented with their rationale followed by discussion would be better than having that discussion intermingled with other comments in a PR. I'll make sure we don't forget to follow-up ;-) |
/lgtm |
allow hock for override
Fixes #
Proposed Changes
Add a new version of Subscription CRD as per the new Spec.
**** To reduce code churn, tests are incomplete Please only look at the subscription_types for now****
Release Note