feature(source binding): Support for Sink Binding as source (#625)

* feature(source binding): Support for Sink Binding as source

Sink bindings are managed like any other source. Sinks are specified as usual (with prefix and name),
'subjects' (the other end of the binding) is managed via a shortcut notation:

* with name: `<kind>:<apiVersion>:<name>`
* with label selector: `<kind>:<apiVersion>:key1=value1,key2=value2`

With `--subject-namespace` and additional namespace can be provided (shoudl be possible for a sink, too but is not yet)

The implementation already uses the new sink binding from the `sources.knative.dev` group
and hence is a bit inconsistent to the still old usage kf `sources.eventing.knative.dev` for apiserver source and cronjob.

However as we will move over to `sources.knative.dev` very soon (right after v0.12.0) release,
this is was more appropriates.

Still WIP, but eventually fixes #624

Task list:

- [X] create
- [] update
- [] delete
- [] describe
- [] list

* fix(source binding): Added missing commands

Also, it looks like that knative eventing 0.12.0 is still on the old api group.
So I need to move this code to use the legacyclient, too, for the sink binding
and only switch over to the new client for 0.13.0

* feature(source binding): Added missing subcommands

* update
* delete
* list
* describe

* fix(source binding): Removed --subject-namespace as this namespace cant b set.

Tuned describe output a bit.

* feat(source binding) add support for CeOverride

* fix: Worked on review comments

* fix: review comments

* fix: names in binding client
This commit is contained in:
Roland Huß 2020-01-29 21:08:27 +01:00 committed by GitHub
parent dfd6defcd1
commit 164cb5f362
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 3090 additions and 53 deletions

View File

@ -28,6 +28,7 @@ kn source [flags]
* [kn](kn.md) - Knative client
* [kn source apiserver](kn_source_apiserver.md) - Kubernetes API Server Event Source command group
* [kn source binding](kn_source_binding.md) - Sink binding command group
* [kn source cronjob](kn_source_cronjob.md) - CronJob source command group
* [kn source list-types](kn_source_list-types.md) - List available source types

View File

@ -0,0 +1,35 @@
## kn source binding
Sink binding command group
### Synopsis
Sink binding command group
```
kn source binding [flags]
```
### Options
```
-h, --help help for binding
```
### Options inherited from parent commands
```
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--log-http log http traffic
```
### SEE ALSO
* [kn source](kn_source.md) - Event source command group
* [kn source binding create](kn_source_binding_create.md) - Create a sink binding.
* [kn source binding delete](kn_source_binding_delete.md) - Delete a sink binding.
* [kn source binding describe](kn_source_binding_describe.md) - Describe a sink binding.
* [kn source binding list](kn_source_binding_list.md) - List sink bindings.
* [kn source binding update](kn_source_binding_update.md) - Update a sink binding.

View File

@ -0,0 +1,42 @@
## kn source binding create
Create a sink binding.
### Synopsis
Create a sink binding.
```
kn source binding create NAME --subject SCHEDULE --sink SINK --ce-override KEY=VALUE [flags]
```
### Examples
```
# Create a sink binding which connects a deployment 'myapp' with a Knative service 'mysvc'
kn source binding create my-binding --subject Deployemnt:apps/v1:myapp --sink svc:mysvc
```
### Options
```
--ce-override stringArray Cloud Event overrides to apply before sending event to sink in the format '--ce-override key=value'. --ce-override can be provide multiple times
-h, --help help for create
-n, --namespace string Specify the namespace to operate in.
-s, --sink string Addressable sink for events
--subject string Subject which emits cloud events. This argument takes format kind:apiVersion:name for named resources or kind:apiVersion:labelKey1=value1,labelKey2=value2 for matching via a label selector
```
### Options inherited from parent commands
```
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--log-http log http traffic
```
### SEE ALSO
* [kn source binding](kn_source_binding.md) - Sink binding command group

View File

@ -0,0 +1,39 @@
## kn source binding delete
Delete a sink binding.
### Synopsis
Delete a sink binding.
```
kn source binding delete NAME [flags]
```
### Examples
```
# Delete a sink binding with name 'my-binding'
kn source binding delete my-binding
```
### Options
```
-h, --help help for delete
-n, --namespace string Specify the namespace to operate in.
```
### Options inherited from parent commands
```
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--log-http log http traffic
```
### SEE ALSO
* [kn source binding](kn_source_binding.md) - Sink binding command group

View File

@ -0,0 +1,40 @@
## kn source binding describe
Describe a sink binding.
### Synopsis
Describe a sink binding.
```
kn source binding describe NAME [flags]
```
### Examples
```
# Describe a sink binding with name 'mysinkbinding'
kn source binding describe mysinkbinding
```
### Options
```
-h, --help help for describe
-n, --namespace string Specify the namespace to operate in.
-v, --verbose More output.
```
### Options inherited from parent commands
```
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--log-http log http traffic
```
### SEE ALSO
* [kn source binding](kn_source_binding.md) - Sink binding command group

View File

@ -0,0 +1,44 @@
## kn source binding list
List sink bindings.
### Synopsis
List sink bindings.
```
kn source binding list [flags]
```
### Examples
```
# List all sink binding in YAML format
kn source binding list -o yaml
```
### Options
```
-A, --all-namespaces If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.
--allow-missing-template-keys If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. (default true)
-h, --help help for list
-n, --namespace string Specify the namespace to operate in.
--no-headers When using the default output format, don't print headers (default: print headers).
-o, --output string Output format. One of: json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file.
--template string Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].
```
### Options inherited from parent commands
```
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--log-http log http traffic
```
### SEE ALSO
* [kn source binding](kn_source_binding.md) - Sink binding command group

View File

@ -0,0 +1,42 @@
## kn source binding update
Update a sink binding.
### Synopsis
Update a sink binding.
```
kn source binding update NAME --subject SCHEDULE --sink SINK --ce-override OVERRIDE [flags]
```
### Examples
```
# Update the subject of a sink binding 'my-binding' to a new cronjob with label selector 'app=ping'
kn source binding update my-binding --subject cronjob:batch/v1beta1:app=ping"
```
### Options
```
--ce-override stringArray Cloud Event overrides to apply before sending event to sink in the format '--ce-override key=value'. --ce-override can be provide multiple times
-h, --help help for update
-n, --namespace string Specify the namespace to operate in.
-s, --sink string Addressable sink for events
--subject string Subject which emits cloud events. This argument takes format kind:apiVersion:name for named resources or kind:apiVersion:labelKey1=value1,labelKey2=value2 for matching via a label selector
```
### Options inherited from parent commands
```
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--log-http log http traffic
```
### SEE ALSO
* [kn source binding](kn_source_binding.md) - Sink binding command group

View File

@ -14,8 +14,8 @@ kn source cronjob describe NAME [flags]
```
# Describe a cronjob source with name 'my-cron-trigger'
kn source cronjob describe my-cron-trigger
# Describe a cronjob source with name 'mycronjob'
kn source cronjob describe mycronjob
```
### Options

View File

@ -134,8 +134,9 @@ func newAPIServerSource(name, resource string) *v1alpha1.ApiServerSource {
b := NewAPIServerSourceBuilder(name).ServiceAccount("testsa").Mode("Ref")
b.Sink(&v1beta1.Destination{
Ref: &v1.ObjectReference{
Kind: "Service",
Name: "foosvc",
Kind: "Service",
Name: "foosvc",
Namespace: "default",
}})
if resource != "" {

View File

@ -142,8 +142,9 @@ func newCronJobSource(name string, sink string) *v1alpha1.CronJobSource {
b.Sink(
&v1beta1.Destination{
Ref: &v1.ObjectReference{
Kind: "Service",
Name: sink,
Kind: "Service",
Name: sink,
Namespace: "default",
},
})
}

View File

@ -27,10 +27,6 @@ import (
"knative.dev/client/pkg/util"
)
const (
nameFieldKey = "metadata.name"
)
// KnEventingClient to Eventing Sources. All methods are relative to the
// namespace specified during construction
type KnEventingClient interface {

View File

@ -17,8 +17,9 @@ package v1alpha1
import (
"testing"
"knative.dev/client/pkg/util/mock"
"knative.dev/eventing/pkg/apis/eventing/v1alpha1"
"knative.dev/client/pkg/util/mock"
)
// MockKnEventingClient is a combine of test object and recorder

View File

@ -81,15 +81,15 @@ func (i *SinkFlags) ResolveSink(knclient kn_dynamic.KnDynamicClient, namespace s
return nil, err
}
return &duckv1beta1.Destination{
destination := &duckv1beta1.Destination{
Ref: &v1.ObjectReference{
Kind: obj.GetKind(),
APIVersion: obj.GetAPIVersion(),
Name: obj.GetName(),
Namespace: namespace,
},
}, nil
}
return destination, nil
}
// parseSink takes the string given by the user into the prefix and the name of

View File

@ -49,20 +49,19 @@ func TestResolve(t *testing.T) {
{"svc:mysvc", &duckv1beta1.Destination{
Ref: &v1.ObjectReference{Kind: "Service",
APIVersion: "serving.knative.dev/v1alpha1",
Name: "mysvc",
Namespace: "default"}}, ""},
Namespace: "default",
Name: "mysvc"}}, ""},
{"service:mysvc", &duckv1beta1.Destination{
Ref: &v1.ObjectReference{Kind: "Service",
APIVersion: "serving.knative.dev/v1alpha1",
Name: "mysvc",
Namespace: "default"}}, ""},
Namespace: "default",
Name: "mysvc"}}, ""},
{"svc:absent", nil, "\"absent\" not found"},
{"broker:default", &duckv1beta1.Destination{
Ref: &v1.ObjectReference{Kind: "Broker",
APIVersion: "eventing.knative.dev/v1alpha1",
Name: "default",
Namespace: "default",
}}, ""},
Name: "default"}}, ""},
{"http://target.example.com", &duckv1beta1.Destination{
URI: targetExampleCom,
}, ""},

View File

@ -67,8 +67,6 @@ func (f *HumanPrintFlags) EnsureWithNamespace() {
f.WithNamespace = true
}
// Private functions
// conditionsValue returns the True conditions count among total conditions
func ConditionsValue(conditions duckv1.Conditions) string {
var ok int

View File

@ -188,6 +188,9 @@ func printSource(source *v1alpha1.ApiServerSource, options hprinters.PrintOption
resources = append(resources, fmt.Sprintf("%s:%s:%s", resource.Kind, resource.APIVersion, strconv.FormatBool(resource.Controller)))
}
// Not moving to SinkToString() as it references v1beta1.Destination
// This source is going to be moved/removed soon to v1, so no need to move
// it now
var sink string
if source.Spec.Sink != nil {
if source.Spec.Sink.Ref != nil {

View File

@ -0,0 +1,172 @@
// Copyright © 2019 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 binding
import (
"fmt"
"strings"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/clientcmd"
"knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1"
duckv1 "knative.dev/pkg/apis/duck/v1"
duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"
"knative.dev/pkg/tracker"
"knative.dev/client/pkg/kn/commands"
sources_v1alpha1 "knative.dev/client/pkg/sources/v1alpha1"
"knative.dev/client/pkg/util"
)
// NewBindingCommand is the root command for all binding related commands
func NewBindingCommand(p *commands.KnParams) *cobra.Command {
bindingCmd := &cobra.Command{
Use: "binding",
Short: "Sink binding command group",
}
bindingCmd.AddCommand(NewBindingCreateCommand(p))
bindingCmd.AddCommand(NewBindingUpdateCommand(p))
bindingCmd.AddCommand(NewBindingDeleteCommand(p))
bindingCmd.AddCommand(NewBindingListCommand(p))
bindingCmd.AddCommand(NewBindingDescribeCommand(p))
return bindingCmd
}
// This var can be used to inject a factory for fake clients when doing
// tests.
var sinkBindingClientFactory func(config clientcmd.ClientConfig, namespace string) (sources_v1alpha1.KnSinkBindingClient, error)
func newSinkBindingClient(p *commands.KnParams, cmd *cobra.Command) (sources_v1alpha1.KnSinkBindingClient, error) {
namespace, err := p.GetNamespace(cmd)
if err != nil {
return nil, err
}
if sinkBindingClientFactory != nil {
config, err := p.GetClientConfig()
if err != nil {
return nil, err
}
return sinkBindingClientFactory(config, namespace)
}
clientConfig, err := p.RestConfig()
if err != nil {
return nil, err
}
client, err := v1alpha1.NewForConfig(clientConfig)
if err != nil {
return nil, err
}
return sources_v1alpha1.NewKnSourcesClient(client, namespace).SinkBindingClient(), nil
}
// Temporary conversions function until we move to duckv1
func toDuckV1(destination *duckv1beta1.Destination) *duckv1.Destination {
return &duckv1.Destination{
Ref: destination.Ref,
URI: destination.URI,
}
}
func toReference(subjectArg string, namespace string) (*tracker.Reference, error) {
parts := strings.SplitN(subjectArg, ":", 3)
if len(parts) < 3 {
return nil, fmt.Errorf("invalid subject argument '%s': not in format kind:api/version:nameOrSelector", subjectArg)
}
kind := parts[0]
gv, err := schema.ParseGroupVersion(parts[1])
if err != nil {
return nil, err
}
reference := &tracker.Reference{
APIVersion: gv.String(),
Kind: kind,
Namespace: namespace,
}
if !strings.Contains(parts[2], "=") {
reference.Name = parts[2]
} else {
selector, err := parseSelector(parts[2])
if err != nil {
return nil, err
}
reference.Selector = &metav1.LabelSelector{MatchLabels: selector}
}
return reference, nil
}
func parseSelector(labelSelector string) (map[string]string, error) {
selector := map[string]string{}
for _, p := range strings.Split(labelSelector, ",") {
keyValue := strings.SplitN(p, "=", 2)
if len(keyValue) != 2 {
return nil, fmt.Errorf("invalid subject label selector '%s', expected format: key1=value,key2=value", labelSelector)
}
selector[keyValue[0]] = keyValue[1]
}
return selector, nil
}
// subjectToString converts a reference to a string representation
func subjectToString(ref tracker.Reference) string {
ret := ref.Kind + ":" + ref.APIVersion
if ref.Name != "" {
return ret + ":" + ref.Name
}
var keyValues []string
selector := ref.Selector
if selector != nil {
for k, v := range selector.MatchLabels {
keyValues = append(keyValues, k+"="+v)
}
return ret + ":" + strings.Join(keyValues, ",")
}
return ret
}
// sinkToString prepares a sink for list output
// This is kept here until we have moved everything to duckv1 (currently the other sources
// are still on duckv1beta1)
func sinkToString(sink duckv1.Destination) string {
if sink.Ref != nil {
if sink.Ref.Kind == "Service" {
return fmt.Sprintf("svc:%s", sink.Ref.Name)
} else {
return fmt.Sprintf("%s:%s", sink.Ref.Kind, sink.Ref.Name)
}
}
if sink.URI != nil {
return sink.URI.String()
}
return ""
}
// updateCeOverrides updates the values of the --ce-override flags if given
func updateCeOverrides(bindingFlags bindingUpdateFlags, bindingBuilder *sources_v1alpha1.SinkBindingBuilder) error {
if bindingFlags.ceOverrides != nil {
ceOverrideMap, err := util.MapFromArray(bindingFlags.ceOverrides, "=")
if err != nil {
return err
}
bindingBuilder.AddCloudEventOverrides(ceOverrideMap)
}
return nil
}

View File

@ -0,0 +1,108 @@
// Copyright © 2019 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 binding
import (
"bytes"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/clientcmd"
"knative.dev/eventing/pkg/apis/sources/v1alpha1"
v1 "knative.dev/pkg/apis/duck/v1"
kn_dynamic "knative.dev/client/pkg/dynamic"
"knative.dev/client/pkg/kn/commands"
cl_sources_v1alpha1 "knative.dev/client/pkg/sources/v1alpha1"
)
// Helper methods
var blankConfig clientcmd.ClientConfig
// Gvk used in tests
var deploymentGvk = schema.GroupVersionKind{"apps", "v1", "deployment"}
// TODO: Remove that blankConfig hack for tests in favor of overwriting GetConfig()
// Remove also in service_test.go
func init() {
var err error
blankConfig, err = clientcmd.NewClientConfigFromBytes([]byte(`kind: Config
version: v1
users:
- name: u
clusters:
- name: c
cluster:
server: example.com
contexts:
- name: x
context:
user: u
cluster: c
current-context: x
`))
if err != nil {
panic(err)
}
}
func executeSinkBindingCommand(sinkBindingClient cl_sources_v1alpha1.KnSinkBindingClient, dynamicClient kn_dynamic.KnDynamicClient, args ...string) (string, error) {
knParams := &commands.KnParams{}
knParams.ClientConfig = blankConfig
output := new(bytes.Buffer)
knParams.Output = output
knParams.NewDynamicClient = func(namespace string) (kn_dynamic.KnDynamicClient, error) {
return dynamicClient, nil
}
cmd := NewBindingCommand(knParams)
cmd.SetArgs(args)
cmd.SetOutput(output)
sinkBindingClientFactory = func(config clientcmd.ClientConfig, namespace string) (cl_sources_v1alpha1.KnSinkBindingClient, error) {
return sinkBindingClient, nil
}
defer cleanupSinkBindingClient()
err := cmd.Execute()
return output.String(), err
}
func cleanupSinkBindingClient() {
sinkBindingClientFactory = nil
}
func createSinkBinding(name, service string, subjectGvk schema.GroupVersionKind, subjectName string, ceOverrides map[string]string) *v1alpha1.SinkBinding {
sink := createServiceSink(service)
builder := cl_sources_v1alpha1.NewSinkBindingBuilder(name).
Namespace("default").
Sink(&sink).
SubjectGVK(&subjectGvk).
SubjectName(subjectName).
SubjectNamespace("default")
if ceOverrides != nil {
builder.AddCloudEventOverrides(ceOverrides)
}
binding, _ := builder.Build()
return binding
}
func createServiceSink(service string) v1.Destination {
return v1.Destination{
Ref: &corev1.ObjectReference{Name: service, Kind: "Service", APIVersion: "serving.knative.dev/v1alpha1", Namespace: "default"},
}
}

View File

@ -0,0 +1,98 @@
// Copyright © 2019 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 binding
import (
"errors"
"fmt"
"github.com/spf13/cobra"
"knative.dev/client/pkg/kn/commands"
"knative.dev/client/pkg/kn/commands/flags"
v1alpha12 "knative.dev/client/pkg/sources/v1alpha1"
)
// NewBindingCreateCommand is for creating sink bindings
func NewBindingCreateCommand(p *commands.KnParams) *cobra.Command {
var bindingFlags bindingUpdateFlags
var sinkFlags flags.SinkFlags
cmd := &cobra.Command{
Use: "create NAME --subject SCHEDULE --sink SINK --ce-override KEY=VALUE",
Short: "Create a sink binding.",
Example: `
# Create a sink binding which connects a deployment 'myapp' with a Knative service 'mysvc'
kn source binding create my-binding --subject Deployemnt:apps/v1:myapp --sink svc:mysvc`,
RunE: func(cmd *cobra.Command, args []string) (err error) {
if len(args) != 1 {
return errors.New("requires the name of the sink binding to create as single argument")
}
name := args[0]
sinkBindingClient, err := newSinkBindingClient(p, cmd)
if err != nil {
return err
}
namespace, err := p.GetNamespace(cmd)
if err != nil {
return err
}
dynamicClient, err := p.NewDynamicClient(namespace)
if err != nil {
return err
}
destination, err := sinkFlags.ResolveSink(dynamicClient, namespace)
if err != nil {
return err
}
reference, err := toReference(bindingFlags.subject, namespace)
if err != nil {
return err
}
bindingBuilder := v1alpha12.NewSinkBindingBuilder(name).
Sink(toDuckV1(destination)).
Subject(reference).
Namespace(namespace)
err = updateCeOverrides(bindingFlags, bindingBuilder)
if err != nil {
return err
}
binding, err := bindingBuilder.Build()
if err != nil {
return err
}
err = sinkBindingClient.CreateSinkBinding(binding)
if err == nil {
fmt.Fprintf(cmd.OutOrStdout(), "Sink binding '%s' created in namespace '%s'.\n", args[0], sinkBindingClient.Namespace())
}
return err
},
}
commands.AddNamespaceFlags(cmd.Flags(), false)
bindingFlags.addBindingFlags(cmd)
sinkFlags.Add(cmd)
cmd.MarkFlagRequired("subject")
cmd.MarkFlagRequired("sink")
return cmd
}

View File

@ -0,0 +1,57 @@
// Copyright © 2019 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 binding
import (
"testing"
"gotest.tools/assert"
dynamic_fake "knative.dev/client/pkg/dynamic/fake"
"knative.dev/client/pkg/sources/v1alpha1"
"knative.dev/client/pkg/util"
)
func TestSimpleCreateBinding(t *testing.T) {
mysvc := createService("mysvc")
dynamicClient := dynamic_fake.CreateFakeKnDynamicClient("default", mysvc)
bindingClient := v1alpha1.NewMockKnSinkBindingClient(t)
bindingRecorder := bindingClient.Recorder()
bindingRecorder.CreateSinkBinding(createSinkBinding("testbinding", "mysvc", deploymentGvk, "mydeploy", map[string]string{"bla": "blub", "foo": "bar"}), nil)
out, err := executeSinkBindingCommand(bindingClient, dynamicClient, "create", "testbinding", "--sink", "svc:mysvc", "--subject", "deployment:apps/v1:mydeploy", "--ce-override", "bla=blub", "--ce-override", "foo=bar")
assert.NilError(t, err, "Source should have been created")
util.ContainsAll(out, "created", "default", "testbinding")
bindingRecorder.Validate()
}
func TestNoSinkError(t *testing.T) {
bindingClient := v1alpha1.NewMockKnSinkBindingClient(t)
dynamicClient := dynamic_fake.CreateFakeKnDynamicClient("default")
_, err := executeSinkBindingCommand(bindingClient, dynamicClient, "create", "testbinding", "--sink", "svc:mysvc", "--subject", "deployment:apps/v1:app=myapp")
assert.ErrorContains(t, err, "mysvc")
assert.ErrorContains(t, err, "not found")
}
func TestNoSinkGivenError(t *testing.T) {
out, err := executeSinkBindingCommand(nil, nil, "create", "testbinding", "--subject", "deployment:apps/v1:app=myapp")
assert.ErrorContains(t, err, "sink")
assert.ErrorContains(t, err, "required")
assert.Assert(t, util.ContainsAll(out, "not set", "required"))
}

View File

@ -0,0 +1,55 @@
// Copyright © 2019 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 binding
import (
"errors"
"fmt"
"github.com/spf13/cobra"
"knative.dev/client/pkg/kn/commands"
)
// NewBindingDeleteCommand is for deleting a sink binding
func NewBindingDeleteCommand(p *commands.KnParams) *cobra.Command {
cmd := &cobra.Command{
Use: "delete NAME",
Short: "Delete a sink binding.",
Example: `
# Delete a sink binding with name 'my-binding'
kn source binding delete my-binding`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("requires the name of the sink binding to delete as single argument")
}
name := args[0]
bindingClient, err := newSinkBindingClient(p, cmd)
if err != nil {
return err
}
err = bindingClient.DeleteSinkBinding(name)
if err != nil {
return err
}
fmt.Fprintf(cmd.OutOrStdout(), "Sink binding '%s' deleted in namespace '%s'.\n", name, bindingClient.Namespace())
return nil
},
}
commands.AddNamespaceFlags(cmd.Flags(), false)
return cmd
}

View File

@ -0,0 +1,53 @@
// Copyright © 2019 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 binding
import (
"errors"
"testing"
"gotest.tools/assert"
"knative.dev/client/pkg/sources/v1alpha1"
"knative.dev/client/pkg/util"
)
func TestSimpleDelete(t *testing.T) {
bindingClient := v1alpha1.NewMockKnSinkBindingClient(t, "mynamespace")
bindingRecorder := bindingClient.Recorder()
bindingRecorder.DeleteSinkBinding("mybinding", nil)
out, err := executeSinkBindingCommand(bindingClient, nil, "delete", "mybinding")
assert.NilError(t, err)
util.ContainsAll(out, "deleted", "mynamespace", "mybinding", "sink binding")
bindingRecorder.Validate()
}
func TestDeleteWithError(t *testing.T) {
bindingClient := v1alpha1.NewMockKnSinkBindingClient(t, "mynamespace")
bindingRecorder := bindingClient.Recorder()
bindingRecorder.DeleteSinkBinding("mybinding", errors.New("no such sink binding mybinding"))
out, err := executeSinkBindingCommand(bindingClient, nil, "delete", "mybinding")
assert.ErrorContains(t, err, "mybinding")
util.ContainsAll(out, "no such", "mybinding")
bindingRecorder.Validate()
}

View File

@ -0,0 +1,146 @@
// Copyright © 2019 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 binding
import (
"errors"
"fmt"
"sort"
"github.com/spf13/cobra"
v1alpha12 "knative.dev/eventing/pkg/apis/sources/v1alpha1"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/pkg/tracker"
"knative.dev/client/pkg/kn/commands"
"knative.dev/client/pkg/printers"
)
// NewBindingDescribeCommand returns a new command for describe a sink binding object
func NewBindingDescribeCommand(p *commands.KnParams) *cobra.Command {
cmd := &cobra.Command{
Use: "describe NAME",
Short: "Describe a sink binding.",
Example: `
# Describe a sink binding with name 'mysinkbinding'
kn source binding describe mysinkbinding`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("'kn source binding describe' requires the name of the source as single argument")
}
name := args[0]
bindingClient, err := newSinkBindingClient(p, cmd)
if err != nil {
return err
}
binding, err := bindingClient.GetSinkBinding(name)
if err != nil {
return err
}
out := cmd.OutOrStdout()
dw := printers.NewPrefixWriter(out)
printDetails, err := cmd.Flags().GetBool("verbose")
if err != nil {
return err
}
writeSinkBinding(dw, binding, printDetails)
dw.WriteLine()
if err := dw.Flush(); err != nil {
return err
}
// Condition info
commands.WriteConditions(dw, binding.Status.Conditions, printDetails)
if err := dw.Flush(); err != nil {
return err
}
return nil
},
}
flags := cmd.Flags()
commands.AddNamespaceFlags(flags, false)
flags.BoolP("verbose", "v", false, "More output.")
return cmd
}
func writeSinkBinding(dw printers.PrefixWriter, binding *v1alpha12.SinkBinding, printDetails bool) {
commands.WriteMetadata(dw, &binding.ObjectMeta, printDetails)
writeSubject(dw, binding.Namespace, &binding.Spec.Subject)
writeSink(dw, binding.Namespace, &binding.Spec.Sink)
if binding.Spec.CloudEventOverrides != nil && binding.Spec.CloudEventOverrides.Extensions != nil {
writeCeOverrides(dw, binding.Spec.CloudEventOverrides.Extensions)
}
}
func writeSink(dw printers.PrefixWriter, namespace string, sink *duckv1.Destination) {
subWriter := dw.WriteAttribute("Sink", "")
if sink.Ref.Namespace != "" && sink.Ref.Namespace != namespace {
subWriter.WriteAttribute("Namespace", sink.Ref.Namespace)
}
subWriter.WriteAttribute("Name", sink.Ref.Name)
ref := sink.Ref
if ref != nil {
subWriter.WriteAttribute("Resource", fmt.Sprintf("%s (%s)", sink.Ref.Kind, sink.Ref.APIVersion))
}
uri := sink.URI
if uri != nil {
subWriter.WriteAttribute("URI", uri.String())
}
}
func writeCeOverrides(dw printers.PrefixWriter, ceOverrides map[string]string) {
subDw := dw.WriteAttribute("CloudEvent Overrides", "")
var keys []string
for k := range ceOverrides {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
subDw.WriteAttribute(k, ceOverrides[k])
}
}
func writeSubject(dw printers.PrefixWriter, namespace string, subject *tracker.Reference) {
subjectDw := dw.WriteAttribute("Subject", "")
if subject.Namespace != "" && subject.Namespace != namespace {
subjectDw.WriteAttribute("Namespace", subject.Namespace)
}
if subject.Name != "" {
subjectDw.WriteAttribute("Name", subject.Name)
}
subjectDw.WriteAttribute("Resource", fmt.Sprintf("%s (%s)", subject.Kind, subject.APIVersion))
if subject.Selector != nil {
matchDw := subjectDw.WriteAttribute("Selector", "")
selector := subject.Selector
if len(selector.MatchLabels) > 0 {
var lKeys []string
for k := range selector.MatchLabels {
lKeys = append(lKeys, k)
}
sort.Strings(lKeys)
for _, k := range lKeys {
matchDw.WriteAttribute(k, selector.MatchLabels[k])
}
}
// TOOD: Print out selector.MatchExpressions
}
}

