Skip to content
1 change: 1 addition & 0 deletions cmd/webhook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ func main() {
// For group eventing.knative.dev,
eventingv1alpha1.SchemeGroupVersion.WithKind("Channel"): &eventingv1alpha1.Channel{},
eventingv1alpha1.SchemeGroupVersion.WithKind("ClusterProvisioner"): &eventingv1alpha1.ClusterProvisioner{},
eventingv1alpha1.SchemeGroupVersion.WithKind("Source"): &eventingv1alpha1.Source{},
eventingv1alpha1.SchemeGroupVersion.WithKind("Subscription"): &eventingv1alpha1.Subscription{},

// For group channels.knative.dev,
Expand Down
29 changes: 29 additions & 0 deletions config/300-source.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 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: sources.eventing.knative.dev
spec:
group: eventing.knative.dev
version: v1alpha1
names:
kind: Sources
plural: sources
singular: source
categories:
- all
- knative
- eventing
scope: Namespaced
25 changes: 7 additions & 18 deletions pkg/apis/eventing/v1alpha1/channel_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,9 @@ import (
var targetURI = "https://example.com"

func TestChannelValidation(t *testing.T) {
tests := []struct {
name string
c *Channel
want *apis.FieldError
}{{
tests := []CRDTest{{
name: "valid",
c: &Channel{
cr: &Channel{
Spec: ChannelSpec{
Provisioner: &ProvisionerReference{
Ref: &corev1.ObjectReference{
Expand All @@ -47,13 +43,13 @@ func TestChannelValidation(t *testing.T) {
want: nil,
}, {
name: "empty",
c: &Channel{
cr: &Channel{
Spec: ChannelSpec{},
},
want: apis.ErrMissingField("spec.provisioner"),
}, {
name: "subscribers array",
c: &Channel{
cr: &Channel{
Spec: ChannelSpec{
Provisioner: &ProvisionerReference{
Ref: &corev1.ObjectReference{
Expand All @@ -70,7 +66,7 @@ func TestChannelValidation(t *testing.T) {
want: nil,
}, {
name: "empty subscriber at index 1",
c: &Channel{
cr: &Channel{
Spec: ChannelSpec{
Provisioner: &ProvisionerReference{
Ref: &corev1.ObjectReference{
Expand All @@ -91,7 +87,7 @@ func TestChannelValidation(t *testing.T) {
}(),
}, {
name: "2 empty subscribers",
c: &Channel{
cr: &Channel{
Spec: ChannelSpec{
Provisioner: &ProvisionerReference{
Ref: &corev1.ObjectReference{
Expand All @@ -115,14 +111,7 @@ func TestChannelValidation(t *testing.T) {
}(),
}}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := test.c.Validate()
if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" {
t.Errorf("validateChannel (-want, +got) = %v", diff)
}
})
}
doValidateTest(t, tests)
}

func TestChannelImmutableFields(t *testing.T) {
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ var cProvCondSet = duckv1alpha1.NewLivingConditionSet()
// ClusterProvisionerStatus is the status for a ClusterProvisioner resource
type ClusterProvisionerStatus struct {
// Conditions holds the state of a cluster provisioner at a point in time.
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
Conditions duckv1alpha1.Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`

// ObservedGeneration is the 'Generation' of the ClusterProvisioner that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"testing"
)

func TestValidate(t *testing.T) {
func TestClusterProvisionerValidate(t *testing.T) {
tests := []struct {
name string
p *ClusterProvisioner
Expand Down
41 changes: 41 additions & 0 deletions pkg/apis/eventing/v1alpha1/crd_validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
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 (
"github.com/google/go-cmp/cmp"
"github.com/knative/pkg/apis"
"github.com/knative/pkg/webhook"
"testing"
)

type CRDTest struct {
name string
cr webhook.GenericCRD
want *apis.FieldError
}

func doValidateTest(t *testing.T, tests []CRDTest) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := test.cr.Validate()
if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" {
t.Errorf("%s: validate (-want, +got) = %v", test.name, diff)
}
})
}
}
27 changes: 27 additions & 0 deletions pkg/apis/eventing/v1alpha1/source_defaults.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
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

// SetDefaults defaults
func (s *Source) SetDefaults() {
s.Spec.SetDefaults()
}

// SetDefaults defaults the Source spec.
func (ss *SourceSpec) SetDefaults() {
// no defaults
}
27 changes: 27 additions & 0 deletions pkg/apis/eventing/v1alpha1/source_defaults_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
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.
func TestSourceSetDefaults(t *testing.T) {
s := Source{}
s.SetDefaults()
}
171 changes: 171 additions & 0 deletions pkg/apis/eventing/v1alpha1/source_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
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 (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/knative/pkg/apis/duck"
duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1"
"github.com/knative/pkg/webhook"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
)

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

// Source resource Describes a specific configuration (credentials, etc) of a
// source system which can be used to supply events. Sources emit events using a
// channel specified in their status. They cannot receive events.
type Source struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`

// Spec defines the the Provisioner and arguments provided for this Source.
Spec SourceSpec `json:"spec"`

// Status is the current status of the Source.
// +optional
Status SourceStatus `json:"status,omitempty"`
}

// Check that Source can be validated and can be defaulted.
var _ webhook.GenericCRD = (*Source)(nil)

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

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

// Check that Source implements the Generation duck type.
var emptyGenSource duckv1alpha1.Generation
var _ = duck.VerifyType(&Source{}, &emptyGenSource)

// And it's Subscribable
var _ = duck.VerifyType(&Subscription{}, &duckv1alpha1.Subscribable{})

// SourceSpec is the spec for a Source resource.
type SourceSpec 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 is used to create any backing resources and configuration.
// +required
Provisioner *ProvisionerReference `json:"provisioner,omitempty"`

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

// Specify an existing channel to use to emit events. If empty, create a new
// Channel using the cluster/namespace default.
Comment thread
n3wscott marked this conversation as resolved.
//
// This object must fulfill the Channelable contract.
//
// You can specify only the following fields of the ObjectReference:
// - Kind
// - APIVersion
// - Name
// Currently Kind must be "Channel" and
// APIVersion must be "eventing.knative.dev/v1alpha1"
// +optional
Channel *corev1.ObjectReference `json:"target,omitempty"`
}

const (
// SourceConditionReady has status True when the Source is ready to send events.
SourceConditionReady = duckv1alpha1.ConditionReady

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

var sourceCondSet = duckv1alpha1.NewLivingConditionSet(SourceConditionProvisioned)

// SourceStatus is the status for a Source resource
type SourceStatus struct {
// Conditions holds the state of a source at a point in time.
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
Conditions duckv1alpha1.Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`

// ObservedGeneration is the 'Generation' of the Source that
// was last reconciled by the controller.
// +optional
ObservedGeneration int64 `json:"observedGeneration,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.

Not really part of this PR, but I just noticed Subscription doesn't have an ObservedGeneration field. Should it?

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.

yes.


// Source might be Subscribable. This points to the Channelable object.
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.

might? I think it must be Subscribable, how else would we get events from it? Is this CRD the "factory" for say, creating K8S events for consumption by the system, or is an instance one that I could create a Subscription.Spec.From to point to?

Subscribable duckv1alpha1.Subscribable `json:"subscribable,omitempty"`
}

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

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

// IsReady returns true if the resource is ready overall.
func (ss *SourceStatus) IsReady() bool {
return sourceCondSet.Manage(ss).IsHappy()
}

// InitializeConditions sets relevant unset conditions to Unknown state.
func (ss *SourceStatus) InitializeConditions() {
sourceCondSet.Manage(ss).InitializeConditions()
}

// MarkProvisioned sets the condition that the source has had its backing resources created.
func (ss *SourceStatus) MarkProvisioned() {
sourceCondSet.Manage(ss).MarkTrue(SourceConditionProvisioned)
}

// MarkDeprovisioned sets the condition that the source has had its backing resources removed.
func (ss *SourceStatus) MarkDeprovisioned(reason, messageFormat string, messageA ...interface{}) {
sourceCondSet.Manage(ss).MarkFalse(SourceConditionProvisioned, reason, messageFormat, messageA)
}

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

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

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

Items []Source `json:"items"`
}
Loading