From 68b5a79bcee544c412076ded759e05c12e631fd1 Mon Sep 17 00:00:00 2001
From: Matthias Wessendorf
+
Package v1alpha1 contains API Schema definitions for the sources v1alpha1 API group.
+ +Resource Types: + ++
IntegrationSource is the Schema for the Integrationsources API
+ +Field | +Description | +||||||
---|---|---|---|---|---|---|---|
+apiVersion +string |
+
+
+sources.knative.dev/v1alpha1
+
+ |
+||||||
+kind +string + |
+IntegrationSource |
+||||||
+metadata + + +Kubernetes meta/v1.ObjectMeta + + + |
+
+Refer to the Kubernetes API documentation for the fields of the
+metadata field.
+ |
+||||||
+spec + + +IntegrationSourceSpec + + + |
+
+ + +
|
+||||||
+status + + +IntegrationSourceStatus + + + |
++ | +
+(Appears on:AWSDDBStreams, AWSS3, AWSSQS) +
++
+Field | +Description | +
---|---|
+region + +string + + |
+
+ Auth is the S3 authentication (accessKey/secretKey) configuration. + |
+
+profileCredentialsName + +string + + |
+
+ AWS region + |
+
+sessionToken + +string + + |
+
+ Profile name for profile credentials provider + |
+
+uriEndpointOverride + +string + + |
+
+ Session token + |
+
+overrideEndpoint + +bool + + |
+
+ Override endpoint URI + |
+
+(Appears on:Aws) +
++
+Field | +Description | +
---|---|
+AWSCommon + + +AWSCommon + + + |
+
+
+(Members of |
+
+table + +string + + |
+
+ Embeds AWSCommon to inherit its fields in JSON + |
+
+streamIteratorType + +string + + |
+
+ The name of the DynamoDB table + |
+
+delay + +int + + |
+
+ Defines where in the DynamoDB stream to start getting records + |
+
+(Appears on:Aws) +
++
+Field | +Description | +
---|---|
+AWSCommon + + +AWSCommon + + + |
+
+
+(Members of |
+
+bucketNameOrArn + +string + + |
+
+ Embeds AWSCommon to inherit its fields in JSON + |
+
+deleteAfterRead + +bool + + |
+
+ S3 Bucket name or ARN + |
+
+moveAfterRead + +bool + + |
+
+ Auto-delete objects after reading + |
+
+destinationBucket + +string + + |
+
+ Move objects after reading + |
+
+destinationBucketPrefix + +string + + |
+
+ Destination bucket for moved objects + |
+
+destinationBucketSuffix + +string + + |
+
+ Prefix for moved objects + |
+
+autoCreateBucket + +bool + + |
+
+ Suffix for moved objects + |
+
+prefix + +string + + |
+
+ Auto-create S3 bucket + |
+
+ignoreBody + +bool + + |
+
+ S3 bucket prefix for search + |
+
+forcePathStyle + +bool + + |
+
+ Ignore object body + |
+
+delay + +int + + |
+
+ Force path style for bucket access + |
+
+maxMessagesPerPoll + +int + + |
+
+ Delay between polls in milliseconds + |
+
+(Appears on:Aws) +
++
+Field | +Description | +
---|---|
+AWSCommon + + +AWSCommon + + + |
+
+
+(Members of |
+
+queueNameOrArn + +string + + |
+
+ Embeds AWSCommon to inherit its fields in JSON + |
+
+deleteAfterRead + +bool + + |
+
+ SQS Queue name or ARN + |
+
+autoCreateQueue + +bool + + |
+
+ Auto-delete messages after reading + |
+
+amazonAWSHost + +string + + |
+
+ Auto-create SQS queue + |
+
+protocol + +string + + |
+
+ AWS host + |
+
+queueURL + +string + + |
+
+ Communication protocol (http/https) + |
+
+greedy + +bool + + |
+
+ Full SQS queue URL + |
+
+delay + +int + + |
+
+ Greedy scheduler + |
+
+maxMessagesPerPoll + +int + + |
+
+ Delay between polls in milliseconds + |
+
+waitTimeSeconds + +int + + |
+
+ Max messages to return (1-10) + |
+
+visibilityTimeout + +int + + |
+
+ Wait time for messages + |
+
+(Appears on:Aws) +
++
+Field | +Description | +
---|---|
+secret + + +Secret + + + |
+
+ Auth Secret + |
+
+accessKey + +string + + |
+
+ AccessKey is the AWS access key ID. + |
+
+secretKey + +string + + |
+
+ SecretKey is the AWS secret access key. + |
+
+(Appears on:IntegrationSourceSpec) +
++
+Field | +Description | +
---|---|
+s3 + + +AWSS3 + + + |
++ | +
+sqs + + +AWSSQS + + + |
+
+ S3 source configuration + |
+
+ddb-streams + + +AWSDDBStreams + + + |
+
+ SQS source configuration + |
+
+auth + + +Auth + + + |
+
+ DynamoDB Streams source configuration + |
+
+(Appears on:IntegrationSource) +
++
IntegrationSourceSpec defines the desired state of IntegrationSource
+ +Field | +Description | +
---|---|
+SourceSpec + + +knative.dev/pkg/apis/duck/v1.SourceSpec + + + |
+
+
+(Members of inherits duck/v1 SourceSpec, which currently provides: +* Sink - a reference to an object that will resolve to a domain name or +a URI directly to use as the sink. +* CloudEventOverrides - defines overrides to control the output format +and modifications of the event sent to the sink. + |
+
+aws + + +Aws + + + |
++ | +
+timer + + +Timer + + + |
+
+ AWS source configuration + |
+
+(Appears on:IntegrationSource) +
++
IntegrationSourceStatus defines the observed state of IntegrationSource
+ +Field | +Description | +
---|---|
+SourceStatus + + +knative.dev/pkg/apis/duck/v1.SourceStatus + + + |
+
+
+(Members of inherits duck/v1 SourceStatus, which currently provides: +* ObservedGeneration - the ‘Generation’ of the Service that was last +processed by the controller. +* Conditions - the latest available observations of a resource’s current +state. +* SinkURI - the current active sink URI that has been configured for the +Source. + |
+
+(Appears on:Auth) +
++
+Field | +Description | +
---|---|
+ref + + +SecretReference + + + |
+
+ Secret reference for SASL and SSL configurations. + |
+
+(Appears on:Secret) +
++
+Field | +Description | +
---|---|
+name + +string + + |
+
+ Secret name. + |
+
+(Appears on:IntegrationSourceSpec) +
++
+Field | +Description | +
---|---|
+period + +int + + |
++ | +
+message + +string + + |
+
+ Interval (in milliseconds) between producing messages + |
+
+contentType + +string + + |
+
+ Message to generate + |
+
+repeatCount + +int + + |
+
+ Content type of generated message + |
+
Package v1beta2 contains API Schema definitions for the sources v1beta2 API group.
diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index a0fe67dcbff..dc5206ae11d 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -49,7 +49,7 @@ group "Knative Codegen" # Knative Injection ${KNATIVE_CODEGEN_PKG}/hack/generate-knative.sh "injection" \ knative.dev/eventing/pkg/client knative.dev/eventing/pkg/apis \ - "sinks:v1alpha1 eventing:v1alpha1 eventing:v1beta1 eventing:v1beta2 eventing:v1beta3 eventing:v1 messaging:v1 flows:v1 sources:v1beta2 sources:v1 duck:v1beta1 duck:v1" \ + "sinks:v1alpha1 eventing:v1alpha1 eventing:v1beta1 eventing:v1beta2 eventing:v1beta3 eventing:v1 messaging:v1 flows:v1 sources:v1alpha1 sources:v1beta2 sources:v1 duck:v1beta1 duck:v1" \ --go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt group "Generating API reference docs" diff --git a/pkg/apis/sources/register.go b/pkg/apis/sources/register.go index 04716c8ca65..3571d0a17b4 100644 --- a/pkg/apis/sources/register.go +++ b/pkg/apis/sources/register.go @@ -56,4 +56,10 @@ var ( Group: GroupName, Resource: "containersources", } + + // IntegrationSourceResource respresents a Knative Eventing Sources IntegrationSource + IntegrationSourceResource = schema.GroupResource{ + Group: GroupName, + Resource: "integrationsources", + } ) diff --git a/pkg/apis/sources/v1alpha1/doc.go b/pkg/apis/sources/v1alpha1/doc.go new file mode 100644 index 00000000000..76cd299b7b5 --- /dev/null +++ b/pkg/apis/sources/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 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 contains API Schema definitions for the sources v1alpha1 API group. +// +k8s:deepcopy-gen=package +// +groupName=sources.knative.dev +package v1alpha1 diff --git a/pkg/apis/sources/v1alpha1/integration_conversion.go b/pkg/apis/sources/v1alpha1/integration_conversion.go new file mode 100644 index 00000000000..32bfc7cc4b9 --- /dev/null +++ b/pkg/apis/sources/v1alpha1/integration_conversion.go @@ -0,0 +1,36 @@ +/* +Copyright 2020 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 ( + "context" + "fmt" + + "knative.dev/pkg/apis" +) + +// ConvertTo implements apis.Convertible +// Converts source from v1alpha1.IntegrationSource into a higher version. +func (source *IntegrationSource) ConvertTo(ctx context.Context, obj apis.Convertible) error { + return fmt.Errorf("v1alpha1 is the highest known version, got: %T", source) +} + +// ConvertFrom implements apis.Convertible +// Converts source from a higher version into v1beta2.IntegrationSource +func (source *IntegrationSource) ConvertFrom(ctx context.Context, obj apis.Convertible) error { + return fmt.Errorf("v1alpha1 is the highest known version, got: %T", source) +} diff --git a/pkg/apis/sources/v1alpha1/integration_conversion_test.go b/pkg/apis/sources/v1alpha1/integration_conversion_test.go new file mode 100644 index 00000000000..2272ecce52e --- /dev/null +++ b/pkg/apis/sources/v1alpha1/integration_conversion_test.go @@ -0,0 +1,48 @@ +/* +Copyright 2020 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 ( + "context" + "errors" + "testing" + + "knative.dev/pkg/apis" +) + +// implement apis.Convertible +type testObject struct{} + +func (*testObject) ConvertTo(ctx context.Context, obj apis.Convertible) error { + return errors.New("Won't go") +} + +func (*testObject) ConvertFrom(ctx context.Context, obj apis.Convertible) error { + return errors.New("Won't go") +} + +func TestConversionConversionBadType(t *testing.T) { + good, bad := &IntegrationSource{}, &testObject{} + + if err := good.ConvertTo(context.Background(), bad); err == nil { + t.Errorf("ConvertTo() = %#v, wanted error", bad) + } + + if err := good.ConvertFrom(context.Background(), bad); err == nil { + t.Errorf("ConvertFrom() = %#v, wanted error", good) + } +} diff --git a/pkg/apis/sources/v1alpha1/integration_defaults.go b/pkg/apis/sources/v1alpha1/integration_defaults.go new file mode 100644 index 00000000000..70d82c73e5c --- /dev/null +++ b/pkg/apis/sources/v1alpha1/integration_defaults.go @@ -0,0 +1,26 @@ +/* +Copyright 2020 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 "context" + +func (source *IntegrationSource) SetDefaults(ctx context.Context) { + source.Spec.SetDefaults(ctx) +} + +func (source *IntegrationSourceSpec) SetDefaults(ctx context.Context) { +} diff --git a/pkg/apis/sources/v1alpha1/integration_defaults_test.go b/pkg/apis/sources/v1alpha1/integration_defaults_test.go new file mode 100644 index 00000000000..ed586492d54 --- /dev/null +++ b/pkg/apis/sources/v1alpha1/integration_defaults_test.go @@ -0,0 +1,39 @@ +/* +Copyright 2020 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 ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestSetDefaults(t *testing.T) { + testCases := map[string]struct { + initial IntegrationSource + expected IntegrationSource + }{} + for n, tc := range testCases { + t.Run(n, func(t *testing.T) { + tc.initial.SetDefaults(context.TODO()) + if diff := cmp.Diff(tc.expected, tc.initial); diff != "" { + t.Fatal("Unexpected defaults (-want, +got):", diff) + } + }) + } +} diff --git a/pkg/apis/sources/v1alpha1/integration_lifecycle.go b/pkg/apis/sources/v1alpha1/integration_lifecycle.go new file mode 100644 index 00000000000..4f25e70e520 --- /dev/null +++ b/pkg/apis/sources/v1alpha1/integration_lifecycle.go @@ -0,0 +1,76 @@ +/* +Copyright 2020 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 ( + corev1 "k8s.io/api/core/v1" + v1 "knative.dev/eventing/pkg/apis/sources/v1" + "knative.dev/pkg/apis" +) + +const ( + // IntegrationSourceConditionReady has status True when the IntegrationSource is ready to send events. + IntegrationSourceConditionReady = apis.ConditionReady + + // IntegrationSourceConditionReceiveAdapterReady has status True when the IntegrationSource's Deployment is ready. + IntegrationSourceConditionContainerSourceReady apis.ConditionType = "ContainerSourceReady" +) + +var IntegrationCondSet = apis.NewLivingConditionSet( + IntegrationSourceConditionContainerSourceReady, +) + +// GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface. +func (*IntegrationSource) GetConditionSet() apis.ConditionSet { + return IntegrationCondSet +} + +// GetTopLevelCondition returns the top level condition. +func (s *IntegrationSourceStatus) GetTopLevelCondition() *apis.Condition { + return IntegrationCondSet.Manage(s).GetTopLevelCondition() +} + +// InitializeConditions sets relevant unset conditions to Unknown state. +func (s *IntegrationSourceStatus) InitializeConditions() { + IntegrationCondSet.Manage(s).InitializeConditions() +} + +func (iss *IntegrationSourceStatus) IsReady() bool { + return IntegrationCondSet.Manage(iss).IsHappy() +} + +func (s *IntegrationSourceStatus) PropagateContainerSourceStatus(status *v1.ContainerSourceStatus) { + //// Do not copy conditions nor observedGeneration + s.SourceStatus = *status.SourceStatus.DeepCopy() + + cond := status.GetCondition(apis.ConditionReady) + switch { + case cond == nil: + IntegrationCondSet.Manage(s).MarkUnknown(IntegrationSourceConditionContainerSourceReady, "", "") + case cond.Status == corev1.ConditionTrue: + IntegrationCondSet.Manage(s).MarkTrue(IntegrationSourceConditionContainerSourceReady) + case cond.Status == corev1.ConditionFalse: + IntegrationCondSet.Manage(s).MarkFalse(IntegrationSourceConditionContainerSourceReady, cond.Reason, cond.Message) + case cond.Status == corev1.ConditionUnknown: + IntegrationCondSet.Manage(s).MarkUnknown(IntegrationSourceConditionContainerSourceReady, cond.Reason, cond.Message) + default: + IntegrationCondSet.Manage(s).MarkUnknown(IntegrationSourceConditionContainerSourceReady, cond.Reason, cond.Message) + } + + // Propagate ContainerSources AuthStatus to IntegrationSources AuthStatus + s.Auth = status.Auth +} diff --git a/pkg/apis/sources/v1alpha1/integration_lifecycle_test.go b/pkg/apis/sources/v1alpha1/integration_lifecycle_test.go new file mode 100644 index 00000000000..26b2a0046fa --- /dev/null +++ b/pkg/apis/sources/v1alpha1/integration_lifecycle_test.go @@ -0,0 +1,189 @@ +/* +Copyright 2020 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" + "github.com/google/go-cmp/cmp/cmpopts" + corev1 "k8s.io/api/core/v1" + v1 "knative.dev/eventing/pkg/apis/sources/v1" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +var ( + readyContainerSource = &v1.ContainerSource{ + Status: v1.ContainerSourceStatus{ + SourceStatus: duckv1.SourceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{{ + Type: apis.ConditionReady, + Status: corev1.ConditionTrue, + }}, + }, + }, + }, + } + + notReadyContainerSource = &v1.ContainerSource{ + Status: v1.ContainerSourceStatus{ + SourceStatus: duckv1.SourceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{{ + Type: apis.ConditionReady, + Status: corev1.ConditionFalse, + Reason: "Testing", + Message: "hi", + }}, + }, + }, + }, + } +) + +func TestIntegrationSourceGetConditionSet(t *testing.T) { + r := &IntegrationSource{} + + if got, want := r.GetConditionSet().GetTopLevelConditionType(), apis.ConditionReady; got != want { + t.Errorf("GetTopLevelCondition=%v, want=%v", got, want) + } +} + +func TestIntegrationSourceStatusIsReady(t *testing.T) { + tests := []struct { + name string + s *IntegrationSourceStatus + wantConditionStatus corev1.ConditionStatus + want bool + }{{ + name: "uninitialized", + s: &IntegrationSourceStatus{}, + want: false, + }, { + name: "initialized", + s: func() *IntegrationSourceStatus { + s := &IntegrationSourceStatus{} + s.InitializeConditions() + return s + }(), + wantConditionStatus: corev1.ConditionUnknown, + want: false, + }, { + name: "mark ready container source", + s: func() *IntegrationSourceStatus { + s := &IntegrationSourceStatus{} + s.InitializeConditions() + s.PropagateContainerSourceStatus(&readyContainerSource.Status) + return s + }(), + wantConditionStatus: corev1.ConditionTrue, + want: true, + }, { + name: "mark not ready container source", + s: func() *IntegrationSourceStatus { + s := &IntegrationSourceStatus{} + s.InitializeConditions() + s.PropagateContainerSourceStatus(¬ReadyContainerSource.Status) + return s + }(), + wantConditionStatus: corev1.ConditionFalse, + want: false, + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.wantConditionStatus != "" { + gotConditionStatus := test.s.GetTopLevelCondition().Status + if gotConditionStatus != test.wantConditionStatus { + t.Errorf("unexpected condition status: want %v, got %v", test.wantConditionStatus, gotConditionStatus) + } + } + got := test.s.IsReady() + if got != test.want { + t.Errorf("unexpected readiness: want %v, got %v", test.want, got) + } + }) + } +} + +func TestIntegrationSourceStatusGetCondition(t *testing.T) { + tests := []struct { + name string + s *IntegrationSourceStatus + condQuery apis.ConditionType + want *apis.Condition + }{{ + name: "uninitialized", + s: &IntegrationSourceStatus{}, + condQuery: IntegrationSourceConditionReady, + want: nil, + }, { + name: "initialized", + s: func() *IntegrationSourceStatus { + s := &IntegrationSourceStatus{} + s.InitializeConditions() + return s + }(), + condQuery: IntegrationSourceConditionReady, + want: &apis.Condition{ + Type: IntegrationSourceConditionReady, + Status: corev1.ConditionUnknown, + }, + }, { + name: "mark ready cs", + s: func() *IntegrationSourceStatus { + s := &IntegrationSourceStatus{} + s.InitializeConditions() + s.PropagateContainerSourceStatus(&readyContainerSource.Status) + return s + }(), + condQuery: IntegrationSourceConditionReady, + want: &apis.Condition{ + Type: IntegrationSourceConditionReady, + Status: corev1.ConditionTrue, + }, + }, { + name: "mark ready cs then no cs", + s: func() *IntegrationSourceStatus { + s := &IntegrationSourceStatus{} + s.InitializeConditions() + s.PropagateContainerSourceStatus(&readyContainerSource.Status) + s.PropagateContainerSourceStatus(¬ReadyContainerSource.Status) + return s + }(), + condQuery: IntegrationSourceConditionReady, + want: &apis.Condition{ + Type: IntegrationSourceConditionReady, + Status: corev1.ConditionFalse, + Reason: "Testing", + Message: "hi", + }, + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := test.s.GetCondition(test.condQuery) + ignoreTime := cmpopts.IgnoreFields(apis.Condition{}, + "LastTransitionTime", "Severity") + if diff := cmp.Diff(test.want, got, ignoreTime); diff != "" { + t.Error("unexpected condition (-want, +got) =", diff) + } + }) + } +} diff --git a/pkg/apis/sources/v1alpha1/integration_types.go b/pkg/apis/sources/v1alpha1/integration_types.go new file mode 100644 index 00000000000..15b4e60a2b0 --- /dev/null +++ b/pkg/apis/sources/v1alpha1/integration_types.go @@ -0,0 +1,185 @@ +/* +Copyright 2020 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" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + "knative.dev/pkg/kmeta" +) + +// +genclient +// +genreconciler +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// IntegrationSource is the Schema for the Integrationsources API +type IntegrationSource struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec IntegrationSourceSpec `json:"spec,omitempty"` + Status IntegrationSourceStatus `json:"status,omitempty"` +} + +var ( + _ runtime.Object = (*IntegrationSource)(nil) + _ kmeta.OwnerRefable = (*IntegrationSource)(nil) + _ apis.Validatable = (*IntegrationSource)(nil) + _ apis.Defaultable = (*IntegrationSource)(nil) + _ apis.HasSpec = (*IntegrationSource)(nil) + _ duckv1.KRShaped = (*IntegrationSource)(nil) + _ apis.Convertible = (*IntegrationSource)(nil) +) + +// IntegrationSourceSpec defines the desired state of IntegrationSource +type IntegrationSourceSpec struct { + // inherits duck/v1 SourceSpec, which currently provides: + // * Sink - a reference to an object that will resolve to a domain name or + // a URI directly to use as the sink. + // * CloudEventOverrides - defines overrides to control the output format + // and modifications of the event sent to the sink. + duckv1.SourceSpec `json:",inline"` + + Aws *Aws `json:"aws,omitempty"` // AWS source configuration + Timer *Timer `json:"timer,omitempty"` // Timer configuration +} + +type Timer struct { + Period int `json:"period" default:"1000"` // Interval (in milliseconds) between producing messages + Message string `json:"message"` // Message to generate + ContentType string `json:"contentType" default:"text/plain"` // Content type of generated message + RepeatCount int `json:"repeatCount,omitempty"` // Max number of fires (optional) +} + +type AWSCommon struct { + // Auth is the S3 authentication (accessKey/secretKey) configuration. + Region string `json:"region,omitempty"` // AWS region + ProfileCredentialsName string `json:"profileCredentialsName,omitempty"` // Profile name for profile credentials provider + SessionToken string `json:"sessionToken,omitempty"` // Session token + URIEndpointOverride string `json:"uriEndpointOverride,omitempty"` // Override endpoint URI + OverrideEndpoint bool `json:"overrideEndpoint" default:"false"` // Override endpoint flag +} + +type AWSS3 struct { + AWSCommon `json:",inline"` // Embeds AWSCommon to inherit its fields in JSON + BucketNameOrArn string `json:"bucketNameOrArn,omitempty"` // S3 Bucket name or ARN + DeleteAfterRead bool `json:"deleteAfterRead" default:"true"` // Auto-delete objects after reading + MoveAfterRead bool `json:"moveAfterRead" default:"false"` // Move objects after reading + DestinationBucket string `json:"destinationBucket,omitempty"` // Destination bucket for moved objects + DestinationBucketPrefix string `json:"destinationBucketPrefix,omitempty"` // Prefix for moved objects + DestinationBucketSuffix string `json:"destinationBucketSuffix,omitempty"` // Suffix for moved objects + AutoCreateBucket bool `json:"autoCreateBucket" default:"false"` // Auto-create S3 bucket + Prefix string `json:"prefix,omitempty"` // S3 bucket prefix for search + IgnoreBody bool `json:"ignoreBody" default:"false"` // Ignore object body + ForcePathStyle bool `json:"forcePathStyle" default:"false"` // Force path style for bucket access + Delay int `json:"delay" default:"500"` // Delay between polls in milliseconds + MaxMessagesPerPoll int `json:"maxMessagesPerPoll" default:"10"` // Max messages to poll per request +} + +type AWSSQS struct { + AWSCommon `json:",inline"` // Embeds AWSCommon to inherit its fields in JSON + QueueNameOrArn string `json:"queueNameOrArn,omitempty"` // SQS Queue name or ARN + DeleteAfterRead bool `json:"deleteAfterRead" default:"true"` // Auto-delete messages after reading + AutoCreateQueue bool `json:"autoCreateQueue" default:"false"` // Auto-create SQS queue + AmazonAWSHost string `json:"amazonAWSHost" default:"amazonaws.com"` // AWS host + Protocol string `json:"protocol" default:"https"` // Communication protocol (http/https) + QueueURL string `json:"queueURL,omitempty"` // Full SQS queue URL + Greedy bool `json:"greedy" default:"false"` // Greedy scheduler + Delay int `json:"delay" default:"500"` // Delay between polls in milliseconds + MaxMessagesPerPoll int `json:"maxMessagesPerPoll" default:"1"` // Max messages to return (1-10) + WaitTimeSeconds int `json:"waitTimeSeconds,omitempty"` // Wait time for messages + VisibilityTimeout int `json:"visibilityTimeout,omitempty"` // Visibility timeout in seconds +} + +type AWSDDBStreams struct { + AWSCommon `json:",inline"` // Embeds AWSCommon to inherit its fields in JSON + Table string `json:"table,omitempty"` // The name of the DynamoDB table + StreamIteratorType string `json:"streamIteratorType,omitempty" default:"FROM_LATEST"` // Defines where in the DynamoDB stream to start getting records + Delay int `json:"delay,omitempty" default:"500"` // Delay in milliseconds before the next poll from the database +} + +type Aws struct { + S3 *AWSS3 `json:"s3,omitempty"` // S3 source configuration + SQS *AWSSQS `json:"sqs,omitempty"` // SQS source configuration + DDBStreams *AWSDDBStreams `json:"ddb-streams,omitempty"` // DynamoDB Streams source configuration + Auth *Auth `json:"auth,omitempty"` +} + +type Auth struct { + // Auth Secret + Secret *Secret `json:"secret,omitempty"` + + // AccessKey is the AWS access key ID. + AccessKey string `json:"accessKey,omitempty"` + + // SecretKey is the AWS secret access key. + SecretKey string `json:"secretKey,omitempty"` +} + +func (a *Auth) HasAuth() bool { + return a != nil && a.Secret != nil && + a.Secret.Ref != nil && a.Secret.Ref.Name != "" +} + +type Secret struct { + // Secret reference for SASL and SSL configurations. + Ref *SecretReference `json:"ref,omitempty"` +} + +type SecretReference struct { + // Secret name. + Name string `json:"name"` +} + +// GetGroupVersionKind returns the GroupVersionKind. +func (*IntegrationSource) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("IntegrationSource") +} + +// IntegrationSourceStatus defines the observed state of IntegrationSource +type IntegrationSourceStatus struct { + // inherits duck/v1 SourceStatus, which currently provides: + // * ObservedGeneration - the 'Generation' of the Service that was last + // processed by the controller. + // * Conditions - the latest available observations of a resource's current + // state. + // * SinkURI - the current active sink URI that has been configured for the + // Source. + duckv1.SourceStatus `json:",inline"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// IntegrationSourceList contains a list of IntegrationSource +type IntegrationSourceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []IntegrationSource `json:"items"` +} + +// GetUntypedSpec returns the spec of the IntegrationSource. +func (c *IntegrationSource) GetUntypedSpec() interface{} { + return c.Spec +} + +// GetStatus retrieves the status of the IntegrationSource. Implements the KRShaped interface. +func (c *IntegrationSource) GetStatus() *duckv1.Status { + return &c.Status.Status +} diff --git a/pkg/apis/sources/v1alpha1/integration_types_test.go b/pkg/apis/sources/v1alpha1/integration_types_test.go new file mode 100644 index 00000000000..83b2ff0ffc5 --- /dev/null +++ b/pkg/apis/sources/v1alpha1/integration_types_test.go @@ -0,0 +1,109 @@ +/* +Copyright 2020 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 TestIntegrationSource_GetStatus(t *testing.T) { + r := &IntegrationSource{ + Status: IntegrationSourceStatus{}, + } + if got, want := r.GetStatus(), &r.Status.Status; got != want { + t.Errorf("GetStatus=%v, want=%v", got, want) + } +} + +func TestIntegrationSource_GetGroupVersionKind(t *testing.T) { + src := &IntegrationSource{} + gvk := src.GetGroupVersionKind() + + if gvk.Kind != "IntegrationSource" { + t.Errorf("Should be IntegrationSource.") + } +} + +func TestTimer(t *testing.T) { + timer := Timer{ + Period: 1000, + Message: "test message", + } + + if timer.Period != 1000 { + t.Errorf("Timer.Period = %v, want '1000'", timer.Period) + } + if timer.Message != "test message" { + t.Errorf("Timer.Message = %v, want 'test message'", timer.Message) + } +} + +func TestAWS(t *testing.T) { + s3 := AWSS3{ + AWSCommon: AWSCommon{ + Region: "eu-north-1", + }, + BucketNameOrArn: "example-bucket", + } + + if s3.Region != "eu-north-1" { + t.Errorf("AWSS3.Region = %v, want 'eu-north-1'", s3.Region) + } + + sqs := AWSSQS{ + AWSCommon: AWSCommon{ + Region: "eu-north-1", + }, + QueueNameOrArn: "example-queue", + } + + if sqs.Region != "eu-north-1" { + t.Errorf("AWSSQS.Region = %v, want 'eu-north-1'", sqs.Region) + } + + ddbStreams := AWSDDBStreams{ + AWSCommon: AWSCommon{ + Region: "eu-north-1", + }, + Table: "example-table", + } + + if ddbStreams.Region != "eu-north-1" { + t.Errorf("AWSDDBStreams.Region = %v, want 'eu-north-1'", ddbStreams.Region) + } +} + +// TestAuthFieldAccess tests the HasAuth method and field access in Auth struct +func TestAuthFieldAccess(t *testing.T) { + auth := Auth{ + Secret: &Secret{ + Ref: &SecretReference{ + Name: "aws-secret", + }, + }, + AccessKey: "access-key", + SecretKey: "secret-key", + } + + if !auth.HasAuth() { + t.Error("Auth.HasAuth() = false, want true") + } + + if auth.Secret.Ref.Name != "aws-secret" { + t.Errorf("Auth.Secret.Ref.Name = %v, want 'aws-secret'", auth.Secret.Ref.Name) + } +} diff --git a/pkg/apis/sources/v1alpha1/integration_validation.go b/pkg/apis/sources/v1alpha1/integration_validation.go new file mode 100644 index 00000000000..4df223eb5dd --- /dev/null +++ b/pkg/apis/sources/v1alpha1/integration_validation.go @@ -0,0 +1,98 @@ +/* +Copyright 2020 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 ( + "context" + + "knative.dev/pkg/apis" +) + +func (source *IntegrationSource) Validate(ctx context.Context) *apis.FieldError { + ctx = apis.WithinParent(ctx, source.ObjectMeta) + return source.Spec.Validate(ctx).ViaField("spec") +} + +func (spec *IntegrationSourceSpec) Validate(ctx context.Context) *apis.FieldError { + var errs *apis.FieldError + + // Count how many fields are set to ensure mutual exclusivity + sourceSetCount := 0 + if spec.Timer != nil { + sourceSetCount++ + } + if spec.Aws != nil { + if spec.Aws.S3 != nil { + sourceSetCount++ + } + if spec.Aws.SQS != nil { + sourceSetCount++ + } + if spec.Aws.DDBStreams != nil { + sourceSetCount++ + } + } + + // Validate that only one source field is set + if sourceSetCount > 1 { + errs = errs.Also(apis.ErrGeneric("only one source type can be set", "spec")) + } else if sourceSetCount == 0 { + errs = errs.Also(apis.ErrGeneric("at least one source type must be specified", "spec")) + } + + // Only perform AWS-specific validation if exactly one AWS source is configured + if sourceSetCount == 1 && spec.Aws != nil { + if spec.Aws.S3 != nil || spec.Aws.SQS != nil || spec.Aws.DDBStreams != nil { + // Check that AWS Auth is properly configured + if !spec.Aws.Auth.HasAuth() { + errs = errs.Also(apis.ErrMissingField("aws.auth.secret.ref.name")) + } + } + + // Additional validation for AWS S3 required fields + if spec.Aws.S3 != nil { + if spec.Aws.S3.BucketNameOrArn == "" { + errs = errs.Also(apis.ErrMissingField("aws.s3.bucketNameOrArn")) + } + if spec.Aws.S3.Region == "" { + errs = errs.Also(apis.ErrMissingField("aws.s3.region")) + } + } + + // Additional validation for AWS SQS required fields + if spec.Aws.SQS != nil { + if spec.Aws.SQS.QueueNameOrArn == "" { + errs = errs.Also(apis.ErrMissingField("aws.sqs.queueNameOrArn")) + } + if spec.Aws.SQS.Region == "" { + errs = errs.Also(apis.ErrMissingField("aws.sqs.region")) + } + } + + // Additional validation for AWS DDBStreams required fields + if spec.Aws.DDBStreams != nil { + if spec.Aws.DDBStreams.Table == "" { + errs = errs.Also(apis.ErrMissingField("aws.ddb-streams.table")) + } + if spec.Aws.DDBStreams.Region == "" { + errs = errs.Also(apis.ErrMissingField("aws.ddb-streams.region")) + } + } + } + + return errs +} diff --git a/pkg/apis/sources/v1alpha1/integration_validation_test.go b/pkg/apis/sources/v1alpha1/integration_validation_test.go new file mode 100644 index 00000000000..6e0071703d1 --- /dev/null +++ b/pkg/apis/sources/v1alpha1/integration_validation_test.go @@ -0,0 +1,240 @@ +/* +Copyright 2020 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 ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "knative.dev/pkg/apis" +) + +func TestIntegrationSourceSpecValidation(t *testing.T) { + tests := []struct { + name string + spec IntegrationSourceSpec + want *apis.FieldError + }{ + { + name: "valid timer source", + spec: IntegrationSourceSpec{ + Timer: &Timer{ + Period: 1000, + Message: "test message", + ContentType: "text/plain", + }, + }, + want: nil, + }, + { + name: "valid AWS S3 source with auth and region", + spec: IntegrationSourceSpec{ + Aws: &Aws{ + S3: &AWSS3{ + AWSCommon: AWSCommon{ + Region: "us-east-1", + }, + BucketNameOrArn: "example-bucket", + }, + Auth: &Auth{ + Secret: &Secret{ + Ref: &SecretReference{ + Name: "aws-secret", + }, + }, + }, + }, + }, + want: nil, + }, + { + name: "valid AWS SQS source with auth and region", + spec: IntegrationSourceSpec{ + Aws: &Aws{ + SQS: &AWSSQS{ + AWSCommon: AWSCommon{ + Region: "us-east-1", + }, + QueueNameOrArn: "example-queue", + }, + Auth: &Auth{ + Secret: &Secret{ + Ref: &SecretReference{ + Name: "aws-secret", + }, + }, + }, + }, + }, + want: nil, + }, + { + name: "valid AWS DDBStreams source with auth and region", + spec: IntegrationSourceSpec{ + Aws: &Aws{ + DDBStreams: &AWSDDBStreams{ + AWSCommon: AWSCommon{ + Region: "us-east-1", + }, + Table: "example-table", + }, + Auth: &Auth{ + Secret: &Secret{ + Ref: &SecretReference{ + Name: "aws-secret", + }, + }, + }, + }, + }, + want: nil, + }, + { + name: "multiple sources set (invalid)", + spec: IntegrationSourceSpec{ + Timer: &Timer{ + Period: 1000, + Message: "test message", + ContentType: "text/plain", + }, + Aws: &Aws{ + S3: &AWSS3{ + AWSCommon: AWSCommon{ + Region: "us-east-1", + }, + BucketNameOrArn: "example-bucket", + }, + }, + }, + want: apis.ErrGeneric("only one source type can be set", "spec"), + }, + { + name: "multiple AWS sources set (invalid)", + spec: IntegrationSourceSpec{ + Aws: &Aws{ + S3: &AWSS3{ + AWSCommon: AWSCommon{ + Region: "us-east-1", + }, + BucketNameOrArn: "example-bucket", + }, + SQS: &AWSSQS{ + AWSCommon: AWSCommon{ + Region: "us-east-1", + }, + QueueNameOrArn: "example-queue", + }, + Auth: &Auth{ + Secret: &Secret{ + Ref: &SecretReference{ + Name: "aws-secret", + }, + }, + }, + }, + }, + want: apis.ErrGeneric("only one source type can be set", "spec"), + }, + { + name: "AWS SQS source without QueueNameOrArn (invalid)", + spec: IntegrationSourceSpec{ + Aws: &Aws{ + SQS: &AWSSQS{ + AWSCommon: AWSCommon{ + Region: "us-east-1", + }, + }, + Auth: &Auth{ + Secret: &Secret{ + Ref: &SecretReference{ + Name: "aws-secret", + }, + }, + }, + }, + }, + want: apis.ErrMissingField("aws.sqs.queueNameOrArn"), + }, + { + name: "AWS DDBStreams source without Table (invalid)", + spec: IntegrationSourceSpec{ + Aws: &Aws{ + DDBStreams: &AWSDDBStreams{ + AWSCommon: AWSCommon{ + Region: "us-east-1", + }, + }, + Auth: &Auth{ + Secret: &Secret{ + Ref: &SecretReference{ + Name: "aws-secret", + }, + }, + }, + }, + }, + want: apis.ErrMissingField("aws.ddb-streams.table"), + }, + { + name: "no source type specified (invalid)", + spec: IntegrationSourceSpec{}, + want: apis.ErrGeneric("at least one source type must be specified", "spec"), + }, + { + name: "AWS source without auth (invalid)", + spec: IntegrationSourceSpec{ + Aws: &Aws{ + S3: &AWSS3{ + AWSCommon: AWSCommon{ + Region: "us-east-1", + }, + BucketNameOrArn: "example-bucket", + }, + }, + }, + want: apis.ErrMissingField("aws.auth.secret.ref.name"), + }, + { + name: "AWS S3 source without region (invalid)", + spec: IntegrationSourceSpec{ + Aws: &Aws{ + S3: &AWSS3{ + BucketNameOrArn: "example-bucket", + }, + Auth: &Auth{ + Secret: &Secret{ + Ref: &SecretReference{ + Name: "aws-secret", + }, + }, + }, + }, + }, + want: apis.ErrMissingField("aws.s3.region"), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := test.spec.Validate(context.TODO()) + if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { + t.Errorf("IntegrationSourceSpec.Validate (-want, +got) = %v", diff) + } + }) + } +} diff --git a/pkg/apis/sources/v1alpha1/register.go b/pkg/apis/sources/v1alpha1/register.go new file mode 100644 index 00000000000..a812c885f5b --- /dev/null +++ b/pkg/apis/sources/v1alpha1/register.go @@ -0,0 +1,52 @@ +/* +Copyright 2020 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" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/eventing/pkg/apis/sources" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: sources.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &IntegrationSource{}, + &IntegrationSourceList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/sources/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/sources/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..5fe9cf729af --- /dev/null +++ b/pkg/apis/sources/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,308 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2021 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 deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AWSCommon) DeepCopyInto(out *AWSCommon) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSCommon. +func (in *AWSCommon) DeepCopy() *AWSCommon { + if in == nil { + return nil + } + out := new(AWSCommon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AWSDDBStreams) DeepCopyInto(out *AWSDDBStreams) { + *out = *in + out.AWSCommon = in.AWSCommon + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSDDBStreams. +func (in *AWSDDBStreams) DeepCopy() *AWSDDBStreams { + if in == nil { + return nil + } + out := new(AWSDDBStreams) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AWSS3) DeepCopyInto(out *AWSS3) { + *out = *in + out.AWSCommon = in.AWSCommon + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSS3. +func (in *AWSS3) DeepCopy() *AWSS3 { + if in == nil { + return nil + } + out := new(AWSS3) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AWSSQS) DeepCopyInto(out *AWSSQS) { + *out = *in + out.AWSCommon = in.AWSCommon + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSSQS. +func (in *AWSSQS) DeepCopy() *AWSSQS { + if in == nil { + return nil + } + out := new(AWSSQS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Auth) DeepCopyInto(out *Auth) { + *out = *in + if in.Secret != nil { + in, out := &in.Secret, &out.Secret + *out = new(Secret) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Auth. +func (in *Auth) DeepCopy() *Auth { + if in == nil { + return nil + } + out := new(Auth) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Aws) DeepCopyInto(out *Aws) { + *out = *in + if in.S3 != nil { + in, out := &in.S3, &out.S3 + *out = new(AWSS3) + **out = **in + } + if in.SQS != nil { + in, out := &in.SQS, &out.SQS + *out = new(AWSSQS) + **out = **in + } + if in.DDBStreams != nil { + in, out := &in.DDBStreams, &out.DDBStreams + *out = new(AWSDDBStreams) + **out = **in + } + if in.Auth != nil { + in, out := &in.Auth, &out.Auth + *out = new(Auth) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Aws. +func (in *Aws) DeepCopy() *Aws { + if in == nil { + return nil + } + out := new(Aws) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IntegrationSource) DeepCopyInto(out *IntegrationSource) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationSource. +func (in *IntegrationSource) DeepCopy() *IntegrationSource { + if in == nil { + return nil + } + out := new(IntegrationSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IntegrationSource) 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 *IntegrationSourceList) DeepCopyInto(out *IntegrationSourceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]IntegrationSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationSourceList. +func (in *IntegrationSourceList) DeepCopy() *IntegrationSourceList { + if in == nil { + return nil + } + out := new(IntegrationSourceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *IntegrationSourceList) 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 *IntegrationSourceSpec) DeepCopyInto(out *IntegrationSourceSpec) { + *out = *in + in.SourceSpec.DeepCopyInto(&out.SourceSpec) + if in.Aws != nil { + in, out := &in.Aws, &out.Aws + *out = new(Aws) + (*in).DeepCopyInto(*out) + } + if in.Timer != nil { + in, out := &in.Timer, &out.Timer + *out = new(Timer) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationSourceSpec. +func (in *IntegrationSourceSpec) DeepCopy() *IntegrationSourceSpec { + if in == nil { + return nil + } + out := new(IntegrationSourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IntegrationSourceStatus) DeepCopyInto(out *IntegrationSourceStatus) { + *out = *in + in.SourceStatus.DeepCopyInto(&out.SourceStatus) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationSourceStatus. +func (in *IntegrationSourceStatus) DeepCopy() *IntegrationSourceStatus { + if in == nil { + return nil + } + out := new(IntegrationSourceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Secret) DeepCopyInto(out *Secret) { + *out = *in + if in.Ref != nil { + in, out := &in.Ref, &out.Ref + *out = new(SecretReference) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Secret. +func (in *Secret) DeepCopy() *Secret { + if in == nil { + return nil + } + out := new(Secret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretReference) DeepCopyInto(out *SecretReference) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretReference. +func (in *SecretReference) DeepCopy() *SecretReference { + if in == nil { + return nil + } + out := new(SecretReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Timer) DeepCopyInto(out *Timer) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Timer. +func (in *Timer) DeepCopy() *Timer { + if in == nil { + return nil + } + out := new(Timer) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index 72890b65cc8..dcb08498521 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -34,6 +34,7 @@ import ( messagingv1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/messaging/v1" sinksv1alpha1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sinks/v1alpha1" sourcesv1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1" + sourcesv1alpha1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1" sourcesv1beta2 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1beta2" ) @@ -48,6 +49,7 @@ type Interface interface { MessagingV1() messagingv1.MessagingV1Interface SinksV1alpha1() sinksv1alpha1.SinksV1alpha1Interface SourcesV1() sourcesv1.SourcesV1Interface + SourcesV1alpha1() sourcesv1alpha1.SourcesV1alpha1Interface SourcesV1beta2() sourcesv1beta2.SourcesV1beta2Interface } @@ -63,6 +65,7 @@ type Clientset struct { messagingV1 *messagingv1.MessagingV1Client sinksV1alpha1 *sinksv1alpha1.SinksV1alpha1Client sourcesV1 *sourcesv1.SourcesV1Client + sourcesV1alpha1 *sourcesv1alpha1.SourcesV1alpha1Client sourcesV1beta2 *sourcesv1beta2.SourcesV1beta2Client } @@ -111,6 +114,11 @@ func (c *Clientset) SourcesV1() sourcesv1.SourcesV1Interface { return c.sourcesV1 } +// SourcesV1alpha1 retrieves the SourcesV1alpha1Client +func (c *Clientset) SourcesV1alpha1() sourcesv1alpha1.SourcesV1alpha1Interface { + return c.sourcesV1alpha1 +} + // SourcesV1beta2 retrieves the SourcesV1beta2Client func (c *Clientset) SourcesV1beta2() sourcesv1beta2.SourcesV1beta2Interface { return c.sourcesV1beta2 @@ -196,6 +204,10 @@ func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, if err != nil { return nil, err } + cs.sourcesV1alpha1, err = sourcesv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient) + if err != nil { + return nil, err + } cs.sourcesV1beta2, err = sourcesv1beta2.NewForConfigAndClient(&configShallowCopy, httpClient) if err != nil { return nil, err @@ -230,6 +242,7 @@ func New(c rest.Interface) *Clientset { cs.messagingV1 = messagingv1.New(c) cs.sinksV1alpha1 = sinksv1alpha1.New(c) cs.sourcesV1 = sourcesv1.New(c) + cs.sourcesV1alpha1 = sourcesv1alpha1.New(c) cs.sourcesV1beta2 = sourcesv1beta2.New(c) cs.DiscoveryClient = discovery.NewDiscoveryClient(c) diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index 83462864136..90b59fe63a7 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -43,6 +43,8 @@ import ( fakesinksv1alpha1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sinks/v1alpha1/fake" sourcesv1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1" fakesourcesv1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1/fake" + sourcesv1alpha1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1" + fakesourcesv1alpha1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake" sourcesv1beta2 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1beta2" fakesourcesv1beta2 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1beta2/fake" ) @@ -142,6 +144,11 @@ func (c *Clientset) SourcesV1() sourcesv1.SourcesV1Interface { return &fakesourcesv1.FakeSourcesV1{Fake: &c.Fake} } +// SourcesV1alpha1 retrieves the SourcesV1alpha1Client +func (c *Clientset) SourcesV1alpha1() sourcesv1alpha1.SourcesV1alpha1Interface { + return &fakesourcesv1alpha1.FakeSourcesV1alpha1{Fake: &c.Fake} +} + // SourcesV1beta2 retrieves the SourcesV1beta2Client func (c *Clientset) SourcesV1beta2() sourcesv1beta2.SourcesV1beta2Interface { return &fakesourcesv1beta2.FakeSourcesV1beta2{Fake: &c.Fake} diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index 6fc03b4ba58..834b36ce0ae 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -33,6 +33,7 @@ import ( messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1" sinksv1alpha1 "knative.dev/eventing/pkg/apis/sinks/v1alpha1" sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" + sourcesv1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" sourcesv1beta2 "knative.dev/eventing/pkg/apis/sources/v1beta2" ) @@ -49,6 +50,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{ messagingv1.AddToScheme, sinksv1alpha1.AddToScheme, sourcesv1.AddToScheme, + sourcesv1alpha1.AddToScheme, sourcesv1beta2.AddToScheme, } diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go index 5d2955e038f..786a698e532 100644 --- a/pkg/client/clientset/versioned/scheme/register.go +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -33,6 +33,7 @@ import ( messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1" sinksv1alpha1 "knative.dev/eventing/pkg/apis/sinks/v1alpha1" sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" + sourcesv1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" sourcesv1beta2 "knative.dev/eventing/pkg/apis/sources/v1beta2" ) @@ -49,6 +50,7 @@ var localSchemeBuilder = runtime.SchemeBuilder{ messagingv1.AddToScheme, sinksv1alpha1.AddToScheme, sourcesv1.AddToScheme, + sourcesv1alpha1.AddToScheme, sourcesv1beta2.AddToScheme, } diff --git a/pkg/client/clientset/versioned/typed/sources/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/sources/v1alpha1/doc.go new file mode 100644 index 00000000000..0b13fd8e001 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/sources/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2021 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. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake/doc.go new file mode 100644 index 00000000000..40528db3a52 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2021 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 has the automatically generated clients. +package fake diff --git a/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake/fake_integrationsource.go b/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake/fake_integrationsource.go new file mode 100644 index 00000000000..cf59005eb09 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake/fake_integrationsource.go @@ -0,0 +1,141 @@ +/* +Copyright 2021 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 ( + "context" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" +) + +// FakeIntegrationSources implements IntegrationSourceInterface +type FakeIntegrationSources struct { + Fake *FakeSourcesV1alpha1 + ns string +} + +var integrationsourcesResource = v1alpha1.SchemeGroupVersion.WithResource("integrationsources") + +var integrationsourcesKind = v1alpha1.SchemeGroupVersion.WithKind("IntegrationSource") + +// Get takes name of the integrationSource, and returns the corresponding integrationSource object, and an error if there is any. +func (c *FakeIntegrationSources) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.IntegrationSource, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(integrationsourcesResource, c.ns, name), &v1alpha1.IntegrationSource{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.IntegrationSource), err +} + +// List takes label and field selectors, and returns the list of IntegrationSources that match those selectors. +func (c *FakeIntegrationSources) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.IntegrationSourceList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(integrationsourcesResource, integrationsourcesKind, c.ns, opts), &v1alpha1.IntegrationSourceList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.IntegrationSourceList{ListMeta: obj.(*v1alpha1.IntegrationSourceList).ListMeta} + for _, item := range obj.(*v1alpha1.IntegrationSourceList).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 integrationSources. +func (c *FakeIntegrationSources) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(integrationsourcesResource, c.ns, opts)) + +} + +// Create takes the representation of a integrationSource and creates it. Returns the server's representation of the integrationSource, and an error, if there is any. +func (c *FakeIntegrationSources) Create(ctx context.Context, integrationSource *v1alpha1.IntegrationSource, opts v1.CreateOptions) (result *v1alpha1.IntegrationSource, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(integrationsourcesResource, c.ns, integrationSource), &v1alpha1.IntegrationSource{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.IntegrationSource), err +} + +// Update takes the representation of a integrationSource and updates it. Returns the server's representation of the integrationSource, and an error, if there is any. +func (c *FakeIntegrationSources) Update(ctx context.Context, integrationSource *v1alpha1.IntegrationSource, opts v1.UpdateOptions) (result *v1alpha1.IntegrationSource, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(integrationsourcesResource, c.ns, integrationSource), &v1alpha1.IntegrationSource{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.IntegrationSource), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeIntegrationSources) UpdateStatus(ctx context.Context, integrationSource *v1alpha1.IntegrationSource, opts v1.UpdateOptions) (*v1alpha1.IntegrationSource, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(integrationsourcesResource, "status", c.ns, integrationSource), &v1alpha1.IntegrationSource{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.IntegrationSource), err +} + +// Delete takes name of the integrationSource and deletes it. Returns an error if one occurs. +func (c *FakeIntegrationSources) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(integrationsourcesResource, c.ns, name, opts), &v1alpha1.IntegrationSource{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeIntegrationSources) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(integrationsourcesResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.IntegrationSourceList{}) + return err +} + +// Patch applies the patch and returns the patched integrationSource. +func (c *FakeIntegrationSources) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IntegrationSource, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(integrationsourcesResource, c.ns, name, pt, data, subresources...), &v1alpha1.IntegrationSource{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.IntegrationSource), err +} diff --git a/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake/fake_sources_client.go b/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake/fake_sources_client.go new file mode 100644 index 00000000000..29164f5c7a6 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake/fake_sources_client.go @@ -0,0 +1,40 @@ +/* +Copyright 2021 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 ( + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" + v1alpha1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1" +) + +type FakeSourcesV1alpha1 struct { + *testing.Fake +} + +func (c *FakeSourcesV1alpha1) IntegrationSources(namespace string) v1alpha1.IntegrationSourceInterface { + return &FakeIntegrationSources{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeSourcesV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/client/clientset/versioned/typed/sources/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/sources/v1alpha1/generated_expansion.go new file mode 100644 index 00000000000..93a464c5232 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/sources/v1alpha1/generated_expansion.go @@ -0,0 +1,21 @@ +/* +Copyright 2021 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 + +type IntegrationSourceExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/sources/v1alpha1/integrationsource.go b/pkg/client/clientset/versioned/typed/sources/v1alpha1/integrationsource.go new file mode 100644 index 00000000000..aa695a3b878 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/sources/v1alpha1/integrationsource.go @@ -0,0 +1,195 @@ +/* +Copyright 2021 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 ( + "context" + "time" + + 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" + v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" + scheme "knative.dev/eventing/pkg/client/clientset/versioned/scheme" +) + +// IntegrationSourcesGetter has a method to return a IntegrationSourceInterface. +// A group's client should implement this interface. +type IntegrationSourcesGetter interface { + IntegrationSources(namespace string) IntegrationSourceInterface +} + +// IntegrationSourceInterface has methods to work with IntegrationSource resources. +type IntegrationSourceInterface interface { + Create(ctx context.Context, integrationSource *v1alpha1.IntegrationSource, opts v1.CreateOptions) (*v1alpha1.IntegrationSource, error) + Update(ctx context.Context, integrationSource *v1alpha1.IntegrationSource, opts v1.UpdateOptions) (*v1alpha1.IntegrationSource, error) + UpdateStatus(ctx context.Context, integrationSource *v1alpha1.IntegrationSource, opts v1.UpdateOptions) (*v1alpha1.IntegrationSource, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.IntegrationSource, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.IntegrationSourceList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IntegrationSource, err error) + IntegrationSourceExpansion +} + +// integrationSources implements IntegrationSourceInterface +type integrationSources struct { + client rest.Interface + ns string +} + +// newIntegrationSources returns a IntegrationSources +func newIntegrationSources(c *SourcesV1alpha1Client, namespace string) *integrationSources { + return &integrationSources{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the integrationSource, and returns the corresponding integrationSource object, and an error if there is any. +func (c *integrationSources) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.IntegrationSource, err error) { + result = &v1alpha1.IntegrationSource{} + err = c.client.Get(). + Namespace(c.ns). + Resource("integrationsources"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of IntegrationSources that match those selectors. +func (c *integrationSources) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.IntegrationSourceList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.IntegrationSourceList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("integrationsources"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested integrationSources. +func (c *integrationSources) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("integrationsources"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a integrationSource and creates it. Returns the server's representation of the integrationSource, and an error, if there is any. +func (c *integrationSources) Create(ctx context.Context, integrationSource *v1alpha1.IntegrationSource, opts v1.CreateOptions) (result *v1alpha1.IntegrationSource, err error) { + result = &v1alpha1.IntegrationSource{} + err = c.client.Post(). + Namespace(c.ns). + Resource("integrationsources"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(integrationSource). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a integrationSource and updates it. Returns the server's representation of the integrationSource, and an error, if there is any. +func (c *integrationSources) Update(ctx context.Context, integrationSource *v1alpha1.IntegrationSource, opts v1.UpdateOptions) (result *v1alpha1.IntegrationSource, err error) { + result = &v1alpha1.IntegrationSource{} + err = c.client.Put(). + Namespace(c.ns). + Resource("integrationsources"). + Name(integrationSource.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(integrationSource). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *integrationSources) UpdateStatus(ctx context.Context, integrationSource *v1alpha1.IntegrationSource, opts v1.UpdateOptions) (result *v1alpha1.IntegrationSource, err error) { + result = &v1alpha1.IntegrationSource{} + err = c.client.Put(). + Namespace(c.ns). + Resource("integrationsources"). + Name(integrationSource.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(integrationSource). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the integrationSource and deletes it. Returns an error if one occurs. +func (c *integrationSources) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("integrationsources"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *integrationSources) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("integrationsources"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched integrationSource. +func (c *integrationSources) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IntegrationSource, err error) { + result = &v1alpha1.IntegrationSource{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("integrationsources"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/sources/v1alpha1/sources_client.go b/pkg/client/clientset/versioned/typed/sources/v1alpha1/sources_client.go new file mode 100644 index 00000000000..838e0042784 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/sources/v1alpha1/sources_client.go @@ -0,0 +1,107 @@ +/* +Copyright 2021 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 ( + "net/http" + + rest "k8s.io/client-go/rest" + v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" + "knative.dev/eventing/pkg/client/clientset/versioned/scheme" +) + +type SourcesV1alpha1Interface interface { + RESTClient() rest.Interface + IntegrationSourcesGetter +} + +// SourcesV1alpha1Client is used to interact with features provided by the sources.knative.dev group. +type SourcesV1alpha1Client struct { + restClient rest.Interface +} + +func (c *SourcesV1alpha1Client) IntegrationSources(namespace string) IntegrationSourceInterface { + return newIntegrationSources(c, namespace) +} + +// NewForConfig creates a new SourcesV1alpha1Client for the given config. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). +func NewForConfig(c *rest.Config) (*SourcesV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + httpClient, err := rest.HTTPClientFor(&config) + if err != nil { + return nil, err + } + return NewForConfigAndClient(&config, httpClient) +} + +// NewForConfigAndClient creates a new SourcesV1alpha1Client for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +func NewForConfigAndClient(c *rest.Config, h *http.Client) (*SourcesV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientForConfigAndClient(&config, h) + if err != nil { + return nil, err + } + return &SourcesV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new SourcesV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *SourcesV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new SourcesV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *SourcesV1alpha1Client { + return &SourcesV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *SourcesV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 41c490e06ce..cf7e4cf4896 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -32,6 +32,7 @@ import ( messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1" sinksv1alpha1 "knative.dev/eventing/pkg/apis/sinks/v1alpha1" sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" + sourcesv1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" sourcesv1beta2 "knative.dev/eventing/pkg/apis/sources/v1beta2" ) @@ -111,6 +112,10 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource case sourcesv1.SchemeGroupVersion.WithResource("sinkbindings"): return &genericInformer{resource: resource.GroupResource(), informer: f.Sources().V1().SinkBindings().Informer()}, nil + // Group=sources.knative.dev, Version=v1alpha1 + case sourcesv1alpha1.SchemeGroupVersion.WithResource("integrationsources"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Sources().V1alpha1().IntegrationSources().Informer()}, nil + // Group=sources.knative.dev, Version=v1beta2 case sourcesv1beta2.SchemeGroupVersion.WithResource("pingsources"): return &genericInformer{resource: resource.GroupResource(), informer: f.Sources().V1beta2().PingSources().Informer()}, nil diff --git a/pkg/client/informers/externalversions/sources/interface.go b/pkg/client/informers/externalversions/sources/interface.go index c8b6385943e..20ad73586e7 100644 --- a/pkg/client/informers/externalversions/sources/interface.go +++ b/pkg/client/informers/externalversions/sources/interface.go @@ -21,6 +21,7 @@ package sources import ( internalinterfaces "knative.dev/eventing/pkg/client/informers/externalversions/internalinterfaces" v1 "knative.dev/eventing/pkg/client/informers/externalversions/sources/v1" + v1alpha1 "knative.dev/eventing/pkg/client/informers/externalversions/sources/v1alpha1" v1beta2 "knative.dev/eventing/pkg/client/informers/externalversions/sources/v1beta2" ) @@ -28,6 +29,8 @@ import ( type Interface interface { // V1 provides access to shared informers for resources in V1. V1() v1.Interface + // V1alpha1 provides access to shared informers for resources in V1alpha1. + V1alpha1() v1alpha1.Interface // V1beta2 provides access to shared informers for resources in V1beta2. V1beta2() v1beta2.Interface } @@ -48,6 +51,11 @@ func (g *group) V1() v1.Interface { return v1.New(g.factory, g.namespace, g.tweakListOptions) } +// V1alpha1 returns a new v1alpha1.Interface. +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} + // V1beta2 returns a new v1beta2.Interface. func (g *group) V1beta2() v1beta2.Interface { return v1beta2.New(g.factory, g.namespace, g.tweakListOptions) diff --git a/pkg/client/informers/externalversions/sources/v1alpha1/integrationsource.go b/pkg/client/informers/externalversions/sources/v1alpha1/integrationsource.go new file mode 100644 index 00000000000..34e3a1f1b34 --- /dev/null +++ b/pkg/client/informers/externalversions/sources/v1alpha1/integrationsource.go @@ -0,0 +1,90 @@ +/* +Copyright 2021 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 ( + "context" + time "time" + + 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" + sourcesv1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" + versioned "knative.dev/eventing/pkg/client/clientset/versioned" + internalinterfaces "knative.dev/eventing/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "knative.dev/eventing/pkg/client/listers/sources/v1alpha1" +) + +// IntegrationSourceInformer provides access to a shared informer and lister for +// IntegrationSources. +type IntegrationSourceInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.IntegrationSourceLister +} + +type integrationSourceInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewIntegrationSourceInformer constructs a new informer for IntegrationSource 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 NewIntegrationSourceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredIntegrationSourceInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredIntegrationSourceInformer constructs a new informer for IntegrationSource 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 NewFilteredIntegrationSourceInformer(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.SourcesV1alpha1().IntegrationSources(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.SourcesV1alpha1().IntegrationSources(namespace).Watch(context.TODO(), options) + }, + }, + &sourcesv1alpha1.IntegrationSource{}, + resyncPeriod, + indexers, + ) +} + +func (f *integrationSourceInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredIntegrationSourceInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *integrationSourceInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&sourcesv1alpha1.IntegrationSource{}, f.defaultInformer) +} + +func (f *integrationSourceInformer) Lister() v1alpha1.IntegrationSourceLister { + return v1alpha1.NewIntegrationSourceLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/sources/v1alpha1/interface.go b/pkg/client/informers/externalversions/sources/v1alpha1/interface.go new file mode 100644 index 00000000000..6dce960d202 --- /dev/null +++ b/pkg/client/informers/externalversions/sources/v1alpha1/interface.go @@ -0,0 +1,45 @@ +/* +Copyright 2021 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 ( + internalinterfaces "knative.dev/eventing/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // IntegrationSources returns a IntegrationSourceInformer. + IntegrationSources() IntegrationSourceInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// IntegrationSources returns a IntegrationSourceInformer. +func (v *version) IntegrationSources() IntegrationSourceInformer { + return &integrationSourceInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/injection/informers/sources/v1alpha1/integrationsource/fake/fake.go b/pkg/client/injection/informers/sources/v1alpha1/integrationsource/fake/fake.go new file mode 100644 index 00000000000..6a0fed73290 --- /dev/null +++ b/pkg/client/injection/informers/sources/v1alpha1/integrationsource/fake/fake.go @@ -0,0 +1,40 @@ +/* +Copyright 2021 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 injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + fake "knative.dev/eventing/pkg/client/injection/informers/factory/fake" + integrationsource "knative.dev/eventing/pkg/client/injection/informers/sources/v1alpha1/integrationsource" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" +) + +var Get = integrationsource.Get + +func init() { + injection.Fake.RegisterInformer(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := fake.Get(ctx) + inf := f.Sources().V1alpha1().IntegrationSources() + return context.WithValue(ctx, integrationsource.Key{}, inf), inf.Informer() +} diff --git a/pkg/client/injection/informers/sources/v1alpha1/integrationsource/filtered/fake/fake.go b/pkg/client/injection/informers/sources/v1alpha1/integrationsource/filtered/fake/fake.go new file mode 100644 index 00000000000..8977c76fbf0 --- /dev/null +++ b/pkg/client/injection/informers/sources/v1alpha1/integrationsource/filtered/fake/fake.go @@ -0,0 +1,52 @@ +/* +Copyright 2021 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 injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + factoryfiltered "knative.dev/eventing/pkg/client/injection/informers/factory/filtered" + filtered "knative.dev/eventing/pkg/client/injection/informers/sources/v1alpha1/integrationsource/filtered" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +var Get = filtered.Get + +func init() { + injection.Fake.RegisterFilteredInformers(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, []controller.Informer) { + untyped := ctx.Value(factoryfiltered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + infs := []controller.Informer{} + for _, selector := range labelSelectors { + f := factoryfiltered.Get(ctx, selector) + inf := f.Sources().V1alpha1().IntegrationSources() + ctx = context.WithValue(ctx, filtered.Key{Selector: selector}, inf) + infs = append(infs, inf.Informer()) + } + return ctx, infs +} diff --git a/pkg/client/injection/informers/sources/v1alpha1/integrationsource/filtered/integrationsource.go b/pkg/client/injection/informers/sources/v1alpha1/integrationsource/filtered/integrationsource.go new file mode 100644 index 00000000000..d21cf4ab247 --- /dev/null +++ b/pkg/client/injection/informers/sources/v1alpha1/integrationsource/filtered/integrationsource.go @@ -0,0 +1,65 @@ +/* +Copyright 2021 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 injection-gen. DO NOT EDIT. + +package filtered + +import ( + context "context" + + v1alpha1 "knative.dev/eventing/pkg/client/informers/externalversions/sources/v1alpha1" + filtered "knative.dev/eventing/pkg/client/injection/informers/factory/filtered" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterFilteredInformers(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct { + Selector string +} + +func withInformer(ctx context.Context) (context.Context, []controller.Informer) { + untyped := ctx.Value(filtered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + infs := []controller.Informer{} + for _, selector := range labelSelectors { + f := filtered.Get(ctx, selector) + inf := f.Sources().V1alpha1().IntegrationSources() + ctx = context.WithValue(ctx, Key{Selector: selector}, inf) + infs = append(infs, inf.Informer()) + } + return ctx, infs +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context, selector string) v1alpha1.IntegrationSourceInformer { + untyped := ctx.Value(Key{Selector: selector}) + if untyped == nil { + logging.FromContext(ctx).Panicf( + "Unable to fetch knative.dev/eventing/pkg/client/informers/externalversions/sources/v1alpha1.IntegrationSourceInformer with selector %s from context.", selector) + } + return untyped.(v1alpha1.IntegrationSourceInformer) +} diff --git a/pkg/client/injection/informers/sources/v1alpha1/integrationsource/integrationsource.go b/pkg/client/injection/informers/sources/v1alpha1/integrationsource/integrationsource.go new file mode 100644 index 00000000000..221f33b8e65 --- /dev/null +++ b/pkg/client/injection/informers/sources/v1alpha1/integrationsource/integrationsource.go @@ -0,0 +1,52 @@ +/* +Copyright 2021 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 injection-gen. DO NOT EDIT. + +package integrationsource + +import ( + context "context" + + v1alpha1 "knative.dev/eventing/pkg/client/informers/externalversions/sources/v1alpha1" + factory "knative.dev/eventing/pkg/client/injection/informers/factory" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterInformer(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct{} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := factory.Get(ctx) + inf := f.Sources().V1alpha1().IntegrationSources() + return context.WithValue(ctx, Key{}, inf), inf.Informer() +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context) v1alpha1.IntegrationSourceInformer { + untyped := ctx.Value(Key{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch knative.dev/eventing/pkg/client/informers/externalversions/sources/v1alpha1.IntegrationSourceInformer from context.") + } + return untyped.(v1alpha1.IntegrationSourceInformer) +} diff --git a/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource/controller.go b/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource/controller.go new file mode 100644 index 00000000000..1338a059205 --- /dev/null +++ b/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource/controller.go @@ -0,0 +1,170 @@ +/* +Copyright 2021 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 injection-gen. DO NOT EDIT. + +package integrationsource + +import ( + context "context" + fmt "fmt" + reflect "reflect" + strings "strings" + + zap "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + scheme "k8s.io/client-go/kubernetes/scheme" + v1 "k8s.io/client-go/kubernetes/typed/core/v1" + record "k8s.io/client-go/tools/record" + versionedscheme "knative.dev/eventing/pkg/client/clientset/versioned/scheme" + client "knative.dev/eventing/pkg/client/injection/client" + integrationsource "knative.dev/eventing/pkg/client/injection/informers/sources/v1alpha1/integrationsource" + kubeclient "knative.dev/pkg/client/injection/kube/client" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" + logkey "knative.dev/pkg/logging/logkey" + reconciler "knative.dev/pkg/reconciler" +) + +const ( + defaultControllerAgentName = "integrationsource-controller" + defaultFinalizerName = "integrationsources.sources.knative.dev" +) + +// NewImpl returns a controller.Impl that handles queuing and feeding work from +// the queue through an implementation of controller.Reconciler, delegating to +// the provided Interface and optional Finalizer methods. OptionsFn is used to return +// controller.ControllerOptions to be used by the internal reconciler. +func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsFn) *controller.Impl { + logger := logging.FromContext(ctx) + + // Check the options function input. It should be 0 or 1. + if len(optionsFns) > 1 { + logger.Fatal("Up to one options function is supported, found: ", len(optionsFns)) + } + + integrationsourceInformer := integrationsource.Get(ctx) + + lister := integrationsourceInformer.Lister() + + var promoteFilterFunc func(obj interface{}) bool + var promoteFunc = func(bkt reconciler.Bucket) {} + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + + // Signal promotion event + promoteFunc(bkt) + + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + if promoteFilterFunc != nil { + if ok := promoteFilterFunc(elt); !ok { + continue + } + } + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client.Get(ctx), + Lister: lister, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + ctrType := reflect.TypeOf(r).Elem() + ctrTypeName := fmt.Sprintf("%s.%s", ctrType.PkgPath(), ctrType.Name()) + ctrTypeName = strings.ReplaceAll(ctrTypeName, "/", ".") + + logger = logger.With( + zap.String(logkey.ControllerType, ctrTypeName), + zap.String(logkey.Kind, "sources.knative.dev.IntegrationSource"), + ) + + impl := controller.NewContext(ctx, rec, controller.ControllerOptions{WorkQueueName: ctrTypeName, Logger: logger}) + agentName := defaultControllerAgentName + + // Pass impl to the options. Save any optional results. + for _, fn := range optionsFns { + opts := fn(impl) + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + if opts.AgentName != "" { + agentName = opts.AgentName + } + if opts.SkipStatusUpdates { + rec.skipStatusUpdates = true + } + if opts.DemoteFunc != nil { + rec.DemoteFunc = opts.DemoteFunc + } + if opts.PromoteFilterFunc != nil { + promoteFilterFunc = opts.PromoteFilterFunc + } + if opts.PromoteFunc != nil { + promoteFunc = opts.PromoteFunc + } + } + + rec.Recorder = createRecorder(ctx, agentName) + + return impl +} + +func createRecorder(ctx context.Context, agentName string) record.EventRecorder { + logger := logging.FromContext(ctx) + + recorder := controller.GetEventRecorder(ctx) + if recorder == nil { + // Create event broadcaster + logger.Debug("Creating event broadcaster") + eventBroadcaster := record.NewBroadcaster() + watches := []watch.Interface{ + eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof), + eventBroadcaster.StartRecordingToSink( + &v1.EventSinkImpl{Interface: kubeclient.Get(ctx).CoreV1().Events("")}), + } + recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName}) + go func() { + <-ctx.Done() + for _, w := range watches { + w.Stop() + } + }() + } + + return recorder +} + +func init() { + versionedscheme.AddToScheme(scheme.Scheme) +} diff --git a/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource/reconciler.go b/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource/reconciler.go new file mode 100644 index 00000000000..0de10c6cf8c --- /dev/null +++ b/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource/reconciler.go @@ -0,0 +1,440 @@ +/* +Copyright 2021 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 injection-gen. DO NOT EDIT. + +package integrationsource + +import ( + context "context" + json "encoding/json" + fmt "fmt" + + zap "go.uber.org/zap" + "go.uber.org/zap/zapcore" + v1 "k8s.io/api/core/v1" + equality "k8s.io/apimachinery/pkg/api/equality" + errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + sets "k8s.io/apimachinery/pkg/util/sets" + record "k8s.io/client-go/tools/record" + v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" + versioned "knative.dev/eventing/pkg/client/clientset/versioned" + sourcesv1alpha1 "knative.dev/eventing/pkg/client/listers/sources/v1alpha1" + controller "knative.dev/pkg/controller" + kmp "knative.dev/pkg/kmp" + logging "knative.dev/pkg/logging" + reconciler "knative.dev/pkg/reconciler" +) + +// Interface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1alpha1.IntegrationSource. +type Interface interface { + // ReconcileKind implements custom logic to reconcile v1alpha1.IntegrationSource. Any changes + // to the objects .Status or .Finalizers will be propagated to the stored + // object. It is recommended that implementors do not call any update calls + // for the Kind inside of ReconcileKind, it is the responsibility of the calling + // controller to propagate those properties. The resource passed to ReconcileKind + // will always have an empty deletion timestamp. + ReconcileKind(ctx context.Context, o *v1alpha1.IntegrationSource) reconciler.Event +} + +// Finalizer defines the strongly typed interfaces to be implemented by a +// controller finalizing v1alpha1.IntegrationSource. +type Finalizer interface { + // FinalizeKind implements custom logic to finalize v1alpha1.IntegrationSource. Any changes + // to the objects .Status or .Finalizers will be ignored. Returning a nil or + // Normal type reconciler.Event will allow the finalizer to be deleted on + // the resource. The resource passed to FinalizeKind will always have a set + // deletion timestamp. + FinalizeKind(ctx context.Context, o *v1alpha1.IntegrationSource) reconciler.Event +} + +// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1alpha1.IntegrationSource if they want to process resources for which +// they are not the leader. +type ReadOnlyInterface interface { + // ObserveKind implements logic to observe v1alpha1.IntegrationSource. + // This method should not write to the API. + ObserveKind(ctx context.Context, o *v1alpha1.IntegrationSource) reconciler.Event +} + +type doReconcile func(ctx context.Context, o *v1alpha1.IntegrationSource) reconciler.Event + +// reconcilerImpl implements controller.Reconciler for v1alpha1.IntegrationSource resources. +type reconcilerImpl struct { + // LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware. + reconciler.LeaderAwareFuncs + + // Client is used to write back status updates. + Client versioned.Interface + + // Listers index properties about resources. + Lister sourcesv1alpha1.IntegrationSourceLister + + // Recorder is an event recorder for recording Event resources to the + // Kubernetes API. + Recorder record.EventRecorder + + // configStore allows for decorating a context with config maps. + // +optional + configStore reconciler.ConfigStore + + // reconciler is the implementation of the business logic of the resource. + reconciler Interface + + // finalizerName is the name of the finalizer to reconcile. + finalizerName string + + // skipStatusUpdates configures whether or not this reconciler automatically updates + // the status of the reconciled resource. + skipStatusUpdates bool +} + +// Check that our Reconciler implements controller.Reconciler. +var _ controller.Reconciler = (*reconcilerImpl)(nil) + +// Check that our generated Reconciler is always LeaderAware. +var _ reconciler.LeaderAware = (*reconcilerImpl)(nil) + +func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versioned.Interface, lister sourcesv1alpha1.IntegrationSourceLister, recorder record.EventRecorder, r Interface, options ...controller.Options) controller.Reconciler { + // Check the options function input. It should be 0 or 1. + if len(options) > 1 { + logger.Fatal("Up to one options struct is supported, found: ", len(options)) + } + + // Fail fast when users inadvertently implement the other LeaderAware interface. + // For the typed reconcilers, Promote shouldn't take any arguments. + if _, ok := r.(reconciler.LeaderAware); ok { + logger.Fatalf("%T implements the incorrect LeaderAware interface. Promote() should not take an argument as genreconciler handles the enqueuing automatically.", r) + } + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + // TODO: Consider letting users specify a filter in options. + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client, + Lister: lister, + Recorder: recorder, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + for _, opts := range options { + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + if opts.SkipStatusUpdates { + rec.skipStatusUpdates = true + } + if opts.DemoteFunc != nil { + rec.DemoteFunc = opts.DemoteFunc + } + } + + return rec +} + +// Reconcile implements controller.Reconciler +func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error { + logger := logging.FromContext(ctx) + + // Initialize the reconciler state. This will convert the namespace/name + // string into a distinct namespace and name, determine if this instance of + // the reconciler is the leader, and any additional interfaces implemented + // by the reconciler. Returns an error is the resource key is invalid. + s, err := newState(key, r) + if err != nil { + logger.Error("Invalid resource key: ", key) + return nil + } + + // If we are not the leader, and we don't implement either ReadOnly + // observer interfaces, then take a fast-path out. + if s.isNotLeaderNorObserver() { + return controller.NewSkipKey(key) + } + + // If configStore is set, attach the frozen configuration to the context. + if r.configStore != nil { + ctx = r.configStore.ToContext(ctx) + } + + // Add the recorder to context. + ctx = controller.WithEventRecorder(ctx, r.Recorder) + + // Get the resource with this namespace/name. + + getter := r.Lister.IntegrationSources(s.namespace) + + original, err := getter.Get(s.name) + + if errors.IsNotFound(err) { + // The resource may no longer exist, in which case we stop processing and call + // the ObserveDeletion handler if appropriate. + logger.Debugf("Resource %q no longer exists", key) + if del, ok := r.reconciler.(reconciler.OnDeletionInterface); ok { + return del.ObserveDeletion(ctx, types.NamespacedName{ + Namespace: s.namespace, + Name: s.name, + }) + } + return nil + } else if err != nil { + return err + } + + // Don't modify the informers copy. + resource := original.DeepCopy() + + var reconcileEvent reconciler.Event + + name, do := s.reconcileMethodFor(resource) + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", name)) + switch name { + case reconciler.DoReconcileKind: + // Set and update the finalizer on resource if r.reconciler + // implements Finalizer. + if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil { + return fmt.Errorf("failed to set finalizers: %w", err) + } + + if !r.skipStatusUpdates { + reconciler.PreProcessReconcile(ctx, resource) + } + + // Reconcile this copy of the resource and then write back any status + // updates regardless of whether the reconciliation errored out. + reconcileEvent = do(ctx, resource) + + if !r.skipStatusUpdates { + reconciler.PostProcessReconcile(ctx, resource, original) + } + + case reconciler.DoFinalizeKind: + // For finalizing reconcilers, if this resource being marked for deletion + // and reconciled cleanly (nil or normal event), remove the finalizer. + reconcileEvent = do(ctx, resource) + + if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil { + return fmt.Errorf("failed to clear finalizers: %w", err) + } + + case reconciler.DoObserveKind: + // Observe any changes to this resource, since we are not the leader. + reconcileEvent = do(ctx, resource) + + } + + // Synchronize the status. + switch { + case r.skipStatusUpdates: + // This reconciler implementation is configured to skip resource updates. + // This may mean this reconciler does not observe spec, but reconciles external changes. + case equality.Semantic.DeepEqual(original.Status, resource.Status): + // If we didn't change anything then don't call updateStatus. + // This is important because the copy we loaded from the injectionInformer's + // cache may be stale and we don't want to overwrite a prior update + // to status with this stale state. + case !s.isLeader: + // High-availability reconcilers may have many replicas watching the resource, but only + // the elected leader is expected to write modifications. + logger.Warn("Saw status changes when we aren't the leader!") + default: + if err = r.updateStatus(ctx, logger, original, resource); err != nil { + logger.Warnw("Failed to update resource status", zap.Error(err)) + r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed", + "Failed to update status for %q: %v", resource.Name, err) + return err + } + } + + // Report the reconciler event, if any. + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + logger.Infow("Returned an event", zap.Any("event", reconcileEvent)) + r.Recorder.Event(resource, event.EventType, event.Reason, event.Error()) + + // the event was wrapped inside an error, consider the reconciliation as failed + if _, isEvent := reconcileEvent.(*reconciler.ReconcilerEvent); !isEvent { + return reconcileEvent + } + return nil + } + + if controller.IsSkipKey(reconcileEvent) { + // This is a wrapped error, don't emit an event. + } else if ok, _ := controller.IsRequeueKey(reconcileEvent); ok { + // This is a wrapped error, don't emit an event. + } else { + logger.Errorw("Returned an error", zap.Error(reconcileEvent)) + r.Recorder.Event(resource, v1.EventTypeWarning, "InternalError", reconcileEvent.Error()) + } + return reconcileEvent + } + + return nil +} + +func (r *reconcilerImpl) updateStatus(ctx context.Context, logger *zap.SugaredLogger, existing *v1alpha1.IntegrationSource, desired *v1alpha1.IntegrationSource) error { + existing = existing.DeepCopy() + return reconciler.RetryUpdateConflicts(func(attempts int) (err error) { + // The first iteration tries to use the injectionInformer's state, subsequent attempts fetch the latest state via API. + if attempts > 0 { + + getter := r.Client.SourcesV1alpha1().IntegrationSources(desired.Namespace) + + existing, err = getter.Get(ctx, desired.Name, metav1.GetOptions{}) + if err != nil { + return err + } + } + + // If there's nothing to update, just return. + if equality.Semantic.DeepEqual(existing.Status, desired.Status) { + return nil + } + + if logger.Desugar().Core().Enabled(zapcore.DebugLevel) { + if diff, err := kmp.SafeDiff(existing.Status, desired.Status); err == nil && diff != "" { + logger.Debug("Updating status with: ", diff) + } + } + + existing.Status = desired.Status + + updater := r.Client.SourcesV1alpha1().IntegrationSources(existing.Namespace) + + _, err = updater.UpdateStatus(ctx, existing, metav1.UpdateOptions{}) + return err + }) +} + +// updateFinalizersFiltered will update the Finalizers of the resource. +// TODO: this method could be generic and sync all finalizers. For now it only +// updates defaultFinalizerName or its override. +func (r *reconcilerImpl) updateFinalizersFiltered(ctx context.Context, resource *v1alpha1.IntegrationSource, desiredFinalizers sets.Set[string]) (*v1alpha1.IntegrationSource, error) { + // Don't modify the informers copy. + existing := resource.DeepCopy() + + var finalizers []string + + // If there's nothing to update, just return. + existingFinalizers := sets.New[string](existing.Finalizers...) + + if desiredFinalizers.Has(r.finalizerName) { + if existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Add the finalizer. + finalizers = append(existing.Finalizers, r.finalizerName) + } else { + if !existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Remove the finalizer. + existingFinalizers.Delete(r.finalizerName) + finalizers = sets.List(existingFinalizers) + } + + mergePatch := map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": finalizers, + "resourceVersion": existing.ResourceVersion, + }, + } + + patch, err := json.Marshal(mergePatch) + if err != nil { + return resource, err + } + + patcher := r.Client.SourcesV1alpha1().IntegrationSources(resource.Namespace) + + resourceName := resource.Name + updated, err := patcher.Patch(ctx, resourceName, types.MergePatchType, patch, metav1.PatchOptions{}) + if err != nil { + r.Recorder.Eventf(existing, v1.EventTypeWarning, "FinalizerUpdateFailed", + "Failed to update finalizers for %q: %v", resourceName, err) + } else { + r.Recorder.Eventf(updated, v1.EventTypeNormal, "FinalizerUpdate", + "Updated %q finalizers", resource.GetName()) + } + return updated, err +} + +func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1alpha1.IntegrationSource) (*v1alpha1.IntegrationSource, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + + finalizers := sets.New[string](resource.Finalizers...) + + // If this resource is not being deleted, mark the finalizer. + if resource.GetDeletionTimestamp().IsZero() { + finalizers.Insert(r.finalizerName) + } + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource, finalizers) +} + +func (r *reconcilerImpl) clearFinalizer(ctx context.Context, resource *v1alpha1.IntegrationSource, reconcileEvent reconciler.Event) (*v1alpha1.IntegrationSource, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + if resource.GetDeletionTimestamp().IsZero() { + return resource, nil + } + + finalizers := sets.New[string](resource.Finalizers...) + + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + if event.EventType == v1.EventTypeNormal { + finalizers.Delete(r.finalizerName) + } + } + } else { + finalizers.Delete(r.finalizerName) + } + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource, finalizers) +} diff --git a/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource/state.go b/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource/state.go new file mode 100644 index 00000000000..501667a6e70 --- /dev/null +++ b/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource/state.go @@ -0,0 +1,97 @@ +/* +Copyright 2021 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 injection-gen. DO NOT EDIT. + +package integrationsource + +import ( + fmt "fmt" + + types "k8s.io/apimachinery/pkg/types" + cache "k8s.io/client-go/tools/cache" + v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" + reconciler "knative.dev/pkg/reconciler" +) + +// state is used to track the state of a reconciler in a single run. +type state struct { + // key is the original reconciliation key from the queue. + key string + // namespace is the namespace split from the reconciliation key. + namespace string + // name is the name split from the reconciliation key. + name string + // reconciler is the reconciler. + reconciler Interface + // roi is the read only interface cast of the reconciler. + roi ReadOnlyInterface + // isROI (Read Only Interface) the reconciler only observes reconciliation. + isROI bool + // isLeader the instance of the reconciler is the elected leader. + isLeader bool +} + +func newState(key string, r *reconcilerImpl) (*state, error) { + // Convert the namespace/name string into a distinct namespace and name. + namespace, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + return nil, fmt.Errorf("invalid resource key: %s", key) + } + + roi, isROI := r.reconciler.(ReadOnlyInterface) + + isLeader := r.IsLeaderFor(types.NamespacedName{ + Namespace: namespace, + Name: name, + }) + + return &state{ + key: key, + namespace: namespace, + name: name, + reconciler: r.reconciler, + roi: roi, + isROI: isROI, + isLeader: isLeader, + }, nil +} + +// isNotLeaderNorObserver checks to see if this reconciler with the current +// state is enabled to do any work or not. +// isNotLeaderNorObserver returns true when there is no work possible for the +// reconciler. +func (s *state) isNotLeaderNorObserver() bool { + if !s.isLeader && !s.isROI { + // If we are not the leader, and we don't implement the ReadOnly + // interface, then take a fast-path out. + return true + } + return false +} + +func (s *state) reconcileMethodFor(o *v1alpha1.IntegrationSource) (string, doReconcile) { + if o.GetDeletionTimestamp().IsZero() { + if s.isLeader { + return reconciler.DoReconcileKind, s.reconciler.ReconcileKind + } else if s.isROI { + return reconciler.DoObserveKind, s.roi.ObserveKind + } + } else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok { + return reconciler.DoFinalizeKind, fin.FinalizeKind + } + return "unknown", nil +} diff --git a/pkg/client/listers/sources/v1alpha1/expansion_generated.go b/pkg/client/listers/sources/v1alpha1/expansion_generated.go new file mode 100644 index 00000000000..16600e2ab8b --- /dev/null +++ b/pkg/client/listers/sources/v1alpha1/expansion_generated.go @@ -0,0 +1,27 @@ +/* +Copyright 2021 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 + +// IntegrationSourceListerExpansion allows custom methods to be added to +// IntegrationSourceLister. +type IntegrationSourceListerExpansion interface{} + +// IntegrationSourceNamespaceListerExpansion allows custom methods to be added to +// IntegrationSourceNamespaceLister. +type IntegrationSourceNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/sources/v1alpha1/integrationsource.go b/pkg/client/listers/sources/v1alpha1/integrationsource.go new file mode 100644 index 00000000000..a9d3d1a5292 --- /dev/null +++ b/pkg/client/listers/sources/v1alpha1/integrationsource.go @@ -0,0 +1,99 @@ +/* +Copyright 2021 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 ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" +) + +// IntegrationSourceLister helps list IntegrationSources. +// All objects returned here must be treated as read-only. +type IntegrationSourceLister interface { + // List lists all IntegrationSources in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.IntegrationSource, err error) + // IntegrationSources returns an object that can list and get IntegrationSources. + IntegrationSources(namespace string) IntegrationSourceNamespaceLister + IntegrationSourceListerExpansion +} + +// integrationSourceLister implements the IntegrationSourceLister interface. +type integrationSourceLister struct { + indexer cache.Indexer +} + +// NewIntegrationSourceLister returns a new IntegrationSourceLister. +func NewIntegrationSourceLister(indexer cache.Indexer) IntegrationSourceLister { + return &integrationSourceLister{indexer: indexer} +} + +// List lists all IntegrationSources in the indexer. +func (s *integrationSourceLister) List(selector labels.Selector) (ret []*v1alpha1.IntegrationSource, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.IntegrationSource)) + }) + return ret, err +} + +// IntegrationSources returns an object that can list and get IntegrationSources. +func (s *integrationSourceLister) IntegrationSources(namespace string) IntegrationSourceNamespaceLister { + return integrationSourceNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// IntegrationSourceNamespaceLister helps list and get IntegrationSources. +// All objects returned here must be treated as read-only. +type IntegrationSourceNamespaceLister interface { + // List lists all IntegrationSources in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.IntegrationSource, err error) + // Get retrieves the IntegrationSource from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.IntegrationSource, error) + IntegrationSourceNamespaceListerExpansion +} + +// integrationSourceNamespaceLister implements the IntegrationSourceNamespaceLister +// interface. +type integrationSourceNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all IntegrationSources in the indexer for a given namespace. +func (s integrationSourceNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.IntegrationSource, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.IntegrationSource)) + }) + return ret, err +} + +// Get retrieves the IntegrationSource from the indexer for a given namespace and name. +func (s integrationSourceNamespaceLister) Get(name string) (*v1alpha1.IntegrationSource, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("integrationsource"), name) + } + return obj.(*v1alpha1.IntegrationSource), nil +} diff --git a/pkg/reconciler/containersource/containersource_test.go b/pkg/reconciler/containersource/containersource_test.go index 6c588a59b87..b5b223c1344 100644 --- a/pkg/reconciler/containersource/containersource_test.go +++ b/pkg/reconciler/containersource/containersource_test.go @@ -216,7 +216,7 @@ func TestAllCases(t *testing.T) { }, Key: testNS + "/" + sourceName, WantEvents: []string{ - Eventf(corev1.EventTypeNormal, sourceReconciled, `ContainerSource reconciled: "%s/%s"`, testNS, sourceName), + Eventf(corev1.EventTypeNormal, "ContainerSourceReconciled", `ContainerSource reconciled: "%s/%s"`, testNS, sourceName), }, WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ Object: NewContainerSource(sourceName, testNS, diff --git a/pkg/reconciler/integrationsource/controller.go b/pkg/reconciler/integrationsource/controller.go new file mode 100644 index 00000000000..a84bec85815 --- /dev/null +++ b/pkg/reconciler/integrationsource/controller.go @@ -0,0 +1,111 @@ +/* +Copyright 2024 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 integrationsource + +import ( + "context" + + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/cache" + containersourceinformer "knative.dev/eventing/pkg/client/injection/informers/sources/v1/containersource" + + configmapinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/configmap/filtered" + "knative.dev/pkg/kmeta" + "knative.dev/pkg/system" + + kubeclient "knative.dev/pkg/client/injection/kube/client" + "knative.dev/pkg/configmap" + "knative.dev/pkg/controller" + "knative.dev/pkg/logging" + + "knative.dev/eventing/pkg/apis/feature" + "knative.dev/eventing/pkg/apis/sources/v1alpha1" + eventingclient "knative.dev/eventing/pkg/client/injection/client" + integrationsourceinformer "knative.dev/eventing/pkg/client/injection/informers/sources/v1alpha1/integrationsource" + v1integrationsource "knative.dev/eventing/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource" + "knative.dev/eventing/pkg/eventingtls" +) + +// NewController creates a Reconciler for IntegrationSource and returns the result of NewImpl. +func NewController( + ctx context.Context, + cmw configmap.Watcher, +) *controller.Impl { + + kubeClient := kubeclient.Get(ctx) + eventingClient := eventingclient.Get(ctx) + integrationsourceInformer := integrationsourceinformer.Get(ctx) + containerSourceInformer := containersourceinformer.Get(ctx) + + trustBundleConfigMapInformer := configmapinformer.Get(ctx, eventingtls.TrustBundleLabelSelector) + + var globalResync func(obj interface{}) + featureStore := feature.NewStore(logging.FromContext(ctx).Named("feature-config-store"), + func(name string, value interface{}) { + if globalResync != nil { + globalResync(nil) + } + }) + featureStore.WatchConfigs(cmw) + + r := &Reconciler{ + kubeClientSet: kubeClient, + eventingClientSet: eventingClient, + containerSourceLister: containerSourceInformer.Lister(), + integrationSourceLister: integrationsourceInformer.Lister(), + } + + impl := v1integrationsource.NewImpl(ctx, r, func(impl *controller.Impl) controller.Options { + return controller.Options{ConfigStore: featureStore} + }) + + globalResync = func(_ interface{}) { + impl.GlobalResync(integrationsourceInformer.Informer()) + } + + integrationsourceInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) + + containerSourceInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ + FilterFunc: controller.FilterController(&v1alpha1.IntegrationSource{}), + Handler: controller.HandleAll(impl.EnqueueControllerOf), + }) + + trustBundleConfigMapInformer.Informer().AddEventHandler(controller.HandleAll(func(i interface{}) { + obj, err := kmeta.DeletionHandlingAccessor(i) + if err != nil { + return + } + if obj.GetNamespace() == system.Namespace() { + globalResync(i) + return + } + + sources, err := integrationsourceInformer.Lister().IntegrationSources(obj.GetNamespace()).List(labels.Everything()) + if err != nil { + return + } + for _, src := range sources { + impl.EnqueueKey(types.NamespacedName{ + Namespace: src.Namespace, + Name: src.Name, + }) + } + })) + + return impl +} diff --git a/pkg/reconciler/integrationsource/controller_test.go b/pkg/reconciler/integrationsource/controller_test.go new file mode 100644 index 00000000000..e4c21f26565 --- /dev/null +++ b/pkg/reconciler/integrationsource/controller_test.go @@ -0,0 +1,60 @@ +/* +Copyright 2024 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 integrationsource + +import ( + "context" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + filteredFactory "knative.dev/pkg/client/injection/kube/informers/factory/filtered" + "knative.dev/pkg/configmap" + . "knative.dev/pkg/reconciler/testing" + + // Fake injection informers + "knative.dev/eventing/pkg/apis/feature" + _ "knative.dev/eventing/pkg/client/injection/informers/sources/v1/containersource/fake" + _ "knative.dev/eventing/pkg/client/injection/informers/sources/v1alpha1/integrationsource/fake" + "knative.dev/eventing/pkg/eventingtls" + _ "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment/fake" + _ "knative.dev/pkg/client/injection/kube/informers/core/v1/configmap/filtered/fake" + _ "knative.dev/pkg/client/injection/kube/informers/core/v1/serviceaccount/filtered/fake" + _ "knative.dev/pkg/client/injection/kube/informers/factory/filtered/fake" + _ "knative.dev/pkg/injection/clients/dynamicclient/fake" +) + +func TestNew(t *testing.T) { + ctx, _ := SetupFakeContext(t, SetUpInformerSelector) + + c := NewController(ctx, configmap.NewStaticWatcher( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: feature.FlagsConfigName, + }, + }, + )) + + if c == nil { + t.Fatal("Expected NewController to return a non-nil value") + } +} + +func SetUpInformerSelector(ctx context.Context) context.Context { + ctx = filteredFactory.WithSelectors(ctx, eventingtls.TrustBundleLabelSelector) + return ctx +} diff --git a/pkg/reconciler/integrationsource/integrationsource.go b/pkg/reconciler/integrationsource/integrationsource.go new file mode 100644 index 00000000000..1022b66172b --- /dev/null +++ b/pkg/reconciler/integrationsource/integrationsource.go @@ -0,0 +1,92 @@ +package integrationsource + +import ( + "context" + "fmt" + + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + v1 "knative.dev/eventing/pkg/apis/sources/v1" + "knative.dev/eventing/pkg/apis/sources/v1alpha1" + clientset "knative.dev/eventing/pkg/client/clientset/versioned" + "knative.dev/eventing/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource" + v1listers "knative.dev/eventing/pkg/client/listers/sources/v1" + listers "knative.dev/eventing/pkg/client/listers/sources/v1alpha1" + "knative.dev/eventing/pkg/reconciler/integrationsource/resources" + "knative.dev/pkg/controller" + "knative.dev/pkg/logging" + pkgreconciler "knative.dev/pkg/reconciler" +) + +const ( + // Name of the corev1.Events emitted from the reconciliation process + sourceReconciled = "IntegrationSourceReconciled" + containerSourceCreated = "ContainerSourceCreated" + containerSourceUpdated = "ContainerSourceUpdated" +) + +// Reconciler implements controller.Reconciler for ContainerSource resources. +type Reconciler struct { + kubeClientSet kubernetes.Interface + eventingClientSet clientset.Interface + + containerSourceLister v1listers.ContainerSourceLister + integrationSourceLister listers.IntegrationSourceLister +} + +// Check that our Reconciler implements Interface +var _ integrationsource.Interface = (*Reconciler)(nil) + +// newReconciledNormal makes a new reconciler event with event type Normal, and +// reason ContainerSourceReconciled. +func newReconciledNormal(namespace, name string) pkgreconciler.Event { + return pkgreconciler.NewEvent(corev1.EventTypeNormal, sourceReconciled, "IntegrationSource reconciled: \"%s/%s\"", namespace, name) +} + +func (r *Reconciler) ReconcileKind(ctx context.Context, source *v1alpha1.IntegrationSource) pkgreconciler.Event { + + _, err := r.reconcileContainerSource(ctx, source) + if err != nil { + logging.FromContext(ctx).Errorw("Error reconciling ContainerSource", zap.Error(err)) + return err + } + + return newReconciledNormal(source.Namespace, source.Name) +} + +func (r *Reconciler) reconcileContainerSource(ctx context.Context, source *v1alpha1.IntegrationSource) (*v1.ContainerSource, error) { + expected := resources.NewContainerSource(source) + + cs, err := r.containerSourceLister.ContainerSources(source.Namespace).Get(expected.Name) + if apierrors.IsNotFound(err) { + cs, err = r.eventingClientSet.SourcesV1().ContainerSources(source.Namespace).Create(ctx, expected, metav1.CreateOptions{}) + if err != nil { + return nil, fmt.Errorf("creating new ContainerSource: %v", err) + } + controller.GetEventRecorder(ctx).Eventf(source, corev1.EventTypeNormal, containerSourceCreated, "ContainerSource created %q", cs.Name) + } else if err != nil { + return nil, fmt.Errorf("getting ContainerSource: %v", err) + } else if !metav1.IsControlledBy(cs, source) { + return nil, fmt.Errorf("ContainerSource %q is not owned by IntegrationSource %q", cs.Name, source.Name) + } else if r.containerSourceSpecChanged(&cs.Spec, &expected.Spec) { + cs.Spec = expected.Spec + cs, err = r.eventingClientSet.SourcesV1().ContainerSources(source.Namespace).Update(ctx, cs, metav1.UpdateOptions{}) + if err != nil { + return nil, fmt.Errorf("updating ContainerSource: %v", err) + } + controller.GetEventRecorder(ctx).Eventf(source, corev1.EventTypeNormal, containerSourceUpdated, "ContainerSource updated %q", cs.Name) + } else { + logging.FromContext(ctx).Debugw("Reusing existing ContainerSource", zap.Any("ContainerSource", cs.ObjectMeta)) + } + + source.Status.PropagateContainerSourceStatus(&cs.Status) + return cs, nil +} + +func (r *Reconciler) containerSourceSpecChanged(have *v1.ContainerSourceSpec, want *v1.ContainerSourceSpec) bool { + return !equality.Semantic.DeepDerivative(want, have) +} diff --git a/pkg/reconciler/integrationsource/integrationsource_test.go b/pkg/reconciler/integrationsource/integrationsource_test.go new file mode 100644 index 00000000000..92dc884bfcf --- /dev/null +++ b/pkg/reconciler/integrationsource/integrationsource_test.go @@ -0,0 +1,258 @@ +/* +Copyright 2024 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 integrationsource + +import ( + "fmt" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + clientgotesting "k8s.io/client-go/testing" + sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" + sourcesv1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" + fakeeventingclient "knative.dev/eventing/pkg/client/injection/client/fake" + "knative.dev/eventing/pkg/client/injection/reconciler/sources/v1alpha1/integrationsource" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + fakekubeclient "knative.dev/pkg/client/injection/kube/client/fake" + "knative.dev/pkg/kmeta" + "knative.dev/pkg/logging" + + "context" + + . "knative.dev/eventing/pkg/reconciler/testing/v1" + . "knative.dev/eventing/pkg/reconciler/testing/v1alpha1" + "knative.dev/pkg/client/injection/ducks/duck/v1/addressable" + "knative.dev/pkg/configmap" + "knative.dev/pkg/controller" + logtesting "knative.dev/pkg/logging/testing" + . "knative.dev/pkg/reconciler/testing" + + "testing" +) + +const ( + sourceName = "test-integration-source" + sourceUID = "1234-5678-90" + testNS = "testnamespace" + sinkName = "testsink" + generation = 1 +) + +var ( + conditionTrue = corev1.ConditionTrue + + containerSourceName = fmt.Sprintf("%s-containersource", sourceName) + + sinkDest = duckv1.Destination{ + Ref: &duckv1.KReference{ + Name: sinkName, + Kind: "Channel", + APIVersion: "messaging.knative.dev/v1", + }, + } +) + +func TestReconcile(t *testing.T) { + + table := TableTest{ + { + Name: "bad work queue key", + Key: "too/many/parts", + }, + { + Name: "key not found", + // Make sure Reconcile handles good keys that don't exist. + Key: "foo/not-found", + }, { + Name: "error creating containersource", + Objects: []runtime.Object{ + NewIntegrationSource(sourceName, testNS, + WithIntegrationSourceUID(sourceUID), + WithIntegrationSourceSpec(makeIntegrationSourceSpec(sinkDest)), + ), + }, + Key: testNS + "/" + sourceName, + WithReactors: []clientgotesting.ReactionFunc{ + InduceFailure("create", "containersources"), + }, + WantEvents: []string{ + Eventf(corev1.EventTypeWarning, "InternalError", "creating new ContainerSource: inducing failure for %s %s", "create", "containersources"), + }, + WantErr: true, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: NewIntegrationSource(sourceName, testNS, + WithIntegrationSourceUID(sourceUID), + WithIntegrationSourceSpec(makeIntegrationSourceSpec(sinkDest)), + WithInitIntegrationSourceConditions, + ), + }}, + WantCreates: []runtime.Object{ + makeContainerSource(NewIntegrationSource(sourceName, testNS, + WithIntegrationSourceUID(sourceUID), + WithIntegrationSourceSpec(makeIntegrationSourceSpec(sinkDest))), + nil), + }, + }, { + Name: "successfully reconciled and not ready", + Objects: []runtime.Object{ + NewIntegrationSource(sourceName, testNS, + WithIntegrationSourceUID(sourceUID), + WithIntegrationSourceSpec(makeIntegrationSourceSpec(sinkDest)), + ), + }, + Key: testNS + "/" + sourceName, + WantEvents: []string{ + Eventf(corev1.EventTypeNormal, containerSourceCreated, "ContainerSource created %q", containerSourceName), + Eventf(corev1.EventTypeNormal, sourceReconciled, `IntegrationSource reconciled: "%s/%s"`, testNS, sourceName), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: NewIntegrationSource(sourceName, testNS, + WithIntegrationSourceUID(sourceUID), + WithIntegrationSourceSpec(makeIntegrationSourceSpec(sinkDest)), + WithInitIntegrationSourceConditions, + ), + }}, + WantCreates: []runtime.Object{ + makeContainerSource(NewIntegrationSource(sourceName, testNS, + WithIntegrationSourceUID(sourceUID), + WithIntegrationSourceSpec(makeIntegrationSourceSpec(sinkDest))), + nil), + }, + }, { + Name: "successfully reconciled and ready", + Objects: []runtime.Object{ + NewIntegrationSource(sourceName, testNS, + WithIntegrationSourceUID(sourceUID), + WithIntegrationSourceSpec(makeIntegrationSourceSpec(sinkDest)), + ), + makeContainerSource(NewIntegrationSource(sourceName, testNS, + WithIntegrationSourceUID(sourceUID), + WithIntegrationSourceSpec(makeIntegrationSourceSpec(sinkDest)), + ), &conditionTrue), + }, + Key: testNS + "/" + sourceName, + WantEvents: []string{ + Eventf(corev1.EventTypeNormal, sourceReconciled, `IntegrationSource reconciled: "%s/%s"`, testNS, sourceName), + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{{ + Object: NewIntegrationSource(sourceName, testNS, + WithIntegrationSourceUID(sourceUID), + WithIntegrationSourceSpec(makeIntegrationSourceSpec(sinkDest)), + WithInitIntegrationSourceConditions, + WithIntegrationSourceStatusObservedGeneration(generation), + WithIntegrationSourcePropagateContainerSourceStatus(makeContainerSourceStatus(&conditionTrue)), + ), + }}, + }} + + logger := logtesting.TestLogger(t) + + table.Test(t, MakeFactory(func(ctx context.Context, listers *Listers, cmw configmap.Watcher) controller.Reconciler { + ctx = addressable.WithDuck(ctx) + r := &Reconciler{ + kubeClientSet: fakekubeclient.Get(ctx), + eventingClientSet: fakeeventingclient.Get(ctx), + containerSourceLister: listers.GetContainerSourceLister(), + integrationSourceLister: listers.GetIntegrationSourceLister(), + } + + return integrationsource.NewReconciler(ctx, logging.FromContext(ctx), fakeeventingclient.Get(ctx), listers.GetIntegrationSourceLister(), controller.GetEventRecorder(ctx), r) + }, + true, + logger, + )) +} + +func makeContainerSource(source *sourcesv1alpha1.IntegrationSource, ready *corev1.ConditionStatus) runtime.Object { + cs := &sourcesv1.ContainerSource{ + ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{ + *kmeta.NewControllerRef(source), + }, + Name: containerSourceName, + Namespace: source.Namespace, + }, + Spec: sourcesv1.ContainerSourceSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "source", + Image: "gcr.io/knative-nightly/timer-source:latest", + ImagePullPolicy: corev1.PullIfNotPresent, + Env: []corev1.EnvVar{ + { + Name: "CAMEL_KNATIVE_CLIENT_SSL_ENABLED", + Value: "true", + }, + { + Name: "CAMEL_KNATIVE_CLIENT_SSL_CERT_PATH", + Value: "/knative-custom-certs/knative-eventing-bundle.pem", + }, + { + Name: "CAMEL_KAMELET_TIMER_SOURCE_PERIOD", + Value: "1000", + }, + { + Name: "CAMEL_KAMELET_TIMER_SOURCE_MESSAGE", + Value: "Hallo", + }, + { + Name: "CAMEL_KAMELET_TIMER_SOURCE_REPEATCOUNT", + Value: "0", + }, + }, + }, + }, + }, + }, + SourceSpec: source.Spec.SourceSpec, + }, + } + + if ready != nil { + cs.Status = *makeContainerSourceStatus(ready) + } + return cs +} + +func makeContainerSourceStatus(ready *corev1.ConditionStatus) *sourcesv1.ContainerSourceStatus { + return &sourcesv1.ContainerSourceStatus{ + SourceStatus: duckv1.SourceStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{{ + Type: apis.ConditionReady, + Status: *ready, + }}, + }, + }, + } +} + +func makeIntegrationSourceSpec(sink duckv1.Destination) sourcesv1alpha1.IntegrationSourceSpec { + return sourcesv1alpha1.IntegrationSourceSpec{ + Timer: &sourcesv1alpha1.Timer{ + Period: 1000, + Message: "Hallo", + }, + SourceSpec: duckv1.SourceSpec{ + Sink: sink, + }, + } +} diff --git a/pkg/reconciler/integrationsource/resources/containersource.go b/pkg/reconciler/integrationsource/resources/containersource.go new file mode 100644 index 00000000000..85e4806c6f5 --- /dev/null +++ b/pkg/reconciler/integrationsource/resources/containersource.go @@ -0,0 +1,231 @@ +/* + Copyright 2024 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 resources + +import ( + "fmt" + "reflect" + "strconv" + "strings" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" + "knative.dev/eventing/pkg/apis/sources/v1alpha1" + "knative.dev/pkg/kmeta" +) + +func NewContainerSource(source *v1alpha1.IntegrationSource) *sourcesv1.ContainerSource { + return &sourcesv1.ContainerSource{ + ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{ + *kmeta.NewControllerRef(source), + }, + Name: ContainerSourceName(source), + Namespace: source.Namespace, + }, + Spec: sourcesv1.ContainerSourceSpec{ + + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "source", + Image: selectImage(source), + ImagePullPolicy: corev1.PullIfNotPresent, + Env: makeEnv(source), + }, + }, + }, + }, + SourceSpec: source.Spec.SourceSpec, + }, + } +} + +func generateEnvVarsFromStruct(prefix string, s interface{}) []corev1.EnvVar { + var envVars = makeSSLEnvVar() + + // Use reflection to inspect the struct fields + v := reflect.ValueOf(s) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + + t := v.Type() + + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + fieldType := t.Field(i) + + // Skip unexported fields + if !field.CanInterface() { + continue + } + + // Handle embedded/anonymous structs recursively + if fieldType.Anonymous && field.Kind() == reflect.Struct { + // Recursively handle embedded structs with the same prefix + envVars = append(envVars, generateEnvVarsFromStruct(prefix, field.Interface())...) + continue + } + + // Extract the JSON tag or fall back to the Go field name + jsonTag := fieldType.Tag.Get("json") + tagName := strings.Split(jsonTag, ",")[0] + + // fallback to Go field name if no JSON tag + if tagName == "" || tagName == "-" { + tagName = fieldType.Name + } + + envVarName := fmt.Sprintf("%s_%s", prefix, strings.ToUpper(tagName)) + + if field.Kind() == reflect.Ptr { + if field.IsNil() { + continue + } + field = field.Elem() + } + + var value string + switch field.Kind() { + case reflect.Int, reflect.Int32, reflect.Int64: + value = strconv.FormatInt(field.Int(), 10) + case reflect.Bool: + value = strconv.FormatBool(field.Bool()) + case reflect.String: + value = field.String() + default: + // Skip unsupported types + continue + } + + // Skip zero/empty values + if value == "" { + continue + } + + envVars = append(envVars, corev1.EnvVar{ + Name: envVarName, + Value: value, + }) + } + + return envVars +} + +// Function to create environment variables for Timer or AWS configurations dynamically +func makeEnv(source *v1alpha1.IntegrationSource) []corev1.EnvVar { + var envVars []corev1.EnvVar + + // Timer environment variables + if source.Spec.Timer != nil { + envVars = append(envVars, generateEnvVarsFromStruct("CAMEL_KAMELET_TIMER_SOURCE", *source.Spec.Timer)...) + return envVars + } + + // Handle secret name only if AWS is configured + var secretName string + if source.Spec.Aws != nil && source.Spec.Aws.Auth != nil && source.Spec.Aws.Auth.Secret != nil && source.Spec.Aws.Auth.Secret.Ref != nil { + secretName = source.Spec.Aws.Auth.Secret.Ref.Name + } + + // AWS S3 environment variables + if source.Spec.Aws != nil && source.Spec.Aws.S3 != nil { + envVars = append(envVars, generateEnvVarsFromStruct("CAMEL_KAMELET_AWS_S3_SOURCE", *source.Spec.Aws.S3)...) + if secretName != "" { + envVars = append(envVars, []corev1.EnvVar{ + makeSecretEnvVar("CAMEL_KAMELET_AWS_S3_SOURCE_ACCESSKEY", "aws.s3.accessKey", secretName), + makeSecretEnvVar("CAMEL_KAMELET_AWS_S3_SOURCE_SECRETKEY", "aws.s3.secretKey", secretName), + }...) + } + return envVars + } + + // AWS SQS environment variables + if source.Spec.Aws != nil && source.Spec.Aws.SQS != nil { + envVars = append(envVars, generateEnvVarsFromStruct("CAMEL_KAMELET_AWS_SQS_SOURCE", *source.Spec.Aws.SQS)...) + if secretName != "" { + envVars = append(envVars, []corev1.EnvVar{ + makeSecretEnvVar("CAMEL_KAMELET_AWS_SQS_SOURCE_ACCESSKEY", "aws.s3.accessKey", secretName), + makeSecretEnvVar("CAMEL_KAMELET_AWS_SQS_SOURCE_SECRETKEY", "aws.s3.secretKey", secretName), + }...) + } + return envVars + } + + // AWS DynamoDB Streams environment variables + if source.Spec.Aws != nil && source.Spec.Aws.DDBStreams != nil { + envVars = append(envVars, generateEnvVarsFromStruct("CAMEL_KAMELET_AWS_DDB_STREAMS_SOURCE", *source.Spec.Aws.DDBStreams)...) + if secretName != "" { + envVars = append(envVars, []corev1.EnvVar{ + makeSecretEnvVar("CAMEL_KAMELET_AWS_DDB_STREAMS_SOURCE_ACCESSKEY", "aws.s3.accessKey", secretName), + makeSecretEnvVar("CAMEL_KAMELET_AWS_DDB_STREAMS_SOURCE_SECRETKEY", "aws.s3.secretKey", secretName), + }...) + } + return envVars + } + + // If no valid configuration is found, return empty envVars + return envVars +} + +func makeSSLEnvVar() []corev1.EnvVar { + return []corev1.EnvVar{ + { + Name: "CAMEL_KNATIVE_CLIENT_SSL_ENABLED", + Value: "true", + }, + { + Name: "CAMEL_KNATIVE_CLIENT_SSL_CERT_PATH", + Value: "/knative-custom-certs/knative-eventing-bundle.pem", + }, + } +} + +func selectImage(source *v1alpha1.IntegrationSource) string { + if source.Spec.Timer != nil { + return "gcr.io/knative-nightly/timer-source:latest" + } + if source.Spec.Aws != nil { + if source.Spec.Aws.S3 != nil { + return "gcr.io/knative-nightly/aws-s3-source:latest" + } + if source.Spec.Aws.SQS != nil { + return "gcr.io/knative-nightly/aws-sqs-source:latest" + } + if source.Spec.Aws.DDBStreams != nil { + return "gcr.io/knative-nightly/aws-ddb-streams-source:latest" + } + } + return "" +} + +func makeSecretEnvVar(name, key, secretName string) corev1.EnvVar { + return corev1.EnvVar{ + Name: name, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + Key: key, + LocalObjectReference: corev1.LocalObjectReference{ + Name: secretName, + }, + }, + }, + } +} diff --git a/pkg/reconciler/integrationsource/resources/containersource_test.go b/pkg/reconciler/integrationsource/resources/containersource_test.go new file mode 100644 index 00000000000..5bac6445343 --- /dev/null +++ b/pkg/reconciler/integrationsource/resources/containersource_test.go @@ -0,0 +1,133 @@ +/* +Copyright 2024 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 resources + +import ( + "fmt" + "testing" + + sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" + "knative.dev/pkg/apis" + "knative.dev/pkg/kmeta" + + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/eventing/pkg/apis/sources/v1alpha1" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +const ( + testName = "test-integrationsource" + testNamespace = "test-namespace" + testUID = "test-uid" +) + +func TestNewContainerSource(t *testing.T) { + source := &v1alpha1.IntegrationSource{ + + ObjectMeta: metav1.ObjectMeta{ + Name: testName, + Namespace: testNamespace, + UID: testUID, + }, + Spec: v1alpha1.IntegrationSourceSpec{ + Timer: &v1alpha1.Timer{ + Period: 1000, + Message: "test-message", + ContentType: "text/plain", + RepeatCount: 0, + }, + SourceSpec: duckv1.SourceSpec{ + Sink: duckv1.Destination{ + URI: apis.HTTP("http://test-sink"), + }, + }, + }, + } + + want := &sourcesv1.ContainerSource{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-containersource", testName), + Namespace: testNamespace, + OwnerReferences: []metav1.OwnerReference{ + *kmeta.NewControllerRef(source), + }, + }, + Spec: sourcesv1.ContainerSourceSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "source", + Image: "gcr.io/knative-nightly/timer-source:latest", + ImagePullPolicy: corev1.PullIfNotPresent, + Env: []corev1.EnvVar{ + {Name: "CAMEL_KNATIVE_CLIENT_SSL_ENABLED", Value: "true"}, + {Name: "CAMEL_KNATIVE_CLIENT_SSL_CERT_PATH", Value: "/knative-custom-certs/knative-eventing-bundle.pem"}, + {Name: "CAMEL_KAMELET_TIMER_SOURCE_PERIOD", Value: "1000"}, + {Name: "CAMEL_KAMELET_TIMER_SOURCE_MESSAGE", Value: "test-message"}, + {Name: "CAMEL_KAMELET_TIMER_SOURCE_CONTENTTYPE", Value: "text/plain"}, + {Name: "CAMEL_KAMELET_TIMER_SOURCE_REPEATCOUNT", Value: "0"}, + }, + }, + }, + }, + }, + SourceSpec: duckv1.SourceSpec{ + Sink: duckv1.Destination{ + URI: apis.HTTP("http://test-sink"), + }, + }, + }, + } + + got := NewContainerSource(source) + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("NewContainerSource() mismatch (-want +got):\n%s", diff) + } +} + +func TestGenerateEnvVarsFromStruct(t *testing.T) { + type TestStruct struct { + Field1 int `json:"field1"` + Field2 bool `json:"field2"` + Field3 string `json:"field3"` + } + + prefix := "TEST_PREFIX" + input := &TestStruct{ + Field1: 123, + Field2: true, + Field3: "hello", + } + + // Expected environment variables including SSL settings + want := []corev1.EnvVar{ + {Name: "CAMEL_KNATIVE_CLIENT_SSL_ENABLED", Value: "true"}, + {Name: "CAMEL_KNATIVE_CLIENT_SSL_CERT_PATH", Value: "/knative-custom-certs/knative-eventing-bundle.pem"}, + {Name: "TEST_PREFIX_FIELD1", Value: "123"}, + {Name: "TEST_PREFIX_FIELD2", Value: "true"}, + {Name: "TEST_PREFIX_FIELD3", Value: "hello"}, + } + + got := generateEnvVarsFromStruct(prefix, input) + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("generateEnvVarsFromStruct() mismatch (-want +got):\n%s", diff) + } +} diff --git a/pkg/reconciler/integrationsource/resources/names.go b/pkg/reconciler/integrationsource/resources/names.go new file mode 100644 index 00000000000..a69509e41dc --- /dev/null +++ b/pkg/reconciler/integrationsource/resources/names.go @@ -0,0 +1,26 @@ +/* + Copyright 2024 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 resources + +import ( + "knative.dev/eventing/pkg/apis/sources/v1alpha1" + "knative.dev/pkg/kmeta" +) + +func ContainerSourceName(source *v1alpha1.IntegrationSource) string { + return kmeta.ChildName(source.Name, "-containersource") +} diff --git a/pkg/reconciler/testing/v1/listers.go b/pkg/reconciler/testing/v1/listers.go index 2d2c32a9f93..a6d0733136a 100644 --- a/pkg/reconciler/testing/v1/listers.go +++ b/pkg/reconciler/testing/v1/listers.go @@ -39,6 +39,7 @@ import ( messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1" sinksv1alpha1 "knative.dev/eventing/pkg/apis/sinks/v1alpha1" sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" + sourcesv1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" fakeeventingclientset "knative.dev/eventing/pkg/client/clientset/versioned/fake" eventinglisters "knative.dev/eventing/pkg/client/listers/eventing/v1" eventingv1alpha1listers "knative.dev/eventing/pkg/client/listers/eventing/v1alpha1" @@ -47,6 +48,7 @@ import ( messaginglisters "knative.dev/eventing/pkg/client/listers/messaging/v1" sinkslisters "knative.dev/eventing/pkg/client/listers/sinks/v1alpha1" sourcelisters "knative.dev/eventing/pkg/client/listers/sources/v1" + sourcev1alpha1listers "knative.dev/eventing/pkg/client/listers/sources/v1alpha1" testscheme "knative.dev/eventing/pkg/reconciler/testing/scheme" duckv1 "knative.dev/pkg/apis/duck/v1" "knative.dev/pkg/reconciler/testing" @@ -126,6 +128,10 @@ func (l *Listers) GetJobSinkLister() sinkslisters.JobSinkLister { return sinkslisters.NewJobSinkLister(l.indexerFor(&sinksv1alpha1.JobSink{})) } +func (l *Listers) GetIntegrationSourceLister() sourcev1alpha1listers.IntegrationSourceLister { + return sourcev1alpha1listers.NewIntegrationSourceLister(l.indexerFor(&sourcesv1alpha1.IntegrationSource{})) +} + func (l *Listers) GetPingSourceLister() sourcelisters.PingSourceLister { return sourcelisters.NewPingSourceLister(l.indexerFor(&sourcesv1.PingSource{})) } diff --git a/pkg/reconciler/testing/v1alpha1/integrationsource.go b/pkg/reconciler/testing/v1alpha1/integrationsource.go new file mode 100644 index 00000000000..ace331455c4 --- /dev/null +++ b/pkg/reconciler/testing/v1alpha1/integrationsource.go @@ -0,0 +1,73 @@ +/* +Copyright 2024 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 ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + v1 "knative.dev/eventing/pkg/apis/sources/v1" + v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1" +) + +// IntegrationSourceOption enables further configuration of a IntegrationSource. +type IntegrationSourceOption func(source *v1alpha1.IntegrationSource) + +// NewIntegrationSource creates a v1 IntegrationSource with IntegrationSourceOptions +func NewIntegrationSource(name, namespace string, o ...IntegrationSourceOption) *v1alpha1.IntegrationSource { + s := &v1alpha1.IntegrationSource{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } + for _, opt := range o { + opt(s) + } + s.SetDefaults(context.Background()) + return s +} + +func WithIntegrationSourceUID(uid types.UID) IntegrationSourceOption { + return func(s *v1alpha1.IntegrationSource) { + s.UID = uid + } +} + +// WithInitIntegrationSourceConditions initializes the IntegrationSource's conditions. +func WithInitIntegrationSourceConditions(s *v1alpha1.IntegrationSource) { + s.Status.InitializeConditions() +} + +func WithIntegrationSourceStatusObservedGeneration(generation int64) IntegrationSourceOption { + return func(s *v1alpha1.IntegrationSource) { + s.Status.ObservedGeneration = generation + } +} + +func WithIntegrationSourcePropagateContainerSourceStatus(status *v1.ContainerSourceStatus) IntegrationSourceOption { + return func(s *v1alpha1.IntegrationSource) { + s.Status.PropagateContainerSourceStatus(status) + } +} + +func WithIntegrationSourceSpec(spec v1alpha1.IntegrationSourceSpec) IntegrationSourceOption { + return func(s *v1alpha1.IntegrationSource) { + s.Spec = spec + } +} diff --git a/test/rekt/features/integrationsource/features.go b/test/rekt/features/integrationsource/features.go new file mode 100644 index 00000000000..583ea67c23c --- /dev/null +++ b/test/rekt/features/integrationsource/features.go @@ -0,0 +1,117 @@ +/* +Copyright 2024 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 integrationsource + +import ( + "context" + + "github.com/cloudevents/sdk-go/v2/test" + "knative.dev/eventing/pkg/eventingtls/eventingtlstesting" + "knative.dev/eventing/test/rekt/features/featureflags" + "knative.dev/eventing/test/rekt/features/source" + "knative.dev/eventing/test/rekt/resources/integrationsource" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" + "knative.dev/pkg/network" + "knative.dev/reconciler-test/pkg/environment" + "knative.dev/reconciler-test/pkg/eventshub" + "knative.dev/reconciler-test/pkg/eventshub/assert" + "knative.dev/reconciler-test/pkg/feature" + "knative.dev/reconciler-test/pkg/resources/service" +) + +func SendsEventsWithSinkRef() *feature.Feature { + source := feature.MakeRandomK8sName("integrationsource") + sink := feature.MakeRandomK8sName("sink") + f := feature.NewFeature() + + f.Setup("install sink", eventshub.Install(sink, eventshub.StartReceiver)) + + f.Requirement("install integrationsource", integrationsource.Install(source, integrationsource.WithSink(service.AsDestinationRef(sink)))) + f.Requirement("integrationsource goes ready", integrationsource.IsReady(source)) + + f.Stable("integrationsource as event source"). + Must("delivers events", + assert.OnStore(sink).MatchEvent(test.HasType("dev.knative.connector.event.timer")).AtLeast(1)) + + return f +} + +func SendEventsWithTLSRecieverAsSink() *feature.Feature { + src := feature.MakeRandomK8sName("integrationsource") + sink := feature.MakeRandomK8sName("sink") + f := feature.NewFeature() + + f.Prerequisite("should not run when Istio is enabled", featureflags.IstioDisabled()) + + f.Setup("install sink", eventshub.Install(sink, eventshub.StartReceiverTLS)) + + f.Requirement("install ContainerSource", func(ctx context.Context, t feature.T) { + d := service.AsDestinationRef(sink) + d.CACerts = eventshub.GetCaCerts(ctx) + + integrationsource.Install(src, integrationsource.WithSink(d))(ctx, t) + }) + f.Requirement("integrationsource goes ready", integrationsource.IsReady(src)) + + f.Stable("integrationsource as event source"). + Must("delivers events", + assert.OnStore(sink). + Match(assert.MatchKind(eventshub.EventReceived)). + MatchEvent(test.HasType("dev.knative.connector.event.timer")). + AtLeast(1), + ). + Must("Set sinkURI to HTTPS endpoint", source.ExpectHTTPSSink(integrationsource.Gvr(), src)). + Must("Set sinkCACerts to non empty CA certs", source.ExpectCACerts(integrationsource.Gvr(), src)) + + return f +} + +func SendEventsWithTLSRecieverAsSinkTrustBundle() *feature.Feature { + src := feature.MakeRandomK8sName("integrationsource") + sink := feature.MakeRandomK8sName("sink") + f := feature.NewFeature() + + f.Prerequisite("should not run when Istio is enabled", featureflags.IstioDisabled()) + + f.Setup("install sink", eventshub.Install(sink, + eventshub.IssuerRef(eventingtlstesting.IssuerKind, eventingtlstesting.IssuerName), + eventshub.StartReceiverTLS, + )) + + f.Requirement("install ContainerSource", func(ctx context.Context, t feature.T) { + integrationsource.Install(src, integrationsource.WithSink(&duckv1.Destination{ + URI: &apis.URL{ + Scheme: "https", // Force using https + Host: network.GetServiceHostname(sink, environment.FromContext(ctx).Namespace()), + }, + CACerts: nil, // CA certs are in the trust-bundle + }))(ctx, t) + }) + f.Requirement("integrationsource goes ready", integrationsource.IsReady(src)) + + f.Stable("integrationsource as event source"). + Must("delivers events", + assert.OnStore(sink). + Match(assert.MatchKind(eventshub.EventReceived)). + MatchEvent(test.HasType("dev.knative.connector.event.timer")). + AtLeast(1), + ). + Must("Set sinkURI to HTTPS endpoint", source.ExpectHTTPSSink(integrationsource.Gvr(), src)) + + return f +} diff --git a/test/rekt/features/integrationsource/oidc_feature.go b/test/rekt/features/integrationsource/oidc_feature.go new file mode 100644 index 00000000000..c4f65a7f2dd --- /dev/null +++ b/test/rekt/features/integrationsource/oidc_feature.go @@ -0,0 +1,65 @@ +/* +Copyright 2024 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 integrationsource + +import ( + "context" + + "github.com/cloudevents/sdk-go/v2/test" + "knative.dev/eventing/test/rekt/features/featureflags" + "knative.dev/eventing/test/rekt/features/source" + "knative.dev/eventing/test/rekt/resources/integrationsource" + "knative.dev/reconciler-test/pkg/eventshub" + "knative.dev/reconciler-test/pkg/eventshub/assert" + "knative.dev/reconciler-test/pkg/feature" + "knative.dev/reconciler-test/pkg/resources/service" +) + +func SendsEventsWithSinkRefOIDC() *feature.Feature { + src := feature.MakeRandomK8sName("integrationsource") + sink := feature.MakeRandomK8sName("sink") + sinkAudience := "audience" + f := feature.NewFeature() + + f.Prerequisite("OIDC authentication is enabled", featureflags.AuthenticationOIDCEnabled()) + f.Prerequisite("transport encryption is strict", featureflags.TransportEncryptionStrict()) + f.Prerequisite("should not run when Istio is enabled", featureflags.IstioDisabled()) + + f.Setup("install sink", eventshub.Install(sink, + eventshub.OIDCReceiverAudience(sinkAudience), + eventshub.StartReceiverTLS)) + + f.Requirement("install integrationsource", func(ctx context.Context, t feature.T) { + d := service.AsDestinationRef(sink) + d.CACerts = eventshub.GetCaCerts(ctx) + d.Audience = &sinkAudience + + integrationsource.Install(src, integrationsource.WithSink(d))(ctx, t) + }) + + f.Requirement("integrationsource goes ready", integrationsource.IsReady(src)) + + f.Stable("integrationsource as event source"). + Must("delivers events", + assert.OnStore(sink).MatchEvent(test.HasType("dev.knative.connector.event.timer")).AtLeast(1)). + Must("uses integrationsources identity for OIDC", assert.OnStore(sink).MatchWithContext( + assert.MatchKind(eventshub.EventReceived).WithContext(), + assert.MatchOIDCUserFromResource(integrationsource.Gvr(), src)).AtLeast(1)). + Must("Set sinkURI to HTTPS endpoint", source.ExpectHTTPSSink(integrationsource.Gvr(), src)). + Must("Set sinkCACerts to non empty CA certs", source.ExpectCACerts(integrationsource.Gvr(), src)) + return f +} diff --git a/test/rekt/integrationsource_test.go b/test/rekt/integrationsource_test.go new file mode 100644 index 00000000000..c33a89d8b04 --- /dev/null +++ b/test/rekt/integrationsource_test.go @@ -0,0 +1,63 @@ +//go:build e2e +// +build e2e + +/* +Copyright 2021 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 rekt + +import ( + "testing" + + "knative.dev/pkg/system" + "knative.dev/reconciler-test/pkg/environment" + "knative.dev/reconciler-test/pkg/eventshub" + "knative.dev/reconciler-test/pkg/k8s" + "knative.dev/reconciler-test/pkg/knative" + + "knative.dev/eventing/test/rekt/features/integrationsource" +) + +func TestIntegrationSourceWithSinkRef(t *testing.T) { + t.Parallel() + + ctx, env := global.Environment( + knative.WithKnativeNamespace(system.Namespace()), + knative.WithLoggingConfig, + knative.WithTracingConfig, + k8s.WithEventListener, + environment.Managed(t), + ) + t.Cleanup(env.Finish) + + env.Test(ctx, t, integrationsource.SendsEventsWithSinkRef()) +} + +func TestIntegrationSourceWithTLS(t *testing.T) { + t.Parallel() + + ctx, env := global.Environment( + knative.WithKnativeNamespace(system.Namespace()), + knative.WithLoggingConfig, + knative.WithTracingConfig, + k8s.WithEventListener, + environment.Managed(t), + eventshub.WithTLS(t), + ) + + env.ParallelTest(ctx, t, integrationsource.SendEventsWithTLSRecieverAsSink()) + env.ParallelTest(ctx, t, integrationsource.SendEventsWithTLSRecieverAsSinkTrustBundle()) +} diff --git a/test/rekt/resources/integrationsource/integrationsource.go b/test/rekt/resources/integrationsource/integrationsource.go new file mode 100644 index 00000000000..0c6821b3ea4 --- /dev/null +++ b/test/rekt/resources/integrationsource/integrationsource.go @@ -0,0 +1,103 @@ +/* +Copyright 2024 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 integrationsource + +import ( + "context" + "embed" + "strings" + "time" + + "k8s.io/apimachinery/pkg/runtime/schema" + duckv1 "knative.dev/pkg/apis/duck/v1" + "knative.dev/reconciler-test/pkg/environment" + "knative.dev/reconciler-test/pkg/feature" + "knative.dev/reconciler-test/pkg/k8s" + "knative.dev/reconciler-test/pkg/manifest" +) + +//go:embed integrationsource.yaml +var yaml embed.FS + +func Gvr() schema.GroupVersionResource { + return schema.GroupVersionResource{Group: "sources.knative.dev", Version: "v1alpha1", Resource: "integrationsources"} +} + +// IsReady tests to see if a ContainerSource becomes ready within the time given. +func IsReady(name string, timing ...time.Duration) feature.StepFn { + return k8s.IsReady(Gvr(), name, timing...) +} + +// Install will create a ContainerSource resource, augmented with the config fn options. +func Install(name string, opts ...manifest.CfgFn) feature.StepFn { + cfg := map[string]interface{}{ + "name": name, + } + for _, fn := range opts { + fn(cfg) + } + + return func(ctx context.Context, t feature.T) { + if ic := environment.GetIstioConfig(ctx); ic.Enabled { + manifest.WithIstioPodAnnotations(cfg) + } + + //if err := registerImage(ctx); err != nil { + // t.Fatal(err) + //} + if _, err := manifest.InstallYamlFS(ctx, yaml, cfg); err != nil { + t.Fatal(err) + } + } +} + +func WithSink(d *duckv1.Destination) manifest.CfgFn { + return func(cfg map[string]interface{}) { + if _, set := cfg["sink"]; !set { + cfg["sink"] = map[string]interface{}{} + } + sink := cfg["sink"].(map[string]interface{}) + + ref := d.Ref + uri := d.URI + + if d.CACerts != nil { + // This is a multi-line string and should be indented accordingly. + // Replace "new line" with "new line + spaces". + sink["CACerts"] = strings.ReplaceAll(*d.CACerts, "\n", "\n ") + } + + if uri != nil { + sink["uri"] = uri.String() + } + + if d.Audience != nil { + sink["audience"] = *d.Audience + } + + if ref != nil { + if _, set := sink["ref"]; !set { + sink["ref"] = map[string]interface{}{} + } + sref := sink["ref"].(map[string]interface{}) + sref["apiVersion"] = ref.APIVersion + sref["kind"] = ref.Kind + sref["namespace"] = ref.Namespace + sref["name"] = ref.Name + } + } +} diff --git a/test/rekt/resources/integrationsource/integrationsource.yaml b/test/rekt/resources/integrationsource/integrationsource.yaml new file mode 100644 index 00000000000..0da9cd6f231 --- /dev/null +++ b/test/rekt/resources/integrationsource/integrationsource.yaml @@ -0,0 +1,47 @@ +# Copyright 2024 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: sources.knative.dev/v1alpha1 +kind: IntegrationSource +metadata: + name: {{ .name }} + namespace: {{ .namespace }} +spec: + timer: + period: 2000 + message: "Hello, Eventing Core" +# {{if .sink }} + sink: + {{ if .sink.ref }} + ref: + kind: {{ .sink.ref.kind }} + {{ if .sink.ref.namespace }} + namespace: {{ .sink.ref.namespace }} + {{ else }} + namespace: {{ .namespace }} + {{ end }} + name: {{ .sink.ref.name }} + apiVersion: {{ .sink.ref.apiVersion }} + {{ end }} + {{ if .sink.uri }} + uri: {{ .sink.uri }} + {{ end }} + {{ if .sink.CACerts }} + CACerts: |- + {{ .sink.CACerts }} + {{ end }} + {{if .sink.audience }} + audience: {{ .sink.audience}} + {{ end }} + {{ end }}