View File

@ -0,0 +1,113 @@
// Copyright © 2019 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 binding
import (
"errors"
"strings"
"testing"
"gotest.tools/assert"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1alpha14 "knative.dev/eventing/pkg/apis/sources/v1alpha1"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/pkg/apis/duck/v1alpha1"
"knative.dev/pkg/tracker"
v1alpha13 "knative.dev/client/pkg/sources/v1alpha1"
"knative.dev/client/pkg/util"
)
func TestSimpleDescribeWitName(t *testing.T) {
bindingClient := v1alpha13.NewMockKnSinkBindingClient(t, "mynamespace")
bindingRecorder := bindingClient.Recorder()
bindingRecorder.GetSinkBinding("mybinding", getSinkBindingSource("myapp", map[string]string{"foo": "bar"}), nil)
out, err := executeSinkBindingCommand(bindingClient, nil, "describe", "mybinding")
assert.NilError(t, err)
util.ContainsAll(out, "mybinding", "myapp", "Deployment", "app/v1", "mynamespace", "mysvc", "foo", "bar")
bindingRecorder.Validate()
}
func TestSimpleDescribeWithSelector(t *testing.T) {
bindingClient := v1alpha13.NewMockKnSinkBindingClient(t, "mynamespace")
bindingRecorder := bindingClient.Recorder()
bindingRecorder.GetSinkBinding("mybinding", getSinkBindingSource("app=myapp,type=test", nil), nil)
out, err := executeSinkBindingCommand(bindingClient, nil, "describe", "mybinding")
assert.NilError(t, err)
util.ContainsAll(out, "mybinding", "app:", "myapp", "type:", "test", "Deployment", "app/v1", "mynamespace", "mysvc")
bindingRecorder.Validate()
}
func TestDescribeError(t *testing.T) {
bindingClient := v1alpha13.NewMockKnSinkBindingClient(t, "mynamespace")
bindingRecorder := bindingClient.Recorder()
bindingRecorder.GetSinkBinding("mybinding", nil, errors.New("no sink binding mybinding found"))
out, err := executeSinkBindingCommand(bindingClient, nil, "describe", "mybinding")
assert.ErrorContains(t, err, "mybinding")
util.ContainsAll(out, "mybinding")
bindingRecorder.Validate()
}
func getSinkBindingSource(nameOrSelector string, ceOverrides map[string]string) *v1alpha14.SinkBinding {
binding := &v1alpha14.SinkBinding{
TypeMeta: v1.TypeMeta{},
ObjectMeta: v1.ObjectMeta{
Name: "mysinkbinding",
},
Spec: v1alpha14.SinkBindingSpec{
SourceSpec: duckv1.SourceSpec{
Sink: duckv1.Destination{
Ref: &corev1.ObjectReference{
Kind: "Service",
Namespace: "myservicenamespace",
Name: "mysvc",
},
},
},
BindingSpec: v1alpha1.BindingSpec{
Subject: tracker.Reference{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: "mynamespace",
},
},
},
Status: v1alpha14.SinkBindingStatus{},
}
if strings.Contains(nameOrSelector, "=") {
selector, _ := parseSelector(nameOrSelector)
binding.Spec.Subject.Selector = &v1.LabelSelector{
MatchLabels: selector,
}
} else {
binding.Spec.Subject.Name = nameOrSelector
}
if ceOverrides != nil {
binding.Spec.CloudEventOverrides = &duckv1.CloudEventOverrides{Extensions: ceOverrides}
}
return binding
}

View File

@ -0,0 +1,84 @@
// Copyright © 2019 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 binding
import (
"github.com/spf13/cobra"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"knative.dev/client/pkg/kn/commands"
hprinters "knative.dev/client/pkg/printers"
"knative.dev/eventing/pkg/apis/sources/v1alpha1"
)
type bindingUpdateFlags struct {
subject string
ceOverrides []string
}
func (b *bindingUpdateFlags) addBindingFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&b.subject, "subject", "", "Subject which emits cloud events. This argument takes format kind:apiVersion:name for named resources or kind:apiVersion:labelKey1=value1,labelKey2=value2 for matching via a label selector")
cmd.Flags().StringArrayVar(&b.ceOverrides, "ce-override", nil, "Cloud Event overrides to apply before sending event to sink in the format '--ce-override key=value'. --ce-override can be provide multiple times")
}
func BindingListHandlers(h hprinters.PrintHandler) {
sourceColumnDefinitions := []metav1beta1.TableColumnDefinition{
{Name: "Namespace", Type: "string", Description: "Namespace of the sink binding", Priority: 0},
{Name: "Name", Type: "string", Description: "Name of sink binding", Priority: 1},
{Name: "Subject", Type: "string", Description: "Subject part of binding", Priority: 1},
{Name: "Sink", Type: "string", Description: "Sink part of binding", Priority: 1},
{Name: "Conditions", Type: "string", Description: "Ready state conditions", Priority: 1},
{Name: "Ready", Type: "string", Description: "Ready state of the sink binding", Priority: 1},
{Name: "Reason", Type: "string", Description: "Reason if state is not Ready", Priority: 1},
}
h.TableHandler(sourceColumnDefinitions, printSinkBinding)
h.TableHandler(sourceColumnDefinitions, printSinkBindingList)
}
// printSinkBinding populates a single row of source sink binding list table
func printSinkBinding(binding *v1alpha1.SinkBinding, options hprinters.PrintOptions) ([]metav1beta1.TableRow, error) {
row := metav1beta1.TableRow{
Object: runtime.RawExtension{Object: binding},
}
name := binding.Name
subject := subjectToString(binding.Spec.Subject)
sink := sinkToString(binding.Spec.Sink)
conditions := commands.ConditionsValue(binding.Status.Conditions)
ready := commands.ReadyCondition(binding.Status.Conditions)
reason := commands.NonReadyConditionReason(binding.Status.Conditions)
if options.AllNamespaces {
row.Cells = append(row.Cells, binding.Namespace)
}
row.Cells = append(row.Cells, name, subject, sink, conditions, ready, reason)
return []metav1beta1.TableRow{row}, nil
}
func printSinkBindingList(sinkBindingList *v1alpha1.SinkBindingList, options hprinters.PrintOptions) ([]metav1beta1.TableRow, error) {
rows := make([]metav1beta1.TableRow, 0, len(sinkBindingList.Items))
for _, binding := range sinkBindingList.Items {
r, err := printSinkBinding(&binding, options)
if err != nil {
return nil, err
}
rows = append(rows, r...)
}
return rows, nil
}

