From 707e3969c98f891468d66192ca9ca7c6dc7da5a8 Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Fri, 21 Sep 2018 13:54:02 -0700 Subject: [PATCH 01/11] Added the shell of a source. followup with real insides. --- cmd/webhook/main.go | 1 + config/300-source.yaml | 29 ++++ .../cluster_provisioner_validation_test.go | 2 +- .../eventing/v1alpha1/crd_validation_test.go | 41 ++++++ pkg/apis/eventing/v1alpha1/source_defaults.go | 27 ++++ .../eventing/v1alpha1/source_defaults_test.go | 27 ++++ pkg/apis/eventing/v1alpha1/source_types.go | 119 ++++++++++++++++ .../eventing/v1alpha1/source_types_test.go | 130 ++++++++++++++++++ .../eventing/v1alpha1/source_validation.go | 39 ++++++ .../v1alpha1/source_validation_test.go | 34 +++++ .../v1alpha1/zz_generated.deepcopy.go | 100 ++++++++++++++ .../eventing/v1alpha1/eventing_client.go | 5 + .../v1alpha1/fake/fake_eventing_client.go | 4 + .../eventing/v1alpha1/generated_expansion.go | 2 + .../eventing/v1alpha1/interface.go | 7 + .../informers/externalversions/generic.go | 2 + .../eventing/v1alpha1/expansion_generated.go | 8 ++ 17 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 config/300-source.yaml create mode 100644 pkg/apis/eventing/v1alpha1/crd_validation_test.go create mode 100644 pkg/apis/eventing/v1alpha1/source_defaults.go create mode 100644 pkg/apis/eventing/v1alpha1/source_defaults_test.go create mode 100644 pkg/apis/eventing/v1alpha1/source_types.go create mode 100644 pkg/apis/eventing/v1alpha1/source_types_test.go create mode 100644 pkg/apis/eventing/v1alpha1/source_validation.go create mode 100644 pkg/apis/eventing/v1alpha1/source_validation_test.go diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index aefd15caff0..8388bb45502 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -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, diff --git a/config/300-source.yaml b/config/300-source.yaml new file mode 100644 index 00000000000..82942812712 --- /dev/null +++ b/config/300-source.yaml @@ -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 diff --git a/pkg/apis/eventing/v1alpha1/cluster_provisioner_validation_test.go b/pkg/apis/eventing/v1alpha1/cluster_provisioner_validation_test.go index 9629ca91f34..7d77e4a6d8c 100644 --- a/pkg/apis/eventing/v1alpha1/cluster_provisioner_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/cluster_provisioner_validation_test.go @@ -23,7 +23,7 @@ import ( "testing" ) -func TestValidate(t *testing.T) { +func TestClusterProvisionerValidate(t *testing.T) { tests := []struct { name string p *ClusterProvisioner diff --git a/pkg/apis/eventing/v1alpha1/crd_validation_test.go b/pkg/apis/eventing/v1alpha1/crd_validation_test.go new file mode 100644 index 00000000000..fcc760bbe36 --- /dev/null +++ b/pkg/apis/eventing/v1alpha1/crd_validation_test.go @@ -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("validate (-want, +got) = %v", diff) + } + }) + } +} diff --git a/pkg/apis/eventing/v1alpha1/source_defaults.go b/pkg/apis/eventing/v1alpha1/source_defaults.go new file mode 100644 index 00000000000..9d417f8e35a --- /dev/null +++ b/pkg/apis/eventing/v1alpha1/source_defaults.go @@ -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 +} diff --git a/pkg/apis/eventing/v1alpha1/source_defaults_test.go b/pkg/apis/eventing/v1alpha1/source_defaults_test.go new file mode 100644 index 00000000000..2373f9a70c8 --- /dev/null +++ b/pkg/apis/eventing/v1alpha1/source_defaults_test.go @@ -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() +} diff --git a/pkg/apis/eventing/v1alpha1/source_types.go b/pkg/apis/eventing/v1alpha1/source_types.go new file mode 100644 index 00000000000..b1ee80cea0b --- /dev/null +++ b/pkg/apis/eventing/v1alpha1/source_types.go @@ -0,0 +1,119 @@ +/* +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" +) + +// +genclient +// +genclient:noStatus +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Source +type Source struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the Types provisioned by 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 emptyGen duckv1alpha1.Generation +var _ = duck.VerifyType(&Source{}, &emptyGen) + +// 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"` + + Todo string `json:"todo,omitempty"` + // TODO: add real params. +} + +var sourceCondSet = duckv1alpha1.NewLivingConditionSet() + +// SourceStatus is the status for a Source resource +type SourceStatus struct { + // Conditions holds the state of a cluster provisioner at a point in time. + 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"` +} + +// 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() +} + +// 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"` +} diff --git a/pkg/apis/eventing/v1alpha1/source_types_test.go b/pkg/apis/eventing/v1alpha1/source_types_test.go new file mode 100644 index 00000000000..f39bd5367e6 --- /dev/null +++ b/pkg/apis/eventing/v1alpha1/source_types_test.go @@ -0,0 +1,130 @@ +/* +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" +) + +func TestSourceStatusIsReady(t *testing.T) { + tests := []struct { + name string + s *SourceStatus + want bool + }{{ + name: "uninitialized", + s: &SourceStatus{}, + want: false, + }, { + name: "initialized", + s: func() *SourceStatus { + s := &SourceStatus{} + s.InitializeConditions() + return s + }(), + want: false, + }, { + name: "ready true condition", + s: &SourceStatus{ + Conditions: []duckv1alpha1.Condition{{ + Type: ChannelConditionReady, + Status: corev1.ConditionTrue, + }}, + }, + want: true, + }, { + name: "ready false condition", + s: &SourceStatus{ + Conditions: []duckv1alpha1.Condition{{ + Type: ChannelConditionReady, + Status: corev1.ConditionFalse, + }}, + }, + want: false, + }, { + name: "unknown condition", + s: &SourceStatus{ + Conditions: []duckv1alpha1.Condition{{ + Type: "foo", + Status: corev1.ConditionTrue, + }}, + }, + want: false, + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := test.s.IsReady() + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("%s: unexpected condition (-want, +got) = %v", test.name, diff) + } + }) + } +} + +func TestSourceStatusGetCondition(t *testing.T) { + tests := []struct { + name string + s *SourceStatus + condQuery duckv1alpha1.ConditionType + want *duckv1alpha1.Condition + }{{ + name: "single condition", + s: &SourceStatus{ + Conditions: []duckv1alpha1.Condition{ + condReady, + }, + }, + condQuery: duckv1alpha1.ConditionReady, + want: &condReady, + }, { + name: "unknown condition", + s: &SourceStatus{ + 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.s.GetCondition(test.condQuery) + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("unexpected condition (-want, +got) = %v", diff) + } + }) + } +} + +func TestSourceGetSetConditions(t *testing.T) { + c := &Source{ + Status: SourceStatus{}, + } + 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) + } +} diff --git a/pkg/apis/eventing/v1alpha1/source_validation.go b/pkg/apis/eventing/v1alpha1/source_validation.go new file mode 100644 index 00000000000..a73cb116c46 --- /dev/null +++ b/pkg/apis/eventing/v1alpha1/source_validation.go @@ -0,0 +1,39 @@ +/* +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/knative/pkg/apis" + "k8s.io/apimachinery/pkg/api/equality" +) + +// Validate validates the Source resource. +func (s *Source) Validate() *apis.FieldError { + return s.Spec.Validate().ViaField("spec") +} + +// Validate validates the Source spec +func (ss *SourceSpec) Validate() *apis.FieldError { + if equality.Semantic.DeepEqual(ss, &SourceSpec{}) { + return apis.ErrMissingField("todo") + } + var errs *apis.FieldError + + // TODO: add real validation after adding fields. + + return errs +} diff --git a/pkg/apis/eventing/v1alpha1/source_validation_test.go b/pkg/apis/eventing/v1alpha1/source_validation_test.go new file mode 100644 index 00000000000..ef2e35b331a --- /dev/null +++ b/pkg/apis/eventing/v1alpha1/source_validation_test.go @@ -0,0 +1,34 @@ +/* +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" +) + +func TestSourceValidate(t *testing.T) { + tests := []CRDTest{{ + name: "valid", + cr: &Source{ + Spec: SourceSpec{ + Todo: "todo", + }, + }, + }} + + doValidateTest(t, tests) +} diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index ce044896091..6c44dcc592f 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -370,6 +370,106 @@ func (in *ResultStrategy) DeepCopy() *ResultStrategy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Source) DeepCopyInto(out *Source) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Source. +func (in *Source) DeepCopy() *Source { + if in == nil { + return nil + } + out := new(Source) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Source) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceList) DeepCopyInto(out *SourceList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Source, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceList. +func (in *SourceList) DeepCopy() *SourceList { + if in == nil { + return nil + } + out := new(SourceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *SourceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceSpec) DeepCopyInto(out *SourceSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceSpec. +func (in *SourceSpec) DeepCopy() *SourceSpec { + if in == nil { + return nil + } + out := new(SourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SourceStatus) DeepCopyInto(out *SourceStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(duck_v1alpha1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceStatus. +func (in *SourceStatus) DeepCopy() *SourceStatus { + if in == nil { + return nil + } + out := new(SourceStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Subscription) DeepCopyInto(out *Subscription) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go index 3d84eebe8e4..c9474ed0042 100644 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go @@ -29,6 +29,7 @@ type EventingV1alpha1Interface interface { RESTClient() rest.Interface ChannelsGetter ClusterProvisionersGetter + SourcesGetter SubscriptionsGetter } @@ -45,6 +46,10 @@ func (c *EventingV1alpha1Client) ClusterProvisioners() ClusterProvisionerInterfa return newClusterProvisioners(c) } +func (c *EventingV1alpha1Client) Sources(namespace string) SourceInterface { + return newSources(c, namespace) +} + func (c *EventingV1alpha1Client) Subscriptions(namespace string) SubscriptionInterface { return newSubscriptions(c, namespace) } diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go index 0127acc10fb..dfd67eea8b1 100644 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go @@ -36,6 +36,10 @@ func (c *FakeEventingV1alpha1) ClusterProvisioners() v1alpha1.ClusterProvisioner return &FakeClusterProvisioners{c} } +func (c *FakeEventingV1alpha1) Sources(namespace string) v1alpha1.SourceInterface { + return &FakeSources{c, namespace} +} + func (c *FakeEventingV1alpha1) Subscriptions(namespace string) v1alpha1.SubscriptionInterface { return &FakeSubscriptions{c, namespace} } diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go index 2d119a1cc27..121200ef9f9 100644 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go @@ -22,4 +22,6 @@ type ChannelExpansion interface{} type ClusterProvisionerExpansion interface{} +type SourceExpansion interface{} + type SubscriptionExpansion interface{} diff --git a/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go b/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go index 1fd4c99c7dc..2a44cf0f052 100644 --- a/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go @@ -28,6 +28,8 @@ type Interface interface { Channels() ChannelInformer // ClusterProvisioners returns a ClusterProvisionerInformer. ClusterProvisioners() ClusterProvisionerInformer + // Sources returns a SourceInformer. + Sources() SourceInformer // Subscriptions returns a SubscriptionInformer. Subscriptions() SubscriptionInformer } @@ -53,6 +55,11 @@ func (v *version) ClusterProvisioners() ClusterProvisionerInformer { return &clusterProvisionerInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } +// Sources returns a SourceInformer. +func (v *version) Sources() SourceInformer { + return &sourceInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // Subscriptions returns a SubscriptionInformer. func (v *version) Subscriptions() SubscriptionInformer { return &subscriptionInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 521a45e5029..64978278fda 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -70,6 +70,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().Channels().Informer()}, nil case eventing_v1alpha1.SchemeGroupVersion.WithResource("clusterprovisioners"): return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().ClusterProvisioners().Informer()}, nil + case eventing_v1alpha1.SchemeGroupVersion.WithResource("sources"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().Sources().Informer()}, nil case eventing_v1alpha1.SchemeGroupVersion.WithResource("subscriptions"): return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().Subscriptions().Informer()}, nil diff --git a/pkg/client/listers/eventing/v1alpha1/expansion_generated.go b/pkg/client/listers/eventing/v1alpha1/expansion_generated.go index abc32a5ee74..f3854a6804d 100644 --- a/pkg/client/listers/eventing/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/eventing/v1alpha1/expansion_generated.go @@ -30,6 +30,14 @@ type ChannelNamespaceListerExpansion interface{} // ClusterProvisionerLister. type ClusterProvisionerListerExpansion interface{} +// SourceListerExpansion allows custom methods to be added to +// SourceLister. +type SourceListerExpansion interface{} + +// SourceNamespaceListerExpansion allows custom methods to be added to +// SourceNamespaceLister. +type SourceNamespaceListerExpansion interface{} + // SubscriptionListerExpansion allows custom methods to be added to // SubscriptionLister. type SubscriptionListerExpansion interface{} From 2c52c3a85180680161a6efa9dd20153495bcc976 Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Fri, 21 Sep 2018 13:55:33 -0700 Subject: [PATCH 02/11] adding generated parts. --- .../eventing/v1alpha1/fake/fake_source.go | 128 ++++++++++++++ .../typed/eventing/v1alpha1/source.go | 157 ++++++++++++++++++ .../eventing/v1alpha1/source.go | 89 ++++++++++ .../listers/eventing/v1alpha1/source.go | 94 +++++++++++ 4 files changed, 468 insertions(+) create mode 100644 pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_source.go create mode 100644 pkg/client/clientset/versioned/typed/eventing/v1alpha1/source.go create mode 100644 pkg/client/informers/externalversions/eventing/v1alpha1/source.go create mode 100644 pkg/client/listers/eventing/v1alpha1/source.go diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_source.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_source.go new file mode 100644 index 00000000000..7fa9395eaa4 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_source.go @@ -0,0 +1,128 @@ +/* +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeSources implements SourceInterface +type FakeSources struct { + Fake *FakeEventingV1alpha1 + ns string +} + +var sourcesResource = schema.GroupVersionResource{Group: "eventing.knative.dev", Version: "v1alpha1", Resource: "sources"} + +var sourcesKind = schema.GroupVersionKind{Group: "eventing.knative.dev", Version: "v1alpha1", Kind: "Source"} + +// Get takes name of the source, and returns the corresponding source object, and an error if there is any. +func (c *FakeSources) Get(name string, options v1.GetOptions) (result *v1alpha1.Source, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(sourcesResource, c.ns, name), &v1alpha1.Source{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Source), err +} + +// List takes label and field selectors, and returns the list of Sources that match those selectors. +func (c *FakeSources) List(opts v1.ListOptions) (result *v1alpha1.SourceList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(sourcesResource, sourcesKind, c.ns, opts), &v1alpha1.SourceList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.SourceList{ListMeta: obj.(*v1alpha1.SourceList).ListMeta} + for _, item := range obj.(*v1alpha1.SourceList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested sources. +func (c *FakeSources) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(sourcesResource, c.ns, opts)) + +} + +// Create takes the representation of a source and creates it. Returns the server's representation of the source, and an error, if there is any. +func (c *FakeSources) Create(source *v1alpha1.Source) (result *v1alpha1.Source, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(sourcesResource, c.ns, source), &v1alpha1.Source{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Source), err +} + +// Update takes the representation of a source and updates it. Returns the server's representation of the source, and an error, if there is any. +func (c *FakeSources) Update(source *v1alpha1.Source) (result *v1alpha1.Source, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(sourcesResource, c.ns, source), &v1alpha1.Source{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Source), err +} + +// Delete takes name of the source and deletes it. Returns an error if one occurs. +func (c *FakeSources) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(sourcesResource, c.ns, name), &v1alpha1.Source{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeSources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(sourcesResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.SourceList{}) + return err +} + +// Patch applies the patch and returns the patched source. +func (c *FakeSources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Source, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(sourcesResource, c.ns, name, data, subresources...), &v1alpha1.Source{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Source), err +} diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/source.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/source.go new file mode 100644 index 00000000000..f0a4a8a74f7 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/source.go @@ -0,0 +1,157 @@ +/* +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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// SourcesGetter has a method to return a SourceInterface. +// A group's client should implement this interface. +type SourcesGetter interface { + Sources(namespace string) SourceInterface +} + +// SourceInterface has methods to work with Source resources. +type SourceInterface interface { + Create(*v1alpha1.Source) (*v1alpha1.Source, error) + Update(*v1alpha1.Source) (*v1alpha1.Source, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.Source, error) + List(opts v1.ListOptions) (*v1alpha1.SourceList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Source, err error) + SourceExpansion +} + +// sources implements SourceInterface +type sources struct { + client rest.Interface + ns string +} + +// newSources returns a Sources +func newSources(c *EventingV1alpha1Client, namespace string) *sources { + return &sources{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the source, and returns the corresponding source object, and an error if there is any. +func (c *sources) Get(name string, options v1.GetOptions) (result *v1alpha1.Source, err error) { + result = &v1alpha1.Source{} + err = c.client.Get(). + Namespace(c.ns). + Resource("sources"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Sources that match those selectors. +func (c *sources) List(opts v1.ListOptions) (result *v1alpha1.SourceList, err error) { + result = &v1alpha1.SourceList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("sources"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested sources. +func (c *sources) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("sources"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a source and creates it. Returns the server's representation of the source, and an error, if there is any. +func (c *sources) Create(source *v1alpha1.Source) (result *v1alpha1.Source, err error) { + result = &v1alpha1.Source{} + err = c.client.Post(). + Namespace(c.ns). + Resource("sources"). + Body(source). + Do(). + Into(result) + return +} + +// Update takes the representation of a source and updates it. Returns the server's representation of the source, and an error, if there is any. +func (c *sources) Update(source *v1alpha1.Source) (result *v1alpha1.Source, err error) { + result = &v1alpha1.Source{} + err = c.client.Put(). + Namespace(c.ns). + Resource("sources"). + Name(source.Name). + Body(source). + Do(). + Into(result) + return +} + +// Delete takes name of the source and deletes it. Returns an error if one occurs. +func (c *sources) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("sources"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *sources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("sources"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched source. +func (c *sources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Source, err error) { + result = &v1alpha1.Source{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("sources"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/eventing/v1alpha1/source.go b/pkg/client/informers/externalversions/eventing/v1alpha1/source.go new file mode 100644 index 00000000000..eb06180eed1 --- /dev/null +++ b/pkg/client/informers/externalversions/eventing/v1alpha1/source.go @@ -0,0 +1,89 @@ +/* +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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + eventing_v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + versioned "github.com/knative/eventing/pkg/client/clientset/versioned" + internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/knative/eventing/pkg/client/listers/eventing/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// SourceInformer provides access to a shared informer and lister for +// Sources. +type SourceInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.SourceLister +} + +type sourceInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewSourceInformer constructs a new informer for Source type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewSourceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredSourceInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredSourceInformer constructs a new informer for Source type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredSourceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.EventingV1alpha1().Sources(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.EventingV1alpha1().Sources(namespace).Watch(options) + }, + }, + &eventing_v1alpha1.Source{}, + resyncPeriod, + indexers, + ) +} + +func (f *sourceInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredSourceInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *sourceInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&eventing_v1alpha1.Source{}, f.defaultInformer) +} + +func (f *sourceInformer) Lister() v1alpha1.SourceLister { + return v1alpha1.NewSourceLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/listers/eventing/v1alpha1/source.go b/pkg/client/listers/eventing/v1alpha1/source.go new file mode 100644 index 00000000000..ec5c3a667ba --- /dev/null +++ b/pkg/client/listers/eventing/v1alpha1/source.go @@ -0,0 +1,94 @@ +/* +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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// SourceLister helps list Sources. +type SourceLister interface { + // List lists all Sources in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.Source, err error) + // Sources returns an object that can list and get Sources. + Sources(namespace string) SourceNamespaceLister + SourceListerExpansion +} + +// sourceLister implements the SourceLister interface. +type sourceLister struct { + indexer cache.Indexer +} + +// NewSourceLister returns a new SourceLister. +func NewSourceLister(indexer cache.Indexer) SourceLister { + return &sourceLister{indexer: indexer} +} + +// List lists all Sources in the indexer. +func (s *sourceLister) List(selector labels.Selector) (ret []*v1alpha1.Source, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Source)) + }) + return ret, err +} + +// Sources returns an object that can list and get Sources. +func (s *sourceLister) Sources(namespace string) SourceNamespaceLister { + return sourceNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// SourceNamespaceLister helps list and get Sources. +type SourceNamespaceLister interface { + // List lists all Sources in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.Source, err error) + // Get retrieves the Source from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.Source, error) + SourceNamespaceListerExpansion +} + +// sourceNamespaceLister implements the SourceNamespaceLister +// interface. +type sourceNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Sources in the indexer for a given namespace. +func (s sourceNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Source, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Source)) + }) + return ret, err +} + +// Get retrieves the Source from the indexer for a given namespace and name. +func (s sourceNamespaceLister) Get(name string) (*v1alpha1.Source, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("source"), name) + } + return obj.(*v1alpha1.Source), nil +} From c086a4a811fa8b580a9096ef4caac72358295d14 Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Fri, 21 Sep 2018 15:24:28 -0700 Subject: [PATCH 03/11] added source spec fields and tests. --- .../v1alpha1/channel_validation_test.go | 25 ++--- .../v1alpha1/cluster_provisioner_types.go | 3 + .../eventing/v1alpha1/crd_validation_test.go | 2 +- pkg/apis/eventing/v1alpha1/source_types.go | 52 ++++++++- .../eventing/v1alpha1/source_types_test.go | 28 ++++- .../eventing/v1alpha1/source_validation.go | 8 +- .../v1alpha1/source_validation_test.go | 80 +++++++++++++- .../v1alpha1/subscribable_validation.go | 101 ++++++++++++++++++ .../v1alpha1/subscription_validation.go | 68 +----------- .../v1alpha1/subscription_validation_test.go | 6 +- .../v1alpha1/zz_generated.deepcopy.go | 29 ++++- 11 files changed, 302 insertions(+), 100 deletions(-) create mode 100644 pkg/apis/eventing/v1alpha1/subscribable_validation.go diff --git a/pkg/apis/eventing/v1alpha1/channel_validation_test.go b/pkg/apis/eventing/v1alpha1/channel_validation_test.go index 3423363ce8d..eebd48eedbd 100644 --- a/pkg/apis/eventing/v1alpha1/channel_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/channel_validation_test.go @@ -28,13 +28,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{ @@ -46,13 +42,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{ @@ -88,7 +84,7 @@ func TestChannelValidation(t *testing.T) { want: nil, }, { name: "empty subscriber", - c: &Channel{ + cr: &Channel{ Spec: ChannelSpec{ Provisioner: &ProvisionerReference{ Ref: &corev1.ObjectReference{ @@ -105,7 +101,7 @@ func TestChannelValidation(t *testing.T) { want: apis.ErrMissingField("spec.subscriber[1].call", "spec.subscriber[1].result"), }, { name: "2 empty subscribers", - c: &Channel{ + cr: &Channel{ Spec: ChannelSpec{ Provisioner: &ProvisionerReference{ Ref: &corev1.ObjectReference{ @@ -119,14 +115,7 @@ func TestChannelValidation(t *testing.T) { Also(apis.ErrMissingField("spec.subscriber[1].call", "spec.subscriber[1].result")), }} - 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) { diff --git a/pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go b/pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go index 7d0b78930fa..3be26f37d00 100644 --- a/pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go +++ b/pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go @@ -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 diff --git a/pkg/apis/eventing/v1alpha1/crd_validation_test.go b/pkg/apis/eventing/v1alpha1/crd_validation_test.go index fcc760bbe36..f052c52b9fa 100644 --- a/pkg/apis/eventing/v1alpha1/crd_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/crd_validation_test.go @@ -34,7 +34,7 @@ func doValidateTest(t *testing.T, tests []CRDTest) { t.Run(test.name, func(t *testing.T) { got := test.cr.Validate() if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("validate (-want, +got) = %v", diff) + t.Errorf("%s: validate (-want, +got) = %v", test.name, diff) } }) } diff --git a/pkg/apis/eventing/v1alpha1/source_types.go b/pkg/apis/eventing/v1alpha1/source_types.go index b1ee80cea0b..90552f83001 100644 --- a/pkg/apis/eventing/v1alpha1/source_types.go +++ b/pkg/apis/eventing/v1alpha1/source_types.go @@ -22,6 +22,8 @@ import ( "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 @@ -64,15 +66,47 @@ type SourceSpec struct { // +optional Generation int64 `json:"generation,omitempty"` - Todo string `json:"todo,omitempty"` - // TODO: add real params. + // 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. + // + // This object must fulfill the Subscribable contract. + // + // You can specify only the following fields of the ObjectReference: + // - Kind + // - APIVersion + // - Name + // Currently Kind must be "Channel" and + // APIVersion must be "channels.knative.dev/v1alpha1" + // +optional + Channel *corev1.ObjectReference `json:"target,omitempty"` } -var sourceCondSet = duckv1alpha1.NewLivingConditionSet() +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 cluster provisioner at a point in time. + // 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 @@ -102,6 +136,16 @@ func (ss *SourceStatus) InitializeConditions() { sourceCondSet.Manage(ss).InitializeConditions() } +// MarkProvisioned sets the condition that the source has had it's backing resources created. +func (ss *SourceStatus) MarkProvisioned() { + sourceCondSet.Manage(ss).MarkTrue(SourceConditionProvisioned) +} + +// MarkDeprovisioned sets the condition that the source has had it's 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) { diff --git a/pkg/apis/eventing/v1alpha1/source_types_test.go b/pkg/apis/eventing/v1alpha1/source_types_test.go index f39bd5367e6..1ef8374ca09 100644 --- a/pkg/apis/eventing/v1alpha1/source_types_test.go +++ b/pkg/apis/eventing/v1alpha1/source_types_test.go @@ -45,7 +45,10 @@ func TestSourceStatusIsReady(t *testing.T) { name: "ready true condition", s: &SourceStatus{ Conditions: []duckv1alpha1.Condition{{ - Type: ChannelConditionReady, + Type: SourceConditionReady, + Status: corev1.ConditionTrue, + }, { + Type: SourceConditionProvisioned, Status: corev1.ConditionTrue, }}, }, @@ -54,8 +57,11 @@ func TestSourceStatusIsReady(t *testing.T) { name: "ready false condition", s: &SourceStatus{ Conditions: []duckv1alpha1.Condition{{ - Type: ChannelConditionReady, + Type: SourceConditionReady, Status: corev1.ConditionFalse, + }, { + Type: SourceConditionProvisioned, + Status: corev1.ConditionTrue, }}, }, want: false, @@ -68,6 +74,24 @@ func TestSourceStatusIsReady(t *testing.T) { }}, }, want: false, + }, { + name: "mark provisioned", + s: func() *SourceStatus { + s := &SourceStatus{} + s.InitializeConditions() + s.MarkProvisioned() + return s + }(), + want: true, + }, { + name: "mark deprovisioned", + s: func() *SourceStatus { + s := &SourceStatus{} + s.InitializeConditions() + s.MarkDeprovisioned("Testing", "Just a test") + return s + }(), + want: false, }} for _, test := range tests { diff --git a/pkg/apis/eventing/v1alpha1/source_validation.go b/pkg/apis/eventing/v1alpha1/source_validation.go index a73cb116c46..37811aa6f1d 100644 --- a/pkg/apis/eventing/v1alpha1/source_validation.go +++ b/pkg/apis/eventing/v1alpha1/source_validation.go @@ -29,11 +29,15 @@ func (s *Source) Validate() *apis.FieldError { // Validate validates the Source spec func (ss *SourceSpec) Validate() *apis.FieldError { if equality.Semantic.DeepEqual(ss, &SourceSpec{}) { - return apis.ErrMissingField("todo") + return apis.ErrMissingField("provisioner") } var errs *apis.FieldError - // TODO: add real validation after adding fields. + if ss.Channel != nil { + errs = errs.Also(isValidSubscribable(*ss.Channel).ViaField("channel")) + } + + // TODO: could validate that arguments are json if that is a requirement. return errs } diff --git a/pkg/apis/eventing/v1alpha1/source_validation_test.go b/pkg/apis/eventing/v1alpha1/source_validation_test.go index ef2e35b331a..212a24ad9a3 100644 --- a/pkg/apis/eventing/v1alpha1/source_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/source_validation_test.go @@ -18,16 +18,92 @@ package v1alpha1 import ( "testing" + + "github.com/knative/pkg/apis" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" ) func TestSourceValidate(t *testing.T) { tests := []CRDTest{{ - name: "valid", + name: "empty", + cr: &Source{ + Spec: SourceSpec{}, + }, + want: apis.ErrMissingField("spec.provisioner"), + }, { + name: "minimum valid", cr: &Source{ Spec: SourceSpec{ - Todo: "todo", + Provisioner: &ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Name: "foo", + }, + }, }, }, + }, { + name: "full valid", + cr: &Source{ + Spec: SourceSpec{ + Provisioner: &ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Name: "foo", + }, + }, + Arguments: &runtime.RawExtension{ + Raw: []byte(`{"foo":"baz"}`), + }, + Channel: &corev1.ObjectReference{ + Name: "bar", + Kind: "Channel", + APIVersion: "eventing.knative.dev/v1alpha1", + }, + }, + }, + }, { + name: "invalid, set extra channel parameters", + cr: &Source{ + Spec: SourceSpec{ + Provisioner: &ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Name: "foo", + }, + }, + Channel: &corev1.ObjectReference{ + Name: "bar", + Kind: "Channel", + APIVersion: "eventing.knative.dev/v1alpha1", + ResourceVersion: "SoMad", + }, + }, + }, + want: &apis.FieldError{ + Message: "must not set the field(s)", + Paths: []string{"spec.channel.ResourceVersion"}, + Details: "only name, apiVersion and kind are supported fields", + }, + }, { + name: "invalid, set extra channel as not a channel", + cr: &Source{ + Spec: SourceSpec{ + Provisioner: &ProvisionerReference{ + Ref: &corev1.ObjectReference{ + Name: "foo", + }, + }, + Channel: &corev1.ObjectReference{ + Name: "backwards", + Kind: "lennahC", + APIVersion: "eventing.knative.dev/v1alpha1", + }, + }, + }, + want: &apis.FieldError{ + Message: "invalid value \"lennahC\"", + Paths: []string{"spec.channel.kind"}, + Details: "only 'Channel' kind is allowed", + }, }} doValidateTest(t, tests) diff --git a/pkg/apis/eventing/v1alpha1/subscribable_validation.go b/pkg/apis/eventing/v1alpha1/subscribable_validation.go new file mode 100644 index 00000000000..07aa7463798 --- /dev/null +++ b/pkg/apis/eventing/v1alpha1/subscribable_validation.go @@ -0,0 +1,101 @@ +/* +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 ( + "reflect" + + "github.com/google/go-cmp/cmp" + "github.com/knative/pkg/apis" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" +) + +func isSubscribableEmpty(f corev1.ObjectReference) bool { + return equality.Semantic.DeepEqual(f, corev1.ObjectReference{}) +} + +// Valid from only contains the following fields: +// - Kind == 'Channel' +// - APIVersion == 'channels.knative.dev/v1alpha1' +// - Name == not empty +func isValidSubscribable(f corev1.ObjectReference) *apis.FieldError { + errs := isValidObjectReference(f) + + if f.Kind != "Channel" { + fe := apis.ErrInvalidValue(f.Kind, "kind") + fe.Paths = []string{"kind"} + fe.Details = "only 'Channel' kind is allowed" + errs = errs.Also(fe) + } + if f.APIVersion != "eventing.knative.dev/v1alpha1" { + fe := apis.ErrInvalidValue(f.APIVersion, "apiVersion") + fe.Details = "only eventing.knative.dev/v1alpha1 is allowed for apiVersion" + errs = errs.Also(fe) + } + return errs +} + +func isValidObjectReference(f corev1.ObjectReference) *apis.FieldError { + return checkRequiredObjectReferenceFields(f). + Also(checkDisallowedObjectReferenceFields(f)) +} + +// Check the corev1.ObjectReference to make sure it has the required fields. They +// are not checked for anything more except that they are set. +func checkRequiredObjectReferenceFields(f corev1.ObjectReference) *apis.FieldError { + var errs *apis.FieldError + if f.Name == "" { + errs = errs.Also(apis.ErrMissingField("name")) + } + if f.APIVersion == "" { + errs = errs.Also(apis.ErrMissingField("apiVersion")) + } + if f.Kind == "" { + errs = errs.Also(apis.ErrMissingField("kind")) + } + return errs +} + +// Check the corev1.ObjectReference to make sure it only has the following fields set: +// Name, Kind, APIVersion +// If any other fields are set and is not the Zero value, returns an apis.FieldError +// with the fieldpaths for all those fields. +func checkDisallowedObjectReferenceFields(f corev1.ObjectReference) *apis.FieldError { + disallowedFields := []string{} + // See if there are any fields that have been set that should not be. + // TODO: Hoist this kind of stuff into pkg repository. + s := reflect.ValueOf(f) + typeOf := s.Type() + for i := 0; i < s.NumField(); i++ { + field := s.Field(i) + fieldName := typeOf.Field(i).Name + if fieldName == "Name" || fieldName == "Kind" || fieldName == "APIVersion" { + continue + } + if !cmp.Equal(field.Interface(), reflect.Zero(field.Type()).Interface()) { + disallowedFields = append(disallowedFields, fieldName) + } + } + if len(disallowedFields) > 0 { + fe := apis.ErrDisallowedFields(disallowedFields...) + fe.Details = "only name, apiVersion and kind are supported fields" + return fe + } + return nil + +} diff --git a/pkg/apis/eventing/v1alpha1/subscription_validation.go b/pkg/apis/eventing/v1alpha1/subscription_validation.go index cebdf24cdfd..6adde5c3736 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_validation.go +++ b/pkg/apis/eventing/v1alpha1/subscription_validation.go @@ -17,9 +17,6 @@ limitations under the License. package v1alpha1 import ( - "reflect" - // "strings" - "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/knative/pkg/apis" @@ -97,20 +94,7 @@ func isFromEmpty(f corev1.ObjectReference) bool { // - APIVersion == 'channels.knative.dev/v1alpha1' // - Name == not empty func isValidFrom(f corev1.ObjectReference) *apis.FieldError { - errs := isValidObjectReference(f) - - if f.Kind != "Channel" { - fe := apis.ErrInvalidValue(f.Kind, "kind") - fe.Paths = []string{"kind"} - fe.Details = "only 'Channel' kind is allowed" - errs = errs.Also(fe) - } - if f.APIVersion != "channels.knative.dev/v1alpha1" { - fe := apis.ErrInvalidValue(f.APIVersion, "apiVersion") - fe.Details = "only channels.knative.dev/v1alpha1 is allowed for apiVersion" - errs = errs.Also(fe) - } - return errs + return isValidSubscribable(f) } func isResultStrategyNilOrEmpty(r *ResultStrategy) bool { @@ -121,56 +105,6 @@ func isValidResultStrategy(r *ResultStrategy) *apis.FieldError { return isValidObjectReference(*r.Target).ViaField("target") } -func isValidObjectReference(f corev1.ObjectReference) *apis.FieldError { - return checkRequiredFields(f). - Also(checkDisallowedFields(f)) -} - -// Check the corev1.ObjectReference to make sure it has the required fields. They -// are not checked for anything more except that they are set. -func checkRequiredFields(f corev1.ObjectReference) *apis.FieldError { - var errs *apis.FieldError - if f.Name == "" { - errs = errs.Also(apis.ErrMissingField("name")) - } - if f.APIVersion == "" { - errs = errs.Also(apis.ErrMissingField("apiVersion")) - } - if f.Kind == "" { - errs = errs.Also(apis.ErrMissingField("kind")) - } - return errs -} - -// Check the corev1.ObjectReference to make sure it only has the following fields set: -// Name, Kind, APIVersion -// If any other fields are set and is not the Zero value, returns an apis.FieldError -// with the fieldpaths for all those fields. -func checkDisallowedFields(f corev1.ObjectReference) *apis.FieldError { - disallowedFields := []string{} - // See if there are any fields that have been set that should not be. - // TODO: Hoist this kind of stuff into pkg repository. - s := reflect.ValueOf(f) - typeOf := s.Type() - for i := 0; i < s.NumField(); i++ { - field := s.Field(i) - fieldName := typeOf.Field(i).Name - if fieldName == "Name" || fieldName == "Kind" || fieldName == "APIVersion" { - continue - } - if !cmp.Equal(field.Interface(), reflect.Zero(field.Type()).Interface()) { - disallowedFields = append(disallowedFields, fieldName) - } - } - if len(disallowedFields) > 0 { - fe := apis.ErrDisallowedFields(disallowedFields...) - fe.Details = "only name, apiVersion and kind are supported fields" - return fe - } - return nil - -} - func (current *Subscription) CheckImmutableFields(og apis.Immutable) *apis.FieldError { original, ok := og.(*Subscription) if !ok { diff --git a/pkg/apis/eventing/v1alpha1/subscription_validation_test.go b/pkg/apis/eventing/v1alpha1/subscription_validation_test.go index a5e749212e2..74997f452d2 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/subscription_validation_test.go @@ -25,7 +25,7 @@ import ( const ( channelKind = "Channel" - channelAPIVersion = "channels.knative.dev/v1alpha1" + channelAPIVersion = "eventing.knative.dev/v1alpha1" routeKind = "Route" routeAPIVersion = "serving.knative.dev/v1alpha1" fromChannelName = "fromChannel" @@ -420,7 +420,7 @@ func TestValidFrom(t *testing.T) { }, want: func() *apis.FieldError { fe := apis.ErrInvalidValue("", "apiVersion") - fe.Details = "only channels.knative.dev/v1alpha1 is allowed for apiVersion" + fe.Details = "only eventing.knative.dev/v1alpha1 is allowed for apiVersion" return apis.ErrMissingField("apiVersion").Also(fe) }(), }, { @@ -455,7 +455,7 @@ func TestValidFrom(t *testing.T) { }, want: func() *apis.FieldError { fe := apis.ErrInvalidValue("wrongapiversion", "apiVersion") - fe.Details = "only channels.knative.dev/v1alpha1 is allowed for apiVersion" + fe.Details = "only eventing.knative.dev/v1alpha1 is allowed for apiVersion" return fe }(), }, { diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index 6c44dcc592f..8800588a2c5 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -375,7 +375,7 @@ func (in *Source) DeepCopyInto(out *Source) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) return } @@ -434,6 +434,33 @@ func (in *SourceList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SourceSpec) DeepCopyInto(out *SourceSpec) { *out = *in + if in.Provisioner != nil { + in, out := &in.Provisioner, &out.Provisioner + if *in == nil { + *out = nil + } else { + *out = new(ProvisionerReference) + (*in).DeepCopyInto(*out) + } + } + if in.Arguments != nil { + in, out := &in.Arguments, &out.Arguments + if *in == nil { + *out = nil + } else { + *out = new(runtime.RawExtension) + (*in).DeepCopyInto(*out) + } + } + if in.Channel != nil { + in, out := &in.Channel, &out.Channel + if *in == nil { + *out = nil + } else { + *out = new(v1.ObjectReference) + **out = **in + } + } return } From 98a3129dd7ee56d7a7738053dc3fcaddfa42e11a Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Fri, 21 Sep 2018 15:27:51 -0700 Subject: [PATCH 04/11] update the comment for source resource. --- pkg/apis/eventing/v1alpha1/source_types.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/apis/eventing/v1alpha1/source_types.go b/pkg/apis/eventing/v1alpha1/source_types.go index 90552f83001..f5414521592 100644 --- a/pkg/apis/eventing/v1alpha1/source_types.go +++ b/pkg/apis/eventing/v1alpha1/source_types.go @@ -30,7 +30,9 @@ import ( // +genclient:noStatus // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// Source +// 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 From bccf1c8169e2737bf141da151ab11aa7475aecaa Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Fri, 21 Sep 2018 15:30:24 -0700 Subject: [PATCH 05/11] use isSubscribableEmpty. --- pkg/apis/eventing/v1alpha1/source_validation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/apis/eventing/v1alpha1/source_validation.go b/pkg/apis/eventing/v1alpha1/source_validation.go index 37811aa6f1d..f6223b6c36e 100644 --- a/pkg/apis/eventing/v1alpha1/source_validation.go +++ b/pkg/apis/eventing/v1alpha1/source_validation.go @@ -33,7 +33,7 @@ func (ss *SourceSpec) Validate() *apis.FieldError { } var errs *apis.FieldError - if ss.Channel != nil { + if ss.Channel != nil && !isSubscribableEmpty(*ss.Channel) { errs = errs.Also(isValidSubscribable(*ss.Channel).ViaField("channel")) } From 1fcb560d65a5122ccfa4a41074a2d86c09ac5ad5 Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Mon, 24 Sep 2018 10:11:19 -0700 Subject: [PATCH 06/11] Update comments to use the correct group. --- pkg/apis/eventing/v1alpha1/source_types.go | 2 +- pkg/apis/eventing/v1alpha1/subscribable_validation.go | 2 +- pkg/apis/eventing/v1alpha1/subscription_validation.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/source_types.go b/pkg/apis/eventing/v1alpha1/source_types.go index f5414521592..188045c75d6 100644 --- a/pkg/apis/eventing/v1alpha1/source_types.go +++ b/pkg/apis/eventing/v1alpha1/source_types.go @@ -87,7 +87,7 @@ type SourceSpec struct { // - APIVersion // - Name // Currently Kind must be "Channel" and - // APIVersion must be "channels.knative.dev/v1alpha1" + // APIVersion must be "eventing.knative.dev/v1alpha1" // +optional Channel *corev1.ObjectReference `json:"target,omitempty"` } diff --git a/pkg/apis/eventing/v1alpha1/subscribable_validation.go b/pkg/apis/eventing/v1alpha1/subscribable_validation.go index 07aa7463798..ce33aa74e5c 100644 --- a/pkg/apis/eventing/v1alpha1/subscribable_validation.go +++ b/pkg/apis/eventing/v1alpha1/subscribable_validation.go @@ -31,7 +31,7 @@ func isSubscribableEmpty(f corev1.ObjectReference) bool { // Valid from only contains the following fields: // - Kind == 'Channel' -// - APIVersion == 'channels.knative.dev/v1alpha1' +// - APIVersion == 'eventing.knative.dev/v1alpha1' // - Name == not empty func isValidSubscribable(f corev1.ObjectReference) *apis.FieldError { errs := isValidObjectReference(f) diff --git a/pkg/apis/eventing/v1alpha1/subscription_validation.go b/pkg/apis/eventing/v1alpha1/subscription_validation.go index 6adde5c3736..c59093bfdc7 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_validation.go +++ b/pkg/apis/eventing/v1alpha1/subscription_validation.go @@ -91,7 +91,7 @@ func isFromEmpty(f corev1.ObjectReference) bool { // Valid from only contains the following fields: // - Kind == 'Channel' -// - APIVersion == 'channels.knative.dev/v1alpha1' +// - APIVersion == 'eventing.knative.dev/v1alpha1' // - Name == not empty func isValidFrom(f corev1.ObjectReference) *apis.FieldError { return isValidSubscribable(f) From 2f6dd40593277fb2e1347952e5618faaee9f8691 Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Tue, 25 Sep 2018 10:29:28 -0700 Subject: [PATCH 07/11] one day I will spell its correctly. --- pkg/apis/eventing/v1alpha1/source_types.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/source_types.go b/pkg/apis/eventing/v1alpha1/source_types.go index 188045c75d6..bd13243a8b8 100644 --- a/pkg/apis/eventing/v1alpha1/source_types.go +++ b/pkg/apis/eventing/v1alpha1/source_types.go @@ -38,7 +38,7 @@ type Source struct { // +optional metav1.ObjectMeta `json:"metadata,omitempty"` - // Spec defines the Types provisioned by this Source. + // Spec defines the the Provisioner and arguments provided for this Source. Spec SourceSpec `json:"spec"` // Status is the current status of the Source. @@ -138,12 +138,12 @@ func (ss *SourceStatus) InitializeConditions() { sourceCondSet.Manage(ss).InitializeConditions() } -// MarkProvisioned sets the condition that the source has had it's backing resources created. +// 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 it's backing resources removed. +// 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) } From 6b94532354213b57c4e0111cfdd5cd9a0ea9f8dd Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Tue, 25 Sep 2018 10:42:05 -0700 Subject: [PATCH 08/11] ducks in a row. --- pkg/apis/eventing/v1alpha1/source_types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/source_types.go b/pkg/apis/eventing/v1alpha1/source_types.go index bd13243a8b8..fa0d9f0b7ed 100644 --- a/pkg/apis/eventing/v1alpha1/source_types.go +++ b/pkg/apis/eventing/v1alpha1/source_types.go @@ -56,8 +56,8 @@ var _ duckv1alpha1.ConditionsAccessor = (*SourceStatus)(nil) var _ = duck.VerifyType(&Source{}, &duckv1alpha1.Conditions{}) // Check that Source implements the Generation duck type. -var emptyGen duckv1alpha1.Generation -var _ = duck.VerifyType(&Source{}, &emptyGen) +var emptyGenSource duckv1alpha1.Generation +var _ = duck.VerifyType(&Source{}, &emptyGenSource) // SourceSpec is the spec for a Source resource. type SourceSpec struct { From 9290da44edc1417b9e06812776f2d66ef9b6c5fc Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Tue, 25 Sep 2018 11:36:06 -0700 Subject: [PATCH 09/11] Clear up Channelable and Subscribable elements inside of source. --- pkg/apis/eventing/v1alpha1/source_types.go | 8 +++++- .../eventing/v1alpha1/source_validation.go | 4 +-- ...=> subscribable_channelable_validation.go} | 27 ++++++++++++++++++- .../v1alpha1/subscription_validation_test.go | 4 +-- 4 files changed, 37 insertions(+), 6 deletions(-) rename pkg/apis/eventing/v1alpha1/{subscribable_validation.go => subscribable_channelable_validation.go} (79%) diff --git a/pkg/apis/eventing/v1alpha1/source_types.go b/pkg/apis/eventing/v1alpha1/source_types.go index fa0d9f0b7ed..ebda463fc18 100644 --- a/pkg/apis/eventing/v1alpha1/source_types.go +++ b/pkg/apis/eventing/v1alpha1/source_types.go @@ -59,6 +59,9 @@ var _ = duck.VerifyType(&Source{}, &duckv1alpha1.Conditions{}) 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 @@ -80,7 +83,7 @@ type SourceSpec struct { // Specify an existing channel to use to emit events. If empty, create a new // Channel using the cluster/namespace default. // - // This object must fulfill the Subscribable contract. + // This object must fulfill the Channelable contract. // // You can specify only the following fields of the ObjectReference: // - Kind @@ -115,6 +118,9 @@ type SourceStatus struct { // was last reconciled by the controller. // +optional ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Source might be Subscribable. This points to the Channelable object. + Subscribable duckv1alpha1.Subscribable `json:"subscribable,omitempty"` } // GetCondition returns the condition currently associated with the given type, or nil. diff --git a/pkg/apis/eventing/v1alpha1/source_validation.go b/pkg/apis/eventing/v1alpha1/source_validation.go index f6223b6c36e..5876a2a108c 100644 --- a/pkg/apis/eventing/v1alpha1/source_validation.go +++ b/pkg/apis/eventing/v1alpha1/source_validation.go @@ -33,8 +33,8 @@ func (ss *SourceSpec) Validate() *apis.FieldError { } var errs *apis.FieldError - if ss.Channel != nil && !isSubscribableEmpty(*ss.Channel) { - errs = errs.Also(isValidSubscribable(*ss.Channel).ViaField("channel")) + if ss.Channel != nil && !isChannelableEmpty(*ss.Channel) { + errs = errs.Also(isValidChannelable(*ss.Channel).ViaField("channel")) } // TODO: could validate that arguments are json if that is a requirement. diff --git a/pkg/apis/eventing/v1alpha1/subscribable_validation.go b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go similarity index 79% rename from pkg/apis/eventing/v1alpha1/subscribable_validation.go rename to pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go index ce33aa74e5c..fee643e1a84 100644 --- a/pkg/apis/eventing/v1alpha1/subscribable_validation.go +++ b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go @@ -30,12 +30,37 @@ func isSubscribableEmpty(f corev1.ObjectReference) bool { } // Valid from only contains the following fields: -// - Kind == 'Channel' +// - Kind == 'Channel' || 'Source' // - APIVersion == 'eventing.knative.dev/v1alpha1' // - Name == not empty func isValidSubscribable(f corev1.ObjectReference) *apis.FieldError { errs := isValidObjectReference(f) + if f.Kind != "Channel" && f.Kind != "Source" { + fe := apis.ErrInvalidValue(f.Kind, "kind") + fe.Paths = []string{"kind"} + fe.Details = "only 'Channel' or 'Source' kind is allowed" + errs = errs.Also(fe) + } + if f.APIVersion != "eventing.knative.dev/v1alpha1" { + fe := apis.ErrInvalidValue(f.APIVersion, "apiVersion") + fe.Details = "only eventing.knative.dev/v1alpha1 is allowed for apiVersion" + errs = errs.Also(fe) + } + return errs +} + +func isChannelableEmpty(f corev1.ObjectReference) bool { + return equality.Semantic.DeepEqual(f, corev1.ObjectReference{}) +} + +// Valid from only contains the following fields: +// - Kind == 'Channel' +// - APIVersion == 'eventing.knative.dev/v1alpha1' +// - Name == not empty +func isValidChannelable(f corev1.ObjectReference) *apis.FieldError { + errs := isValidObjectReference(f) + if f.Kind != "Channel" { fe := apis.ErrInvalidValue(f.Kind, "kind") fe.Paths = []string{"kind"} diff --git a/pkg/apis/eventing/v1alpha1/subscription_validation_test.go b/pkg/apis/eventing/v1alpha1/subscription_validation_test.go index 7d9b9dc1711..7d82bd3ed35 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/subscription_validation_test.go @@ -431,7 +431,7 @@ func TestValidFrom(t *testing.T) { }, want: func() *apis.FieldError { fe := apis.ErrInvalidValue("", "kind") - fe.Details = "only 'Channel' kind is allowed" + fe.Details = "only 'Channel' or 'Source' kind is allowed" return apis.ErrMissingField("kind").Also(fe) }(), }, { @@ -443,7 +443,7 @@ func TestValidFrom(t *testing.T) { }, want: func() *apis.FieldError { fe := apis.ErrInvalidValue("subscription", "kind") - fe.Details = "only 'Channel' kind is allowed" + fe.Details = "only 'Channel' or 'Source' kind is allowed" return fe }(), }, { From 0988394daacf15ddc8a252bfcc55f9889af40939 Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Tue, 25 Sep 2018 11:44:23 -0700 Subject: [PATCH 10/11] update codegen. --- pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index 55f05b73d00..dcbd9410622 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -454,6 +454,7 @@ func (in *SourceStatus) DeepCopyInto(out *SourceStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + out.Subscribable = in.Subscribable return } From 7266954f17719700a877c14ce8660706bc284a47 Mon Sep 17 00:00:00 2001 From: Scott Nichols Date: Tue, 25 Sep 2018 11:49:53 -0700 Subject: [PATCH 11/11] use sub helper. --- pkg/apis/eventing/v1alpha1/subscription_validation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/apis/eventing/v1alpha1/subscription_validation.go b/pkg/apis/eventing/v1alpha1/subscription_validation.go index ffb8202b9d2..c84d223be5b 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_validation.go +++ b/pkg/apis/eventing/v1alpha1/subscription_validation.go @@ -86,7 +86,7 @@ func isValidCallable(c Callable) *apis.FieldError { } func isFromEmpty(f corev1.ObjectReference) bool { - return equality.Semantic.DeepEqual(f, corev1.ObjectReference{}) + return isSubscribableEmpty(f) } // Valid from only contains the following fields: