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 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.

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()
}
169 changes: 169 additions & 0 deletions pkg/apis/eventing/v1alpha1/channel_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* 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/apis/duck"
duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1"
"github.com/knative/pkg/webhook"
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 is an abstract resource that implements the Subscribable and Sinkable
// contracts. The Provisioner provisions infrastructure to accepts events and
// deliver to 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)

// Check that ConfigurationStatus may have its conditions managed.
var _ duckv1alpha1.ConditionsAccessor = (*ChannelStatus)(nil)

// Check that Channel implements the Conditions duck type.
var _ = duck.VerifyType(&Channel{}, &duckv1alpha1.Conditions{})

// 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"`
}

var chanCondSet = duckv1alpha1.NewLivingConditionSet(ChannelConditionProvisioned)

// 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
// 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
// TypeMeta.
// +optional
DomainInternal string `json:"domainInternal,omitempty"`

// Represents the latest available observations of a channel's current state.
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
Conditions duckv1alpha1.Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
}

const (
// ChannelConditionReady has status True when the Channel is ready to accept
// traffic.
ChannelConditionReady = duckv1alpha1.ConditionReady

// ChannelConditionProvisioned has status True when the Channel's backing
// resources have been provisioned.
ChannelConditionProvisioned duckv1alpha1.ConditionType = "Provisioned"
)

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

// GetCondition returns the condition currently associated with the given type, or nil.
func (cs *ChannelStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition {
return chanCondSet.Manage(cs).GetCondition(t)
}

// GetConditions returns the Conditions array. This enables generic handling of
// conditions by implementing the duckv1alpha1.Conditions interface.
func (cs *ChannelStatus) GetConditions() duckv1alpha1.Conditions {
return cs.Conditions
}

// SetConditions sets the Conditions array. This enables generic handling of
// conditions by implementing the duckv1alpha1.Conditions interface.
func (cs *ChannelStatus) SetConditions(conditions duckv1alpha1.Conditions) {
cs.Conditions = conditions
}

// +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"`
}
120 changes: 120 additions & 0 deletions pkg/apis/eventing/v1alpha1/channel_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
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"
duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
)

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

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

func TestChannelGetCondition(t *testing.T) {
tests := []struct {
name string
cs *ChannelStatus
condQuery duckv1alpha1.ConditionType
want *duckv1alpha1.Condition
}{{
name: "single condition",
cs: &ChannelStatus{
Conditions: []duckv1alpha1.Condition{
condReady,
},
},
condQuery: duckv1alpha1.ConditionReady,
want: &condReady,
}, {
name: "multiple conditions",
cs: &ChannelStatus{
Conditions: []duckv1alpha1.Condition{
condReady,
condUnprovisioned,
},
},
condQuery: ChannelConditionProvisioned,
want: &condUnprovisioned,
}, {
name: "unknown condition",
cs: &ChannelStatus{
Conditions: []duckv1alpha1.Condition{
condReady,
condUnprovisioned,
},
},
condQuery: duckv1alpha1.ConditionType("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 TestChannelSetConditions(t *testing.T) {
c := &Channel{
Status: ChannelStatus{},
}
want := duckv1alpha1.Conditions{condReady}
c.Status.SetConditions(want)
got := c.Status.GetConditions()
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("unexpected conditions (-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