View File

@ -0,0 +1,74 @@
// Copyright © 2019 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 binding
import (
"fmt"
"github.com/spf13/cobra"
"knative.dev/client/pkg/kn/commands"
"knative.dev/client/pkg/kn/commands/flags"
)
// NewBindingListCommand is for listing sink bindings
func NewBindingListCommand(p *commands.KnParams) *cobra.Command {
listFlags := flags.NewListPrintFlags(BindingListHandlers)
cmd := &cobra.Command{
Use: "list",
Short: "List sink bindings.",
Example: `
# List all sink binding in YAML format
kn source binding list -o yaml`,
RunE: func(cmd *cobra.Command, args []string) (err error) {
// TODO: filter list by given source name
bindingClient, err := newSinkBindingClient(p, cmd)
if err != nil {
return err
}
sourceList, err := bindingClient.ListSinkBindings()
if err != nil {
return err
}
if len(sourceList.Items) == 0 {
fmt.Fprintf(cmd.OutOrStdout(), "No sink binding found.\n")
return nil
}
if bindingClient.Namespace() == "" {
listFlags.EnsureWithNamespace()
}
printer, err := listFlags.ToPrinter()
if err != nil {
return nil
}
err = printer.PrintObj(sourceList, cmd.OutOrStdout())
if err != nil {
return err
}
return nil
},
}
commands.AddNamespaceFlags(cmd.Flags(), true)
listFlags.AddFlags(cmd)
return cmd
}

View File

@ -0,0 +1,60 @@
// Copyright © 2019 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 binding
import (
"testing"
"gotest.tools/assert"
v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1"
client_sources_v1alpha1 "knative.dev/client/pkg/sources/v1alpha1"
"knative.dev/client/pkg/util"
)
func TestListSimple(t *testing.T) {
bindingClient := client_sources_v1alpha1.NewMockKnSinkBindingClient(t)
bindingRecorder := bindingClient.Recorder()
binding := createSinkBinding("testbinding", "mysvc", deploymentGvk, "mydeploy", nil)
bindingList := v1alpha1.SinkBindingList{
Items: []v1alpha1.SinkBinding{
*binding,
},
}
bindingRecorder.ListSinkBindings(&bindingList, nil)
out, err := executeSinkBindingCommand(bindingClient, nil, "list")
assert.NilError(t, err, "Sources should be listed")
util.ContainsAll(out, "NAME", "SUBJECT", "SINK", "CONDITIONS", "READY", "REASON")
util.ContainsAll(out, "testbinding", "deployment:apps/v1:mydeploy", "mysvc")
bindingRecorder.Validate()
}
func TestListEmpty(t *testing.T) {
bindingClient := client_sources_v1alpha1.NewMockKnSinkBindingClient(t)
bindingRecorder := bindingClient.Recorder()
bindingList := v1alpha1.SinkBindingList{}
bindingRecorder.ListSinkBindings(&bindingList, nil)
out, err := executeSinkBindingCommand(bindingClient, nil, "list")
assert.NilError(t, err, "Sources should be listed")
util.ContainsNone(out, "NAME", "SUBJECT", "SINK", "CONDITIONS", "READY", "REASON")
util.ContainsAll(out, "No", "sink binding", "found")
bindingRecorder.Validate()
}

View File

@ -0,0 +1,101 @@
// Copyright © 2019 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 binding
import (
"errors"
"fmt"
"github.com/spf13/cobra"
"knative.dev/client/pkg/kn/commands"
"knative.dev/client/pkg/kn/commands/flags"
v1alpha12 "knative.dev/client/pkg/sources/v1alpha1"
)
// NewBindingUpdateCommand prepares the command for a sink binding update
func NewBindingUpdateCommand(p *commands.KnParams) *cobra.Command {
var bindingFlags bindingUpdateFlags
var sinkFlags flags.SinkFlags
cmd := &cobra.Command{
Use: "update NAME --subject SCHEDULE --sink SINK --ce-override OVERRIDE",
Short: "Update a sink binding.",
Example: `
# Update the subject of a sink binding 'my-binding' to a new cronjob with label selector 'app=ping'
kn source binding update my-binding --subject cronjob:batch/v1beta1:app=ping"`,
RunE: func(cmd *cobra.Command, args []string) (err error) {
if len(args) != 1 {
return errors.New("requires the name of the sink binding to update as single argument")
}
name := args[0]
sinkBindingClient, err := newSinkBindingClient(p, cmd)
if err != nil {
return err
}
namespace, err := p.GetNamespace(cmd)
if err != nil {
return err
}
dynamicClient, err := p.NewDynamicClient(namespace)
if err != nil {
return err
}
source, err := sinkBindingClient.GetSinkBinding(name)
if err != nil {
return err
}
b := v1alpha12.NewSinkBindingBuilderFromExisting(source)
if cmd.Flags().Changed("sink") {
destination, err := sinkFlags.ResolveSink(dynamicClient, namespace)
if err != nil {
return err
}
b.Sink(toDuckV1(destination))
}
if cmd.Flags().Changed("subject") {
reference, err := toReference(bindingFlags.subject, namespace)
if err != nil {
return err
}
b.Subject(reference)
}
err = updateCeOverrides(bindingFlags, b)
if err != nil {
return err
}
binding, err := b.Build()
if err != nil {
return err
}
err = sinkBindingClient.UpdateSinkBinding(binding)
if err == nil {
fmt.Fprintf(cmd.OutOrStdout(), "Sink binding '%s' updated in namespace '%s'.\n", name, sinkBindingClient.Namespace())
}
return err
},
}
commands.AddNamespaceFlags(cmd.Flags(), false)
bindingFlags.addBindingFlags(cmd)
sinkFlags.Add(cmd)
return cmd
}

View File

@ -0,0 +1,68 @@
// Copyright © 2019 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 binding
import (
"errors"
"testing"
"gotest.tools/assert"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
serving_v1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
dynamic_fake "knative.dev/client/pkg/dynamic/fake"
v1alpha13 "knative.dev/client/pkg/sources/v1alpha1"
"knative.dev/client/pkg/util"
)
func TestSimpleUpdate(t *testing.T) {
sinkBindingClient := v1alpha13.NewMockKnSinkBindingClient(t)
mysvc := createService("myscv")
othersvc := createService("othersvc")
dynamicClient := dynamic_fake.CreateFakeKnDynamicClient("default", mysvc, othersvc)
bindingRecorder := sinkBindingClient.Recorder()
ceOverrideMap := map[string]string{"bla": "blub", "foo": "bar"}
bindingRecorder.GetSinkBinding("testbinding", createSinkBinding("testbinding", "mysvc", deploymentGvk, "mydeploy", ceOverrideMap), nil)
bindingRecorder.UpdateSinkBinding(createSinkBinding("testbinding", "othersvc", deploymentGvk, "mydeploy", ceOverrideMap), nil)
out, err := executeSinkBindingCommand(sinkBindingClient, dynamicClient, "update", "testbinding", "--sink", "svc:othersvc", "--ce-override", "bla=blub", "--ce-override", "foo=bar")
assert.NilError(t, err)
util.ContainsAll(out, "updated", "default", "testbinding", "foo", "bar")
bindingRecorder.Validate()
}
func createService(name string) *serving_v1alpha1.Service {
mysvc := &serving_v1alpha1.Service{
TypeMeta: v1.TypeMeta{Kind: "Service", APIVersion: "serving.knative.dev/v1alpha1"},
ObjectMeta: v1.ObjectMeta{Name: name, Namespace: "default"},
}
return mysvc
}
func TestUpdateError(t *testing.T) {
sinkBindingClient := v1alpha13.NewMockKnSinkBindingClient(t)
bindingRecorder := sinkBindingClient.Recorder()
bindingRecorder.GetSinkBinding("testbinding", nil, errors.New("no such binding testbinding"))
out, err := executeSinkBindingCommand(sinkBindingClient, nil, "update", "testbinding")
assert.ErrorContains(t, err, "testbinding")
util.ContainsAll(out, "testbinding", "name", "required")
bindingRecorder.Validate()
}

View File

