Skip to content
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

Add a new Channel CRD to the eventing group #430

Merged
merged 17 commits into from
Sep 18, 2018
Merged
Show file tree
Hide file tree
Changes from 13 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
2 changes: 2 additions & 0 deletions cmd/webhook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ func main() {
Options: options,
Handlers: map[schema.GroupVersionKind]runtime.Object{
// For group eventing.knative.dev,
eventingv1alpha1.SchemeGroupVersion.WithKind("Channel"): &eventingv1alpha1.Channel{},
eventingv1alpha1.SchemeGroupVersion.WithKind("ClusterProvisioner"): &eventingv1alpha1.ClusterProvisioner{},
eventingv1alpha1.SchemeGroupVersion.WithKind("Subscription"): &eventingv1alpha1.Subscription{},
Copy link
Contributor

Choose a reason for hiding this comment

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

nice catch


// For group channels.knative.dev,
channelsv1alpha1.SchemeGroupVersion.WithKind("Bus"): &channelsv1alpha1.Bus{},
Expand Down
31 changes: 31 additions & 0 deletions config/300-channeleventing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2018 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.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: channels.eventing.knative.dev
spec:
group: eventing.knative.dev
version: v1alpha1
names:
kind: Channel
plural: channels
singular: channel
categories:
- all
- knative
- eventing
shortNames:
- chan
scope: Namespaced
26 changes: 26 additions & 0 deletions pkg/apis/eventing/v1alpha1/channel_defaults.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
Copyright 2018 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 v1alpha1

//TODO replace this with openapi defaults when
// https://github.com/kubernetes/features/issues/575 lands (scheduled for 1.13)
func (c *Channel) SetDefaults() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there any reason to keep this empty defaults file and others like it? It pulls down our coverage numbers unless we write no-op tests.

Copy link
Member

Choose a reason for hiding this comment

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

+1 to removing.

Copy link
Contributor

Choose a reason for hiding this comment

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

you need to keep func (c *Channel) SetDefaults() { but you can delete func (fs *ChannelSpec) SetDefaults() {

The webhook validation code requires you implement Defaultable.

Copy link
Contributor

Choose a reason for hiding this comment

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

If you would like to change this, you will have to update GenericCRD interface and https://github.com/knative/pkg/blob/a3bc2db77a14d9ca6195172e81b4bf33e6190f85/webhook/webhook.go#L227

c.Spec.SetDefaults()
}

func (fs *ChannelSpec) SetDefaults() {
}
25 changes: 25 additions & 0 deletions pkg/apis/eventing/v1alpha1/channel_defaults_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
Copyright 2018 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 v1alpha1

import "testing"

// No-op test because method does nothing.
Copy link
Contributor

Choose a reason for hiding this comment

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

does nothing yet :D

func TestChannelSetDefaults(t *testing.T) {
c := Channel{}
c.SetDefaults()
}
172 changes: 172 additions & 0 deletions pkg/apis/eventing/v1alpha1/channel_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright 2018 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 v1alpha1

import (
"encoding/json"

"github.com/knative/pkg/apis"
"github.com/knative/pkg/webhook"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

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

// Channel represents a named endpoint on which a Bus accepts event delivery and
// corresponds to the channels.channels.knative.dev CRD. The Bus handles
Copy link
Contributor

Choose a reason for hiding this comment

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

channels.channels.knative.dev -> channels.eventing.knative.dev?

// provisioning channels, delivering events to Channels, and delivering events
// from Channels to their Subscriptions.
type Channel struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`

// Spec defines the desired state of the Channel.
Spec ChannelSpec `json:"spec,omitempty"`

// Status represents the current state of the Channel. This data may be out of
// date.
// +optional
Status ChannelStatus `json:"status,omitempty"`
}

// Check that Channel can be validated, can be defaulted, and has immutable fields.
var _ apis.Validatable = (*Channel)(nil)
var _ apis.Defaultable = (*Channel)(nil)
var _ apis.Immutable = (*Channel)(nil)
var _ runtime.Object = (*Channel)(nil)
var _ webhook.GenericCRD = (*Channel)(nil)

// ChannelSpec specifies the Provisioner backing a channel and the configuration
// arguments for a Channel.
type ChannelSpec struct {
// TODO: Generation does not work correctly with CRD. They are scrubbed
// by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778)
// So, we add Generation here. Once that gets fixed, remove this and use
// ObjectMeta.Generation instead.
// +optional
Generation int64 `json:"generation,omitempty"`

// Provisioner defines the name of the Provisioner backing this channel.
// TODO: +optional If missing, a default Provisioner may be selected for the Channel.
Provisioner *ProvisionerReference `json:"provisioner,omitempty"`

// Arguments defines the arguments to pass to the Provisioner which provisions
// this Channel.
// +optional
Arguments *runtime.RawExtension `json:"arguments,omitempty"`

// Subscribers is a list of the Subscribers to this channel. This is filled in
// by the Subscriptions controller. Users should not mutate this field.
Subscribers []ChannelSubscriberSpec `json:"subscribers,omitempty"`
}

// ChannelSubscriberSpec defines a single subscriber to a Channel. At least one
// of Call or Result must be present.
type ChannelSubscriberSpec struct {
// Call is an optional reference to a function for processing events.
// Events from the From channel will be delivered here and replies
// are optionally handled by Result.
// +optional
Call *Callable `json:"call,omitempty"`

// Result optionally specifies how to handle events received from the Call
// target.
// +optional
Result *ResultStrategy `json:"result,omitempty"`
}

// ChannelStatus represents the current state of a Channel.
type ChannelStatus struct {
// ObservedGeneration is the most recent generation observed for this Channel.
// It corresponds to the Channel's generation, which is updated on mutation by
// the API Server.
//TODO The above comment is only true once
Copy link
Contributor

Choose a reason for hiding this comment

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

malformed TODO. Should be:

// TODO: The above comment is only true once

// https://github.com/kubernetes/kubernetes/issues/58778 is fixed.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`

// DomainInternal holds the top-level domain that will distribute traffic
// over the provided targets from inside the cluster. It generally has the
// form {channel}.{namespace}.svc.cluster.local
//TODO move this to a struct that can be embedded similar to ObjectMeta and
Copy link
Contributor

Choose a reason for hiding this comment

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

malformed TODO. See above

// TypeMeta.
// +optional
DomainInternal string `json:"domainInternal,omitempty"`

// Represents the latest available observations of a channel's current state.
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
Conditions []ChannelCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
Copy link
Contributor

Choose a reason for hiding this comment

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

you will want to update to the style of #434

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let's get #434 in first, reviewing now :)

Copy link
Contributor

Choose a reason for hiding this comment

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

It is in now!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to use duck Conditions.

}

type ChannelConditionType string

const (
// ChannelConditionReady has status True when the Channel is ready to accept
// traffic.
ChannelConditionReady ChannelConditionType = "Ready"
Copy link
Contributor

Choose a reason for hiding this comment

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

This will turn into ChannelConditionReady = duck.ConditionReady


// ChannelConditionProvisioned has status True when the Channel's backing
// resources have been provisioned.
ChannelConditionProvisioned ChannelConditionType = "Provisioned"
Copy link
Contributor

Choose a reason for hiding this comment

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

duck.ConditionType

)

// ChannelCondition describes the state of a channel at a point in time.
type ChannelCondition struct {
// Type of channel condition.
Type ChannelConditionType `json:"type"`
// Status of the condition, one of True, False, Unknown.
Status corev1.ConditionStatus `json:"status"`
// LastTransitionTime from one status to another.
// We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic
// differences (all other things held constant).
LastTransitionTime apis.VolatileTime `json:"lastTransitionTime,omitempty"`
// Reason for the condition's last transition.
Reason string `json:"reason,omitempty"`
// Message is a human readable message indicating details about the
// last transition.
Message string `json:"message,omitempty"`
}

func (cs *ChannelStatus) GetCondition(t ChannelConditionType) *ChannelCondition {
for _, cond := range cs.Conditions {
if cond.Type == t {
return &cond
}
}
return nil
}

func (c *Channel) GetSpecJSON() ([]byte, error) {
return json.Marshal(c.Spec)
}

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

// ChannelList is a collection of Channels.
type ChannelList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
Items []Channel `json:"items"`
}
107 changes: 107 additions & 0 deletions pkg/apis/eventing/v1alpha1/channel_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
Copyright 2018 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 v1alpha1

import (
"testing"

"github.com/google/go-cmp/cmp"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
)

var condReady = ChannelCondition{
Type: ChannelConditionReady,
Status: corev1.ConditionTrue,
}

var condUnprovisioned = ChannelCondition{
Type: ChannelConditionProvisioned,
Status: corev1.ConditionFalse,
}

func TestChannelGetCondition(t *testing.T) {
tests := []struct {
name string
cs *ChannelStatus
condQuery ChannelConditionType
want *ChannelCondition
}{{
name: "single condition",
cs: &ChannelStatus{
Conditions: []ChannelCondition{
condReady,
},
},
condQuery: ChannelConditionReady,
want: &condReady,
}, {
name: "multiple conditions",
cs: &ChannelStatus{
Conditions: []ChannelCondition{
condReady,
condUnprovisioned,
},
},
condQuery: ChannelConditionProvisioned,
want: &condUnprovisioned,
}, {
name: "unknown condition",
cs: &ChannelStatus{
Conditions: []ChannelCondition{
condReady,
condUnprovisioned,
},
},
condQuery: ChannelConditionType("foo"),
want: nil,
}}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := test.cs.GetCondition(test.condQuery)
if diff := cmp.Diff(test.want, got); diff != "" {
t.Errorf("unexpected condition (-want, +got) = %v", diff)
}
})
}
}

func TestChannelGetSpecJSON(t *testing.T) {
c := &Channel{
Spec: ChannelSpec{
Provisioner: &ProvisionerReference{
Ref: &corev1.ObjectReference{
Name: "foo",
},
},
Arguments: &runtime.RawExtension{
Raw: []byte(`{"foo":"baz"}`),
},
},
}

want := `{"provisioner":{"ref":{"name":"foo"}},"arguments":{"foo":"baz"}}`
got, err := c.GetSpecJSON()
if err != nil {
t.Fatalf("unexpected spec JSON error: %v", err)
}

if diff := cmp.Diff(want, string(got)); diff != "" {
t.Errorf("unexpected spec JSON (-want, +got) = %v", diff)
}
}
Loading