@ -84,7 +84,7 @@ func cleanupCronJobMockClient() {
func createCronJobSource(name, schedule, data, service string) *v1alpha1.CronJobSource {
sink := &v1beta1.Destination{
Ref: &corev1.ObjectReference{Name: service, Kind: "Service", Namespace: "default", APIVersion: "serving.knative.dev/v1alpha1"},
Ref: &corev1.ObjectReference{Name: service, Kind: "Service", APIVersion: "serving.knative.dev/v1alpha1", Namespace: "default"},
}
return source_client_v1alpha1.NewCronJobSourceBuilder(name).Schedule(schedule).Data(data).Sink(sink).Build()
}

View File

@ -24,7 +24,7 @@ import (
// NewCronJobDeleteCommand is for deleting a CronJob source
func NewCronJobDeleteCommand(p *commands.KnParams) *cobra.Command {
ApiServerDeleteCommand := &cobra.Command{
CronJobDeleteCommand := &cobra.Command{
Use: "delete NAME",
Short: "Delete a CronJob source.",
Example: `
@ -50,6 +50,6 @@ func NewCronJobDeleteCommand(p *commands.KnParams) *cobra.Command {
return nil
},
}
commands.AddNamespaceFlags(ApiServerDeleteCommand.Flags(), false)
return ApiServerDeleteCommand
commands.AddNamespaceFlags(CronJobDeleteCommand.Flags(), false)
return CronJobDeleteCommand
}

View File

@ -33,8 +33,8 @@ func NewCronJobDescribeCommand(p *commands.KnParams) *cobra.Command {
Use: "describe NAME",
Short: "Describe a CronJob source.",
Example: `
# Describe a cronjob source with name 'my-cron-trigger'
kn source cronjob describe my-cron-trigger`,
# Describe a cronjob source with name 'mycronjob'
kn source cronjob describe mycronjob`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("'source cronjob describe' requires the name of the source as single argument")

View File

@ -65,6 +65,9 @@ func printSource(source *v1alpha1.CronJobSource, options hprinters.PrintOptions)
ready := commands.ReadyCondition(source.Status.Conditions)
reason := commands.NonReadyConditionReason(source.Status.Conditions)
// Not moving to SinkToString() as it references v1beta1.Destination
// This source is going to be moved/removed soon to v1, so no need to move
// it now
var sink string
if source.Spec.Sink != nil {
if source.Spec.Sink.Ref != nil {

View File

@ -45,8 +45,8 @@ func TestSimpleUpdate(t *testing.T) {
Ref: &corev1.ObjectReference{
Kind: "Service",
Name: "mysvc",
Namespace: "default",
APIVersion: "serving.knative.dev/v1alpha1",
Namespace: "default",
},
},
},

View File

@ -21,6 +21,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
hprinters "knative.dev/client/pkg/printers"
)

View File

@ -19,6 +19,7 @@ import (
"knative.dev/client/pkg/kn/commands"
"knative.dev/client/pkg/kn/commands/source/apiserver"
"knative.dev/client/pkg/kn/commands/source/binding"
"knative.dev/client/pkg/kn/commands/source/cronjob"
)
@ -30,5 +31,6 @@ func NewSourceCommand(p *commands.KnParams) *cobra.Command {
sourceCmd.AddCommand(apiserver.NewAPIServerCommand(p))
sourceCmd.AddCommand(NewListTypesCommand(p))
sourceCmd.AddCommand(cronjob.NewCronJobCommand(p))
sourceCmd.AddCommand(binding.NewBindingCommand(p))
return sourceCmd
}

View File

@ -20,10 +20,11 @@ import (
"github.com/spf13/cobra"
duckv1 "knative.dev/pkg/apis/duck/v1"
client_v1alpha1 "knative.dev/client/pkg/eventing/v1alpha1"
"knative.dev/client/pkg/kn/commands"
"knative.dev/client/pkg/kn/commands/flags"
duckv1 "knative.dev/pkg/apis/duck/v1"
)
// NewTriggerCreateCommand to create trigger create command

View File

@ -20,10 +20,11 @@ import (
"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
serving_v1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
dynamic_fake "knative.dev/client/pkg/dynamic/fake"
eventing_client "knative.dev/client/pkg/eventing/v1alpha1"
"knative.dev/client/pkg/util"
serving_v1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
)
var (

View File

@ -19,6 +19,7 @@ import (
"testing"
"gotest.tools/assert"
eventing_client "knative.dev/client/pkg/eventing/v1alpha1"
"knative.dev/client/pkg/util"
)

View File

@ -23,10 +23,11 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
client_v1alpha1 "knative.dev/client/pkg/eventing/v1alpha1"
"knative.dev/client/pkg/util"
v1alpha1 "knative.dev/eventing/pkg/apis/eventing/v1alpha1"
duckv1 "knative.dev/pkg/apis/duck/v1"
client_v1alpha1 "knative.dev/client/pkg/eventing/v1alpha1"
"knative.dev/client/pkg/util"
)
func TestSimpleDescribe(t *testing.T) {

View File

@ -20,11 +20,12 @@ import (
"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"knative.dev/eventing/pkg/apis/eventing/v1alpha1"
serving_v1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
eventing_client "knative.dev/client/pkg/eventing/v1alpha1"
knserving_client "knative.dev/client/pkg/serving/v1alpha1"
"knative.dev/client/pkg/util"
"knative.dev/eventing/pkg/apis/eventing/v1alpha1"
serving_v1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
)
func TestTriggerList(t *testing.T) {

View File

@ -19,12 +19,13 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/tools/clientcmd"
kn_dynamic "knative.dev/client/pkg/dynamic"
eventc_v1alpha1 "knative.dev/client/pkg/eventing/v1alpha1"
"knative.dev/client/pkg/kn/commands"
"knative.dev/eventing/pkg/apis/eventing/v1alpha1"
"knative.dev/pkg/apis"
duckv1 "knative.dev/pkg/apis/duck/v1"
kn_dynamic "knative.dev/client/pkg/dynamic"
eventc_v1alpha1 "knative.dev/client/pkg/eventing/v1alpha1"
"knative.dev/client/pkg/kn/commands"
)
// Helper methods
@ -76,19 +77,12 @@ func executeTriggerCommand(triggerClient eventc_v1alpha1.KnEventingClient, dynam
}
func createTrigger(namespace string, name string, filters map[string]string, broker string, svcname string) *v1alpha1.Trigger {
triggerBuilder := eventc_v1alpha1.NewTriggerBuilder(name).
return eventc_v1alpha1.NewTriggerBuilder(name).
Namespace(namespace).
Broker(broker).
Filters(filters).
Subscriber(&duckv1.Destination{
Ref: &corev1.ObjectReference{
Name: svcname,
Kind: "Service",
Namespace: "default",
APIVersion: "serving.knative.dev/v1alpha1",
},
})
return triggerBuilder.Build()
Subscriber(createServiceSink(svcname)).
Build()
}
func createTriggerWithStatus(namespace string, name string, filters map[string]string, broker string, svcname string) *v1alpha1.Trigger {
@ -104,3 +98,9 @@ func createTriggerWithStatus(namespace string, name string, filters map[string]s
}
return wanted
}
func createServiceSink(service string) *duckv1.Destination {
return &duckv1.Destination{
Ref: &corev1.ObjectReference{Name: service, Kind: "Service", APIVersion: "serving.knative.dev/v1alpha1", Namespace: "default"},
}
}

View File

@ -20,15 +20,16 @@ import (
"github.com/spf13/cobra"
"knative.dev/eventing/pkg/apis/eventing/v1alpha1"
duckv1 "knative.dev/pkg/apis/duck/v1"
client_v1alpha1 "knative.dev/client/pkg/eventing/v1alpha1"
"knative.dev/client/pkg/kn/commands"
"knative.dev/client/pkg/kn/commands/flags"
"knative.dev/client/pkg/util"
"knative.dev/eventing/pkg/apis/eventing/v1alpha1"
duckv1 "knative.dev/pkg/apis/duck/v1"
)
// NewTriggerUpdateCommand prepares the command for a CronJobSource update
// NewTriggerUpdateCommand prepares the command for a tigger update
func NewTriggerUpdateCommand(p *commands.KnParams) *cobra.Command {
var triggerUpdateFlags TriggerUpdateFlags
var sinkFlags flags.SinkFlags

View File

@ -20,10 +20,11 @@ import (
"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
serving_v1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
dynamic_fake "knative.dev/client/pkg/dynamic/fake"
eventing_client "knative.dev/client/pkg/eventing/v1alpha1"
"knative.dev/client/pkg/util"
serving_v1alpha1 "knative.dev/serving/pkg/apis/serving/v1alpha1"
)
func TestTriggerUpdate(t *testing.T) {

View File

@ -23,11 +23,12 @@ import (
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"knative.dev/client/pkg/util"
eventing "knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1alpha1"
eventing_sources "knative.dev/eventing/pkg/legacyclient/clientset/versioned/typed/legacysources/v1alpha1"
serving_v1alpha1_client "knative.dev/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1"
"knative.dev/client/pkg/util"
dynamic_kn "knative.dev/client/pkg/dynamic"
sources_kn_v1alpha1 "knative.dev/client/pkg/eventing/legacysources/v1alpha1"
eventing_kn_v1alpha1 "knative.dev/client/pkg/eventing/v1alpha1"

View File

@ -0,0 +1,263 @@
// Copyright © 2019 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 (
"fmt"
apis_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"knative.dev/eventing/pkg/apis/sources/v1alpha1"
"knative.dev/eventing/pkg/client/clientset/versioned/scheme"
client_v1alpha1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1"
duckv1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/pkg/tracker"
kn_errors "knative.dev/client/pkg/errors"
"knative.dev/client/pkg/util"
)
// KnSinkBindingClient to Eventing Sources. All methods are relative to the
// namespace specified during construction
type KnSinkBindingClient interface {
// Namespace in which this client is operating for
Namespace() string
// CreateSinkBinding is used to create an instance of binding
CreateSinkBinding(binding *v1alpha1.SinkBinding) error
// DeleteSinkBinding is used to delete an instance of binding
DeleteSinkBinding(name string) error
// GetSinkBinding is used to get an instance of binding
GetSinkBinding(name string) (*v1alpha1.SinkBinding, error)
// ListSinkBinding returns list of binding CRDs
ListSinkBindings() (*v1alpha1.SinkBindingList, error)
// UpdateSinkBinding is used to update an instance of binding
UpdateSinkBinding(binding *v1alpha1.SinkBinding) error
}
// KnSinkBindingClient is a combination of Sources client interface and namespace
// Temporarily help to add sources dependencies
// May be changed when adding real sources features
type knBindingClient struct {
client client_v1alpha1.SinkBindingInterface
namespace string
}
// NewKnSourcesClient is to invoke Eventing Sources Client API to create object
func newKnSinkBindingClient(client client_v1alpha1.SinkBindingInterface, namespace string) KnSinkBindingClient {
return &knBindingClient{
client: client,
namespace: namespace,
}
}
//CreateSinkBinding is used to create an instance of binding
func (c *knBindingClient) CreateSinkBinding(binding *v1alpha1.SinkBinding) error {
binding, err := c.client.Create(binding)
if err != nil {
return kn_errors.GetError(err)
}
return nil
}
//DeleteSinkBinding is used to delete an instance of binding
func (c *knBindingClient) DeleteSinkBinding(name string) error {
err := c.client.Delete(name, &apis_v1.DeleteOptions{})
if err != nil {
return kn_errors.GetError(err)
}
return nil
}
//GetSinkBinding is used to get an instance of binding
func (c *knBindingClient) GetSinkBinding(name string) (*v1alpha1.SinkBinding, error) {
binding, err := c.client.Get(name, apis_v1.GetOptions{})
if err != nil {
return nil, kn_errors.GetError(err)
}
return binding, nil
}
func (c *knBindingClient) ListSinkBindings() (*v1alpha1.SinkBindingList, error) {
bindingList, err := c.client.List(apis_v1.ListOptions{})
if err != nil {
return nil, kn_errors.GetError(err)
}
bindingListNew := bindingList.DeepCopy()
err = updateSinkBindingGvk(bindingListNew)
if err != nil {
return nil, err
}
bindingListNew.Items = make([]v1alpha1.SinkBinding, len(bindingList.Items))
for idx, binding := range bindingList.Items {
bindingClone := binding.DeepCopy()
err := updateSinkBindingGvk(bindingClone)
if err != nil {
return nil, err
}
bindingListNew.Items[idx] = *bindingClone
}
return bindingListNew, nil
}
//CreateSinkBinding is used to create an instance of binding
func (c *knBindingClient) UpdateSinkBinding(binding *v1alpha1.SinkBinding) error {
binding, err := c.client.Update(binding)
if err != nil {
return kn_errors.GetError(err)
}
return nil
}
// Return the client's namespace
func (c *knBindingClient) Namespace() string {
return c.namespace
}
// update with the v1alpha1 group + version
func updateSinkBindingGvk(obj runtime.Object) error {
return util.UpdateGroupVersionKindWithScheme(obj, v1alpha1.SchemeGroupVersion, scheme.Scheme)
}
// SinkBindingBuilder is for building the binding
type SinkBindingBuilder struct {
binding *v1alpha1.SinkBinding
sGvk *schema.GroupVersionKind
sName string
sLabelSelector map[string]string
sNamespace string
// When set directly:
subject *tracker.Reference
}
// NewSinkBindingBuilder for building binding object
func NewSinkBindingBuilder(name string) *SinkBindingBuilder {
return &SinkBindingBuilder{binding: &v1alpha1.SinkBinding{
ObjectMeta: meta_v1.ObjectMeta{
Name: name,
},
}}
}
// NewSinkBindingBuilderFromExisting for building the object from existing SinkBinding object
func NewSinkBindingBuilderFromExisting(binding *v1alpha1.SinkBinding) *SinkBindingBuilder {
return &SinkBindingBuilder{binding: binding.DeepCopy()}
}
// Namespace for this binding
func (b *SinkBindingBuilder) Namespace(ns string) *SinkBindingBuilder {
b.binding.Namespace = ns
return b
}
// Subscriber for the binding to send to (it's a Sink actually)
func (b *SinkBindingBuilder) Subject(subject *tracker.Reference) *SinkBindingBuilder {
b.subject = subject
return b
}
// Add a GVK of the subject
func (b *SinkBindingBuilder) SubjectGVK(gvk *schema.GroupVersionKind) *SinkBindingBuilder {
b.sGvk = gvk
return b
}
// Add a subject name for building up the name
func (b *SinkBindingBuilder) SubjectName(name string) *SinkBindingBuilder {
b.sName = name
return b
}
// Add a subject namespace for building up the name
func (b *SinkBindingBuilder) SubjectNamespace(ns string) *SinkBindingBuilder {
b.sNamespace = ns
return b
}
// Add a label match part for building up the subject
func (b *SinkBindingBuilder) AddSubjectMatchLabel(labelKey, labelValue string) *SinkBindingBuilder {
if b.sLabelSelector == nil {
b.sLabelSelector = map[string]string{}
}
b.sLabelSelector[labelKey] = labelValue
return b
}
// Broker to set the broker of binding object
func (b *SinkBindingBuilder) Sink(sink *duckv1.Destination) *SinkBindingBuilder {
b.binding.Spec.Sink = *sink
return b
}
func (b *SinkBindingBuilder) AddCloudEventOverrides(ceo map[string]string) *SinkBindingBuilder {
ceOverrides := b.binding.Spec.CloudEventOverrides
if ceOverrides == nil {
ceOverrides = &duckv1.CloudEventOverrides{Extensions: map[string]string{}}
b.binding.Spec.CloudEventOverrides = ceOverrides
}
for k, v := range ceo {
ceOverrides.Extensions[k] = v
}
return b
}
// Build to return an instance of binding object
func (b *SinkBindingBuilder) Build() (*v1alpha1.SinkBinding, error) {
// If set directly, return the sink binding directly
if b.subject != nil {
b.binding.Spec.Subject = *b.subject
return b.binding, nil
}
if b.sGvk == nil && b.sName == "" && b.sLabelSelector == nil {
// None of the subject methods has been called, so no subject build up
return b.binding, nil
}
// Otherwise, validate and build up the subject
if b.sGvk == nil {
return nil, fmt.Errorf("no group-version-kind provided for creating binding %s", b.binding.Name)
}
if b.sName != "" && b.sLabelSelector != nil {
return nil, fmt.Errorf("either a subject name or label selector can be used for creating binding %s, but not both (subject name: %s, label selector: %v", b.binding.Name, b.sName, b.sLabelSelector)
}
subject := b.prepareBaseSubject()
if b.sName != "" {
subject.Name = b.sName
} else {
subject.Selector = &meta_v1.LabelSelector{
MatchLabels: b.sLabelSelector,
}
}
b.binding.Spec.Subject = subject
return b.binding, nil
}
func (b *SinkBindingBuilder) prepareBaseSubject() tracker.Reference {
subject := tracker.Reference{
APIVersion: b.sGvk.GroupVersion().String(),
Kind: b.sGvk.Kind,
}
if b.sNamespace != "" {
subject.Namespace = b.sNamespace
}
return subject
}

View File

@ -0,0 +1,120 @@
// Copyright © 2019 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"
"knative.dev/eventing/pkg/apis/sources/v1alpha1"
"knative.dev/client/pkg/util/mock"
)
// MockKnSinkBindingClient is a combine of test object and recorder
type MockKnSinkBindingClient struct {
t *testing.T
recorder *EventingRecorder
namespace string
}
// NewMockKnSinkBindingClient returns a new mock instance which you need to record for
func NewMockKnSinkBindingClient(t *testing.T, ns ...string) *MockKnSinkBindingClient {
namespace := "default"
if len(ns) > 0 {
namespace = ns[0]
}
return &MockKnSinkBindingClient{
t: t,
recorder: &EventingRecorder{mock.NewRecorder(t, namespace)},
}
}
// Ensure that the interface is implemented
var _ KnSinkBindingClient = &MockKnSinkBindingClient{}
// EventingRecorder is recorder for eventing objects
type EventingRecorder struct {
r *mock.Recorder
}
// Recorder returns the recorder for registering API calls
func (c *MockKnSinkBindingClient) Recorder() *EventingRecorder {
return c.recorder
}
// Namespace of this client
func (c *MockKnSinkBindingClient) Namespace() string {
return c.recorder.r.Namespace()
}
// CreateSinkBinding records a call for CreateSinkBinding with the expected error
func (sr *EventingRecorder) CreateSinkBinding(binding interface{}, err error) {
sr.r.Add("CreateSinkBinding", []interface{}{binding}, []interface{}{err})
}
// CreateSinkBinding performs a previously recorded action
func (c *MockKnSinkBindingClient) CreateSinkBinding(binding *v1alpha1.SinkBinding) error {
call := c.recorder.r.VerifyCall("CreateSinkBinding", binding)
return mock.ErrorOrNil(call.Result[0])
}
// GetSinkBinding records a call for GetSinkBinding with the expected object or error. Either binding or err should be nil
func (sr *EventingRecorder) GetSinkBinding(name interface{}, binding *v1alpha1.SinkBinding, err error) {
sr.r.Add("GetSinkBinding", []interface{}{name}, []interface{}{binding, err})
}
// GetSinkBinding performs a previously recorded action
func (c *MockKnSinkBindingClient) GetSinkBinding(name string) (*v1alpha1.SinkBinding, error) {
call := c.recorder.r.VerifyCall("GetSinkBinding", name)
return call.Result[0].(*v1alpha1.SinkBinding), mock.ErrorOrNil(call.Result[1])
}
// DeleteSinkBinding records a call for DeleteSinkBinding with the expected error (nil if none)
func (sr *EventingRecorder) DeleteSinkBinding(name interface{}, err error) {
sr.r.Add("DeleteSinkBinding", []interface{}{name}, []interface{}{err})
}
// DeleteSinkBinding performs a previously recorded action, failing if non has been registered
func (c *MockKnSinkBindingClient) DeleteSinkBinding(name string) error {
call := c.recorder.r.VerifyCall("DeleteSinkBinding", name)
return mock.ErrorOrNil(call.Result[0])
}
// ListSinkBindings records a call for ListSinkBindings with the expected result and error (nil if none)
func (sr *EventingRecorder) ListSinkBindings(bindingList *v1alpha1.SinkBindingList, err error) {
sr.r.Add("ListSinkBindings", nil, []interface{}{bindingList, err})
}
// ListSinkBindings performs a previously recorded action
func (c *MockKnSinkBindingClient) ListSinkBindings() (*v1alpha1.SinkBindingList, error) {
call := c.recorder.r.VerifyCall("ListSinkBindings")
return call.Result[0].(*v1alpha1.SinkBindingList), mock.ErrorOrNil(call.Result[1])
}
// UpdateSinkBinding records a call for ListSinkBindings with the expected result and error (nil if none)
func (sr *EventingRecorder) UpdateSinkBinding(binding interface{}, err error) {
sr.r.Add("UpdateSinkBinding", []interface{}{binding}, []interface{}{err})
}
// UpdateSinkBinding performs a previously recorded action
func (c *MockKnSinkBindingClient) UpdateSinkBinding(binding *v1alpha1.SinkBinding) error {
call := c.recorder.r.VerifyCall("UpdateSinkBinding")
return mock.ErrorOrNil(call.Result[0])
}
// Validate validates whether every recorded action has been called
func (sr *EventingRecorder) Validate() {
sr.r.CheckThatAllRecordedMethodsHaveBeenCalled()
}

View File

@ -0,0 +1,44 @@
// Copyright © 2019 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"
"knative.dev/eventing/pkg/apis/sources/v1alpha1"
)
func TestMockKnClient(t *testing.T) {
client := NewMockKnSinkBindingClient(t)
recorder := client.Recorder()
// Record all services
recorder.GetSinkBinding("hello", nil, nil)
recorder.CreateSinkBinding(&v1alpha1.SinkBinding{}, nil)
recorder.DeleteSinkBinding("hello", nil)
recorder.ListSinkBindings(nil, nil)
recorder.UpdateSinkBinding(&v1alpha1.SinkBinding{}, nil)
// Call all service
client.GetSinkBinding("hello")
client.CreateSinkBinding(&v1alpha1.SinkBinding{})
client.DeleteSinkBinding("hello")
client.ListSinkBindings()
client.UpdateSinkBinding(&v1alpha1.SinkBinding{})
// Validate
recorder.Validate()
}

View File

@ -0,0 +1,253 @@
// Copyright © 2019 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 (
"fmt"
"testing"
"gotest.tools/assert"
v12 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
client_testing "k8s.io/client-go/testing"
"knative.dev/eventing/pkg/apis/sources/v1alpha1"
"knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake"
v1 "knative.dev/pkg/apis/duck/v1"
"knative.dev/pkg/tracker"
)
var testNamespace = "test-ns"
func setup() (fakeSvr fake.FakeSourcesV1alpha1, client KnSinkBindingClient) {
fakeE := fake.FakeSourcesV1alpha1{Fake: &client_testing.Fake{}}
cli := NewKnSourcesClient(&fakeE, "test-ns").SinkBindingClient()
return fakeE, cli
}
func TestDeleteSinkBinding(t *testing.T) {
var name = "new-binding"
server, client := setup()
server.AddReactor("delete", "sinkbindings",
func(a client_testing.Action) (bool, runtime.Object, error) {
name := a.(client_testing.DeleteAction).GetName()
if name == "errorSinkBinding" {
return true, nil, fmt.Errorf("error while deleting binding %s", name)
}
return true, nil, nil
})
err := client.DeleteSinkBinding(name)
assert.NilError(t, err)
err = client.DeleteSinkBinding("errorSinkBinding")
assert.ErrorContains(t, err, "errorSinkBinding")
}
func TestCreateSinkBinding(t *testing.T) {
var name = "new-binding"
server, client := setup()
objNew := newSinkBinding(name, "mysvc", "mycronjob")
server.AddReactor("create", "sinkbindings",
func(a client_testing.Action) (bool, runtime.Object, error) {
assert.Equal(t, testNamespace, a.GetNamespace())
name := a.(client_testing.CreateAction).GetObject().(metav1.Object).GetName()
if name == objNew.Name {
objNew.Generation = 2
return true, objNew, nil
}
return true, nil, fmt.Errorf("error while creating binding %s", name)
})
t.Run("create binding without error", func(t *testing.T) {
err := client.CreateSinkBinding(objNew)
assert.NilError(t, err)
})
t.Run("create binding with an error returns an error object", func(t *testing.T) {
err := client.CreateSinkBinding(newSinkBinding("unknown", "mysvc", "mycronjobs"))
assert.ErrorContains(t, err, "unknown")
})
}
func TestGetSinkBinding(t *testing.T) {
var name = "mysinkbinding"
server, client := setup()
server.AddReactor("get", "sinkbindings",
func(a client_testing.Action) (bool, runtime.Object, error) {
name := a.(client_testing.GetAction).GetName()
if name == "errorSinkBinding" {
return true, nil, fmt.Errorf("error while getting binding %s", name)
}
return true, newSinkBinding(name, "mysvc", "mycronjob"), nil
})
binding, err := client.GetSinkBinding(name)
assert.NilError(t, err)
assert.Equal(t, binding.Name, name)
assert.Equal(t, binding.Spec.Sink.Ref.Name, "mysvc")
assert.Equal(t, binding.Spec.Subject.Name, "mycronjob")
_, err = client.GetSinkBinding("errorSinkBinding")
assert.ErrorContains(t, err, "errorSinkBinding")
}
func TestListSinkBinding(t *testing.T) {
serving, client := setup()
t.Run("list binding returns a list of sink-bindings", func(t *testing.T) {
binding1 := newSinkBinding("binding-1", "mysvc-1", "mycronjob")
binding2 := newSinkBinding("binding-2", "mysvc-2", "mycronjob")
serving.AddReactor("list", "sinkbindings",
func(a client_testing.Action) (bool, runtime.Object, error) {
assert.Equal(t, testNamespace, a.GetNamespace())
return true, &v1alpha1.SinkBindingList{Items: []v1alpha1.SinkBinding{*binding1, *binding2}}, nil
})
listSinkBindings, err := client.ListSinkBindings()
assert.NilError(t, err)
assert.Assert(t, len(listSinkBindings.Items) == 2)
assert.Equal(t, listSinkBindings.Items[0].Name, "binding-1")
assert.Equal(t, listSinkBindings.Items[0].Spec.Sink.Ref.Name, "mysvc-1")
assert.Equal(t, listSinkBindings.Items[0].Spec.Subject.Name, "mycronjob")
assert.Equal(t, listSinkBindings.Items[1].Name, "binding-2")
assert.Equal(t, listSinkBindings.Items[1].Spec.Sink.Ref.Name, "mysvc-2")
assert.Equal(t, listSinkBindings.Items[1].Spec.Subject.Name, "mycronjob")
})
}
func TestSinkBindingBuilderAddCloudEventOverrides(t *testing.T) {
aBuilder := NewSinkBindingBuilder("testsinkbinding")
aBuilder.AddCloudEventOverrides(map[string]string{"type": "foo"})
a, err := aBuilder.Build()
assert.NilError(t, err)
t.Run("update bindings", func(t *testing.T) {
bBuilder := NewSinkBindingBuilderFromExisting(a)
b, err := bBuilder.Build()
assert.NilError(t, err)
assert.DeepEqual(t, b, a)
bBuilder.AddCloudEventOverrides(map[string]string{"type": "new"})
expected := &v1.CloudEventOverrides{
Extensions: map[string]string{
"type": "new",
},
}
assert.DeepEqual(t, expected, b.Spec.CloudEventOverrides)
})
t.Run("update bindings with both new entry and old entry", func(t *testing.T) {
bBuilder := NewSinkBindingBuilderFromExisting(a)
b, err := bBuilder.Build()
assert.NilError(t, err)
assert.DeepEqual(t, b, a)
bBuilder.AddCloudEventOverrides(map[string]string{"source": "bar"})
expected := &v1.CloudEventOverrides{
Extensions: map[string]string{
"type": "foo",
"source": "bar",
},
}
assert.DeepEqual(t, expected, b.Spec.CloudEventOverrides)
})
}
func TestSinkBindingBuilderForSubjectError(t *testing.T) {
b, err := NewSinkBindingBuilder("test").SubjectName("bla").Build()
assert.Assert(t, b == nil)
assert.ErrorContains(t, err, "group")
assert.ErrorContains(t, err, "version")
assert.ErrorContains(t, err, "kind")
b, err = NewSinkBindingBuilder("test").
SubjectGVK(&schema.GroupVersionKind{"apps", "v1", "Deployment"}).
SubjectName("foo").
AddSubjectMatchLabel("bla", "blub").
Build()
assert.ErrorContains(t, err, "label selector")
assert.ErrorContains(t, err, "name")
assert.ErrorContains(t, err, "subject")
assert.Assert(t, b == nil)
}
func TestSinkBindingBuilderForSubject(t *testing.T) {
gvk := schema.GroupVersionKind{"apps", "v1", "Deployment"}
b, err := NewSinkBindingBuilder("test").
SubjectGVK(&gvk).
SubjectName("foo").
Build()
assert.NilError(t, err)
subject := b.Spec.Subject
assert.Equal(t, subject.Name, "foo")
assert.Assert(t, subject.Selector == nil)
assert.DeepEqual(t, subject.GroupVersionKind(), gvk)
b, err = NewSinkBindingBuilder("test").
SubjectGVK(&schema.GroupVersionKind{"apps", "v1", "Deployment"}).
AddSubjectMatchLabel("bla", "blub").
AddSubjectMatchLabel("foo", "bar").
Build()
assert.NilError(t, err)
subject = b.Spec.Subject
assert.Equal(t, subject.Name, "")
assert.DeepEqual(t, subject.GroupVersionKind(), gvk)
selector := map[string]string{
"bla": "blub",
"foo": "bar",
}
assert.DeepEqual(t, subject.Selector.MatchLabels, selector)
}
func TestSinkBindingBuilderForSubjectDirect(t *testing.T) {
subject := tracker.Reference{
Name: "direct",
}
b, err := NewSinkBindingBuilder("test").
Subject(&subject).
SubjectName("nope"). // should be ignored
AddSubjectMatchLabel("bla", "blub"). // should be ignored
Build()
assert.NilError(t, err)
subject = b.Spec.Subject
assert.Equal(t, subject.Name, "direct")
assert.Assert(t, subject.Selector == nil)
}
func newSinkBinding(name, sinkService, cronJobName string) *v1alpha1.SinkBinding {
sink := &v1.Destination{
Ref: &v12.ObjectReference{Name: sinkService, Kind: "Service", Namespace: "default", APIVersion: "serving.knative.dev/v1alpha1"},
}
b, _ := NewSinkBindingBuilder(name).
Namespace(testNamespace).
Sink(sink).
SubjectGVK(&schema.GroupVersionKind{"batch", "v1beta1", "CronJob"}).
SubjectName(cronJobName).
AddCloudEventOverrides(map[string]string{"type": "foo"}).
Build()
return b
}

View File

@ -0,0 +1,47 @@
// Copyright © 2019 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 (
client_v1alpha1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1"
)
// KnSinkBindingClient to Eventing Sources. All methods are relative to the
// namespace specified during construction
type KnSourcesClient interface {
// Get client for sink binding sources
SinkBindingClient() KnSinkBindingClient
}
// sourcesClient is a combination of Sources client interface and namespace
// Temporarily help to add sources dependencies
// May be changed when adding real sources features
type sourcesClient struct {
client client_v1alpha1.SourcesV1alpha1Interface
namespace string
}
// NewKnSourcesClient for managing all eventing built-in sources
func NewKnSourcesClient(client client_v1alpha1.SourcesV1alpha1Interface, namespace string) KnSourcesClient {
return &sourcesClient{
client: client,
namespace: namespace,
}
}
// ApiServerSourcesClient for dealing with ApiServer sources
func (c *sourcesClient) SinkBindingClient() KnSinkBindingClient {
return newKnSinkBindingClient(c.client.SinkBindings(c.namespace), c.namespace)
}

View File

@ -0,0 +1,191 @@
/*
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
import (
"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"
)
// ApiServerSourcesGetter has a method to return a ApiServerSourceInterface.
// A group's client should implement this interface.
type ApiServerSourcesGetter interface {
ApiServerSources(namespace string) ApiServerSourceInterface
}
// ApiServerSourceInterface has methods to work with ApiServerSource resources.
type ApiServerSourceInterface interface {
Create(*v1alpha1.ApiServerSource) (*v1alpha1.ApiServerSource, error)
Update(*v1alpha1.ApiServerSource) (*v1alpha1.ApiServerSource, error)
UpdateStatus(*v1alpha1.ApiServerSource) (*v1alpha1.ApiServerSource, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1alpha1.ApiServerSource, error)
List(opts v1.ListOptions) (*v1alpha1.ApiServerSourceList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ApiServerSource, err error)
ApiServerSourceExpansion
}
// apiServerSources implements ApiServerSourceInterface
type apiServerSources struct {
client rest.Interface
ns string
}
// newApiServerSources returns a ApiServerSources
func newApiServerSources(c *SourcesV1alpha1Client, namespace string) *apiServerSources {
return &apiServerSources{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the apiServerSource, and returns the corresponding apiServerSource object, and an error if there is any.
func (c *apiServerSources) Get(name string, options v1.GetOptions) (result *v1alpha1.ApiServerSource, err error) {
result = &v1alpha1.ApiServerSource{}
err = c.client.Get().
Namespace(c.ns).
Resource("apiserversources").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of ApiServerSources that match those selectors.
func (c *apiServerSources) List(opts v1.ListOptions) (result *v1alpha1.ApiServerSourceList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha1.ApiServerSourceList{}
err = c.client.Get().
Namespace(c.ns).
Resource("apiserversources").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested apiServerSources.
func (c *apiServerSources) Watch(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("apiserversources").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a apiServerSource and creates it. Returns the server's representation of the apiServerSource, and an error, if there is any.
func (c *apiServerSources) Create(apiServerSource *v1alpha1.ApiServerSource) (result *v1alpha1.ApiServerSource, err error) {
result = &v1alpha1.ApiServerSource{}
err = c.client.Post().
Namespace(c.ns).
Resource("apiserversources").
Body(apiServerSource).
Do().
Into(result)
return
}
// Update takes the representation of a apiServerSource and updates it. Returns the server's representation of the apiServerSource, and an error, if there is any.
func (c *apiServerSources) Update(apiServerSource *v1alpha1.ApiServerSource) (result *v1alpha1.ApiServerSource, err error) {
result = &v1alpha1.ApiServerSource{}
err = c.client.Put().
Namespace(c.ns).
Resource("apiserversources").
Name(apiServerSource.Name).
Body(apiServerSource).
Do().
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 *apiServerSources) UpdateStatus(apiServerSource *v1alpha1.ApiServerSource) (result *v1alpha1.ApiServerSource, err error) {
result = &v1alpha1.ApiServerSource{}
err = c.client.Put().
Namespace(c.ns).
Resource("apiserversources").
Name(apiServerSource.Name).
SubResource("status").
Body(apiServerSource).
Do().
Into(result)
return
}
// Delete takes name of the apiServerSource and deletes it. Returns an error if one occurs.
func (c *apiServerSources) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("apiserversources").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *apiServerSources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
var timeout time.Duration
if listOptions.TimeoutSeconds != nil {
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("apiserversources").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched apiServerSource.
func (c *apiServerSources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ApiServerSource, err error) {
result = &v1alpha1.ApiServerSource{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("apiserversources").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@ -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.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated typed clients.
package v1alpha1

View File

@ -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.
*/
// Code generated by client-gen. DO NOT EDIT.
// Package fake has the automatically generated clients.
package fake

View File

@ -0,0 +1,140 @@
/*
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1"
)
// FakeApiServerSources implements ApiServerSourceInterface
type FakeApiServerSources struct {
Fake *FakeSourcesV1alpha1
ns string
}
var apiserversourcesResource = schema.GroupVersionResource{Group: "sources.knative.dev", Version: "v1alpha1", Resource: "apiserversources"}
var apiserversourcesKind = schema.GroupVersionKind{Group: "sources.knative.dev", Version: "v1alpha1", Kind: "ApiServerSource"}
// Get takes name of the apiServerSource, and returns the corresponding apiServerSource object, and an error if there is any.
func (c *FakeApiServerSources) Get(name string, options v1.GetOptions) (result *v1alpha1.ApiServerSource, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(apiserversourcesResource, c.ns, name), &v1alpha1.ApiServerSource{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ApiServerSource), err
}
// List takes label and field selectors, and returns the list of ApiServerSources that match those selectors.
func (c *FakeApiServerSources) List(opts v1.ListOptions) (result *v1alpha1.ApiServerSourceList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(apiserversourcesResource, apiserversourcesKind, c.ns, opts), &v1alpha1.ApiServerSourceList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.ApiServerSourceList{ListMeta: obj.(*v1alpha1.ApiServerSourceList).ListMeta}
for _, item := range obj.(*v1alpha1.ApiServerSourceList).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 apiServerSources.
func (c *FakeApiServerSources) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(apiserversourcesResource, c.ns, opts))
}
// Create takes the representation of a apiServerSource and creates it. Returns the server's representation of the apiServerSource, and an error, if there is any.
func (c *FakeApiServerSources) Create(apiServerSource *v1alpha1.ApiServerSource) (result *v1alpha1.ApiServerSource, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(apiserversourcesResource, c.ns, apiServerSource), &v1alpha1.ApiServerSource{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ApiServerSource), err
}
// Update takes the representation of a apiServerSource and updates it. Returns the server's representation of the apiServerSource, and an error, if there is any.
func (c *FakeApiServerSources) Update(apiServerSource *v1alpha1.ApiServerSource) (result *v1alpha1.ApiServerSource, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(apiserversourcesResource, c.ns, apiServerSource), &v1alpha1.ApiServerSource{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ApiServerSource), 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 *FakeApiServerSources) UpdateStatus(apiServerSource *v1alpha1.ApiServerSource) (*v1alpha1.ApiServerSource, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(apiserversourcesResource, "status", c.ns, apiServerSource), &v1alpha1.ApiServerSource{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ApiServerSource), err
}
// Delete takes name of the apiServerSource and deletes it. Returns an error if one occurs.
func (c *FakeApiServerSources) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(apiserversourcesResource, c.ns, name), &v1alpha1.ApiServerSource{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeApiServerSources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(apiserversourcesResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &v1alpha1.ApiServerSourceList{})
return err
}
// Patch applies the patch and returns the patched apiServerSource.
func (c *FakeApiServerSources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ApiServerSource, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(apiserversourcesResource, c.ns, name, pt, data, subresources...), &v1alpha1.ApiServerSource{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.ApiServerSource), err
}

View File

@ -0,0 +1,140 @@
/*
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
v1alpha1 "knative.dev/eventing/pkg/apis/sources/v1alpha1"
)
// FakeSinkBindings implements SinkBindingInterface
type FakeSinkBindings struct {
Fake *FakeSourcesV1alpha1
ns string
}
var sinkbindingsResource = schema.GroupVersionResource{Group: "sources.knative.dev", Version: "v1alpha1", Resource: "sinkbindings"}
var sinkbindingsKind = schema.GroupVersionKind{Group: "sources.knative.dev", Version: "v1alpha1", Kind: "SinkBinding"}
// Get takes name of the sinkBinding, and returns the corresponding sinkBinding object, and an error if there is any.
func (c *FakeSinkBindings) Get(name string, options v1.GetOptions) (result *v1alpha1.SinkBinding, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(sinkbindingsResource, c.ns, name), &v1alpha1.SinkBinding{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.SinkBinding), err
}
// List takes label and field selectors, and returns the list of SinkBindings that match those selectors.
func (c *FakeSinkBindings) List(opts v1.ListOptions) (result *v1alpha1.SinkBindingList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(sinkbindingsResource, sinkbindingsKind, c.ns, opts), &v1alpha1.SinkBindingList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.SinkBindingList{ListMeta: obj.(*v1alpha1.SinkBindingList).ListMeta}
for _, item := range obj.(*v1alpha1.SinkBindingList).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 sinkBindings.
func (c *FakeSinkBindings) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(sinkbindingsResource, c.ns, opts))
}
// Create takes the representation of a sinkBinding and creates it. Returns the server's representation of the sinkBinding, and an error, if there is any.
func (c *FakeSinkBindings) Create(sinkBinding *v1alpha1.SinkBinding) (result *v1alpha1.SinkBinding, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(sinkbindingsResource, c.ns, sinkBinding), &v1alpha1.SinkBinding{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.SinkBinding), err
}
// Update takes the representation of a sinkBinding and updates it. Returns the server's representation of the sinkBinding, and an error, if there is any.
func (c *FakeSinkBindings) Update(sinkBinding *v1alpha1.SinkBinding) (result *v1alpha1.SinkBinding, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(sinkbindingsResource, c.ns, sinkBinding), &v1alpha1.SinkBinding{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.SinkBinding), 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 *FakeSinkBindings) UpdateStatus(sinkBinding *v1alpha1.SinkBinding) (*v1alpha1.SinkBinding, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(sinkbindingsResource, "status", c.ns, sinkBinding), &v1alpha1.SinkBinding{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.SinkBinding), err
}
// Delete takes name of the sinkBinding and deletes it. Returns an error if one occurs.
func (c *FakeSinkBindings) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(sinkbindingsResource, c.ns, name), &v1alpha1.SinkBinding{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeSinkBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(sinkbindingsResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &v1alpha1.SinkBindingList{})
return err
}
// Patch applies the patch and returns the patched sinkBinding.
func (c *FakeSinkBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.SinkBinding, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(sinkbindingsResource, c.ns, name, pt, data, subresources...), &v1alpha1.SinkBinding{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.SinkBinding), err
}

View File

@ -0,0 +1,44 @@
/*
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.
*/
// 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) ApiServerSources(namespace string) v1alpha1.ApiServerSourceInterface {
return &FakeApiServerSources{c, namespace}
}
func (c *FakeSourcesV1alpha1) SinkBindings(namespace string) v1alpha1.SinkBindingInterface {
return &FakeSinkBindings{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
}

View File

@ -0,0 +1,23 @@
/*
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
type ApiServerSourceExpansion interface{}
type SinkBindingExpansion interface{}

View File

@ -0,0 +1,191 @@
/*
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
import (
"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"
)
// SinkBindingsGetter has a method to return a SinkBindingInterface.
// A group's client should implement this interface.
type SinkBindingsGetter interface {
SinkBindings(namespace string) SinkBindingInterface
}
// SinkBindingInterface has methods to work with SinkBinding resources.
type SinkBindingInterface interface {
Create(*v1alpha1.SinkBinding) (*v1alpha1.SinkBinding, error)
Update(*v1alpha1.SinkBinding) (*v1alpha1.SinkBinding, error)
UpdateStatus(*v1alpha1.SinkBinding) (*v1alpha1.SinkBinding, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1alpha1.SinkBinding, error)
List(opts v1.ListOptions) (*v1alpha1.SinkBindingList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.SinkBinding, err error)
SinkBindingExpansion
}
// sinkBindings implements SinkBindingInterface
type sinkBindings struct {
client rest.Interface
ns string
}
// newSinkBindings returns a SinkBindings
func newSinkBindings(c *SourcesV1alpha1Client, namespace string) *sinkBindings {
return &sinkBindings{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the sinkBinding, and returns the corresponding sinkBinding object, and an error if there is any.
func (c *sinkBindings) Get(name string, options v1.GetOptions) (result *v1alpha1.SinkBinding, err error) {
result = &v1alpha1.SinkBinding{}
err = c.client.Get().
Namespace(c.ns).
Resource("sinkbindings").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of SinkBindings that match those selectors.
func (c *sinkBindings) List(opts v1.ListOptions) (result *v1alpha1.SinkBindingList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha1.SinkBindingList{}
err = c.client.Get().
Namespace(c.ns).
Resource("sinkbindings").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested sinkBindings.
func (c *sinkBindings) Watch(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("sinkbindings").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a sinkBinding and creates it. Returns the server's representation of the sinkBinding, and an error, if there is any.
func (c *sinkBindings) Create(sinkBinding *v1alpha1.SinkBinding) (result *v1alpha1.SinkBinding, err error) {
result = &v1alpha1.SinkBinding{}
err = c.client.Post().
Namespace(c.ns).
Resource("sinkbindings").
Body(sinkBinding).
Do().
Into(result)
return
}
// Update takes the representation of a sinkBinding and updates it. Returns the server's representation of the sinkBinding, and an error, if there is any.
func (c *sinkBindings) Update(sinkBinding *v1alpha1.SinkBinding) (result *v1alpha1.SinkBinding, err error) {
result = &v1alpha1.SinkBinding{}
err = c.client.Put().
Namespace(c.ns).
Resource("sinkbindings").
Name(sinkBinding.Name).
Body(sinkBinding).
Do().
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 *sinkBindings) UpdateStatus(sinkBinding *v1alpha1.SinkBinding) (result *v1alpha1.SinkBinding, err error) {
result = &v1alpha1.SinkBinding{}
err = c.client.Put().
Namespace(c.ns).
Resource("sinkbindings").
Name(sinkBinding.Name).
SubResource("status").
Body(sinkBinding).
Do().
Into(result)
return
}
// Delete takes name of the sinkBinding and deletes it. Returns an error if one occurs.
func (c *sinkBindings) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("sinkbindings").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *sinkBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
var timeout time.Duration
if listOptions.TimeoutSeconds != nil {
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("sinkbindings").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched sinkBinding.
func (c *sinkBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.SinkBinding, err error) {
result = &v1alpha1.SinkBinding{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("sinkbindings").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@ -0,0 +1,94 @@
/*
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
import (
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
ApiServerSourcesGetter
SinkBindingsGetter
}
// SourcesV1alpha1Client is used to interact with features provided by the sources.knative.dev group.
type SourcesV1alpha1Client struct {
restClient rest.Interface
}
func (c *SourcesV1alpha1Client) ApiServerSources(namespace string) ApiServerSourceInterface {
return newApiServerSources(c, namespace)
}
func (c *SourcesV1alpha1Client) SinkBindings(namespace string) SinkBindingInterface {
return newSinkBindings(c, namespace)
}
// NewForConfig creates a new SourcesV1alpha1Client for the given config.
func NewForConfig(c *rest.Config) (*SourcesV1alpha1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientFor(&config)
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
}

2
vendor/modules.txt vendored
View File

@ -654,6 +654,8 @@ knative.dev/eventing/pkg/apis/sources/v1alpha1
knative.dev/eventing/pkg/client/clientset/versioned/scheme
knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1alpha1
knative.dev/eventing/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake
knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1
knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha1/fake
knative.dev/eventing/pkg/legacyclient/clientset/versioned/scheme
knative.dev/eventing/pkg/legacyclient/clientset/versioned/typed/legacysources/v1alpha1
knative.dev/eventing/pkg/legacyclient/clientset/versioned/typed/legacysources/v1alpha1/fake