mirror of https://github.com/knative/client.git
feat: Subscription CRUD (#1013)
* feat: Add subscription CRUD - Add kn subscription command group and CRUDL sub-commands - create/update uses following flag names for better alignment: - --sink for subscriber - --sink-reply for reply - --sink-dead-letter for dead-letter-sink - Add 'subscriptions' and 'sub' aliases - Introduce shared library `knative.dev/client/lib/printing` to print Sink object in describe output * Set default channel type messaging.knative.dev/v1beta1:Channel i.e. if no prefix is given to `--channel`, consider it of `Channel` type * Update e2e tests * Update channel flag description
This commit is contained in:
parent
2c67eb9d7c
commit
ce519b520b
|
|
@ -18,12 +18,16 @@
|
|||
|===
|
||||
| | Description | PR
|
||||
|
||||
| 🎁
|
||||
| Add subscription CRUD
|
||||
| https://github.com/knative/client/pull/1013[#1013]
|
||||
|
||||
| 🐛
|
||||
| Fix service export example documentation
|
||||
| https://github.com/knative/client/pull/1006[#1006]
|
||||
|
||||
| 🎁
|
||||
| Add support for service initialScale via `--scale-init` flag
|
||||
| Add support for service initialscale via `--scale-init` flag
|
||||
| https://github.com/knative/client/pull/990[#990]
|
||||
|
||||
| 🎁
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ kn is the command line interface for managing Knative Serving and Eventing resou
|
|||
* [kn route](kn_route.md) - List and describe service routes
|
||||
* [kn service](kn_service.md) - Manage Knative services
|
||||
* [kn source](kn_source.md) - Manage event sources
|
||||
* [kn subscription](kn_subscription.md) - Manage event subscriptions
|
||||
* [kn trigger](kn_trigger.md) - Manage event triggers
|
||||
* [kn version](kn_version.md) - Show the version of this client
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ kn source apiserver create NAME --resource RESOURCE --sink SINK
|
|||
--resource stringArray Specification for which events to listen, in the format Kind:APIVersion:LabelSelector, e.g. "Event:v1:key=value".
|
||||
"LabelSelector" is a list of comma separated key value pairs. "LabelSelector" can be omitted, e.g. "Event:v1".
|
||||
--service-account string Name of the service account to use to run this source
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink 'ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If prefix is not provided, it is considered as a Knative service.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ kn source apiserver update NAME
|
|||
--resource stringArray Specification for which events to listen, in the format Kind:APIVersion:LabelSelector, e.g. "Event:v1:key=value".
|
||||
"LabelSelector" is a list of comma separated key value pairs. "LabelSelector" can be omitted, e.g. "Event:v1".
|
||||
--service-account string Name of the service account to use to run this source
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink 'ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If prefix is not provided, it is considered as a Knative service.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ kn source binding create NAME --subject SUBJECT --sink SINK
|
|||
--ce-override stringArray Cloud Event overrides to apply before sending event to sink. Example: '--ce-override key=value' You may be provide this flag multiple times. To unset, append "-" to the key (e.g. --ce-override key-).
|
||||
-h, --help help for create
|
||||
-n, --namespace string Specify the namespace to operate in.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink 'ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If prefix is not provided, it is considered as a Knative service.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
--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
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ kn source binding update NAME
|
|||
--ce-override stringArray Cloud Event overrides to apply before sending event to sink. Example: '--ce-override key=value' You may be provide this flag multiple times. To unset, append "-" to the key (e.g. --ce-override key-).
|
||||
-h, --help help for update
|
||||
-n, --namespace string Specify the namespace to operate in.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink 'ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If prefix is not provided, it is considered as a Knative service.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
--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
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ kn source ping create NAME --sink SINK
|
|||
-h, --help help for create
|
||||
-n, --namespace string Specify the namespace to operate in.
|
||||
--schedule string Optional schedule specification in crontab format (e.g. '*/2 * * * *' for every two minutes. By default fire every minute.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink 'ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If prefix is not provided, it is considered as a Knative service.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ kn source ping update NAME
|
|||
-h, --help help for update
|
||||
-n, --namespace string Specify the namespace to operate in.
|
||||
--schedule string Optional schedule specification in crontab format (e.g. '*/2 * * * *' for every two minutes. By default fire every minute.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink 'ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If prefix is not provided, it is considered as a Knative service.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
## kn subscription
|
||||
|
||||
Manage event subscriptions
|
||||
|
||||
### Synopsis
|
||||
|
||||
Manage event subscriptions
|
||||
|
||||
```
|
||||
kn subscription COMMAND
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for subscription
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--config string kn configuration file (default: ~/.config/kn/config.yaml)
|
||||
--kubeconfig string kubectl configuration file (default: ~/.kube/config)
|
||||
--log-http log http traffic
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [kn](kn.md) - kn manages Knative Serving and Eventing resources
|
||||
* [kn subscription create](kn_subscription_create.md) - Create a subscription
|
||||
* [kn subscription delete](kn_subscription_delete.md) - Delete a subscription
|
||||
* [kn subscription describe](kn_subscription_describe.md) - Show details of a subscription
|
||||
* [kn subscription list](kn_subscription_list.md) - List subscriptions
|
||||
* [kn subscription update](kn_subscription_update.md) - Update an event subscription
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
## kn subscription create
|
||||
|
||||
Create a subscription
|
||||
|
||||
### Synopsis
|
||||
|
||||
Create a subscription
|
||||
|
||||
```
|
||||
kn subscription create NAME
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
|
||||
# Create a subscription 'sub0' from InMemoryChannel 'pipe0' to a subscriber ksvc 'receiver'
|
||||
kn subscription create sub0 --channel imcv1beta1:pipe0 --sink ksvc:receiver
|
||||
|
||||
# Create a subscription 'sub1' from KafkaChannel 'k1' to ksvc 'mirror', reply to a broker 'nest' and DeadLetterSink to a ksvc 'bucket'
|
||||
kn subscription create sub1 --channel messaging.knative.dev:v1alpha1:KafkaChannel:k1 --sink mirror --sink-reply broker:nest --sink-dead-letter bucket
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--channel string Specify the channel to subscribe to. For the default channel, just use the name (e.g. 'mychannel'). A mapped channel type like 'imc' can be used as a prefix (e.g. 'imc:mychannel'). Finally you can specify the full coordinates to the referenced channel with Group:Version:Kind:Name (e.g. 'messaging.knative.dev:v1alpha1:KafkaChannel:mychannel').
|
||||
-h, --help help for create
|
||||
-n, --namespace string Specify the namespace to operate in.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
--sink-dead-letter string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink-dead-letter broker:nest' for a broker 'nest', '--sink-dead-letter https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink-dead-letter ksvc:receiver' or simply '--sink-dead-letter receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
--sink-reply string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink-reply broker:nest' for a broker 'nest', '--sink-reply https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink-reply ksvc:receiver' or simply '--sink-reply receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--config string kn configuration file (default: ~/.config/kn/config.yaml)
|
||||
--kubeconfig string kubectl configuration file (default: ~/.kube/config)
|
||||
--log-http log http traffic
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [kn subscription](kn_subscription.md) - Manage event subscriptions
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
## kn subscription delete
|
||||
|
||||
Delete a subscription
|
||||
|
||||
### Synopsis
|
||||
|
||||
Delete a subscription
|
||||
|
||||
```
|
||||
kn subscription delete NAME
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
|
||||
# Delete a subscription 'sub0'
|
||||
kn subscription delete sub0
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for delete
|
||||
-n, --namespace string Specify the namespace to operate in.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--config string kn configuration file (default: ~/.config/kn/config.yaml)
|
||||
--kubeconfig string kubectl configuration file (default: ~/.kube/config)
|
||||
--log-http log http traffic
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [kn subscription](kn_subscription.md) - Manage event subscriptions
|
||||
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
## kn subscription describe
|
||||
|
||||
Show details of a subscription
|
||||
|
||||
### Synopsis
|
||||
|
||||
Show details of a subscription
|
||||
|
||||
```
|
||||
kn subscription describe NAME
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
|
||||
# Describe a subscription 'pipe'
|
||||
kn subscription describe pipe
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--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 describe
|
||||
-n, --namespace string Specify the namespace to operate in.
|
||||
-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].
|
||||
-v, --verbose More output.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--config string kn configuration file (default: ~/.config/kn/config.yaml)
|
||||
--kubeconfig string kubectl configuration file (default: ~/.kube/config)
|
||||
--log-http log http traffic
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [kn subscription](kn_subscription.md) - Manage event subscriptions
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
## kn subscription list
|
||||
|
||||
List subscriptions
|
||||
|
||||
### Synopsis
|
||||
|
||||
List subscriptions
|
||||
|
||||
```
|
||||
kn subscription list
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
|
||||
# List all subscriptions
|
||||
kn subscription list
|
||||
|
||||
# List subscriptions in YAML format
|
||||
kn subscription 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 configuration file (default: ~/.config/kn/config.yaml)
|
||||
--kubeconfig string kubectl configuration file (default: ~/.kube/config)
|
||||
--log-http log http traffic
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [kn subscription](kn_subscription.md) - Manage event subscriptions
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
## kn subscription update
|
||||
|
||||
Update an event subscription
|
||||
|
||||
### Synopsis
|
||||
|
||||
Update an event subscription
|
||||
|
||||
```
|
||||
kn subscription update NAME
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
|
||||
# Update a subscription 'sub0' with a subscriber ksvc 'receiver'
|
||||
kn subscription update sub0 --sink ksvc:receiver
|
||||
|
||||
# Update a subscription 'sub1' with subscriber ksvc 'mirror', reply to a broker 'nest' and DeadLetterSink to a ksvc 'bucket'
|
||||
kn subscription update sub1 --sink mirror --sink-reply broker:nest --sink-dead-letter bucket
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for update
|
||||
-n, --namespace string Specify the namespace to operate in.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
--sink-dead-letter string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink-dead-letter broker:nest' for a broker 'nest', '--sink-dead-letter https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink-dead-letter ksvc:receiver' or simply '--sink-dead-letter receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
--sink-reply string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink-reply broker:nest' for a broker 'nest', '--sink-reply https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink-reply ksvc:receiver' or simply '--sink-reply receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--config string kn configuration file (default: ~/.config/kn/config.yaml)
|
||||
--kubeconfig string kubectl configuration file (default: ~/.kube/config)
|
||||
--log-http log http traffic
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [kn subscription](kn_subscription.md) - Manage event subscriptions
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ kn trigger create NAME --sink SINK
|
|||
-h, --help help for create
|
||||
--inject-broker Create new broker with name default through common annotation
|
||||
-n, --namespace string Specify the namespace to operate in.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink 'ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If prefix is not provided, it is considered as a Knative service.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ kn trigger update NAME
|
|||
-h, --help help for update
|
||||
--inject-broker Create new broker with name default through common annotation
|
||||
-n, --namespace string Specify the namespace to operate in.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink 'ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If prefix is not provided, it is considered as a Knative service.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. If a prefix is not provided, it is considered as a Knative service.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package printing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"knative.dev/client/pkg/printers"
|
||||
duckv1 "knative.dev/pkg/apis/duck/v1"
|
||||
)
|
||||
|
||||
// DescribeSink prints the given 'sink' for the given prefix writer 'dw',
|
||||
// provide 'attribute' to print the section heading for this sink
|
||||
func DescribeSink(dw printers.PrefixWriter, attribute, namespace string, sink *duckv1.Destination) {
|
||||
if sink == nil {
|
||||
return
|
||||
}
|
||||
subWriter := dw.WriteAttribute(attribute, "")
|
||||
ref := sink.Ref
|
||||
if ref != nil {
|
||||
subWriter.WriteAttribute("Name", sink.Ref.Name)
|
||||
if sink.Ref.Namespace != "" && sink.Ref.Namespace != namespace {
|
||||
subWriter.WriteAttribute("Namespace", sink.Ref.Namespace)
|
||||
}
|
||||
subWriter.WriteAttribute("Resource", fmt.Sprintf("%s (%s)", sink.Ref.Kind, sink.Ref.APIVersion))
|
||||
}
|
||||
uri := sink.URI
|
||||
if uri != nil {
|
||||
subWriter.WriteAttribute("URI", uri.String())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"gotest.tools/assert"
|
||||
|
||||
"knative.dev/client/pkg/util"
|
||||
)
|
||||
|
||||
func SubscriptionCreate(r *KnRunResultCollector, sname string, args ...string) {
|
||||
cmd := []string{"subscription", "create", sname}
|
||||
cmd = append(cmd, args...)
|
||||
out := r.KnTest().Kn().Run(cmd...)
|
||||
r.AssertNoError(out)
|
||||
assert.Check(r.T(), util.ContainsAllIgnoreCase(out.Stdout, "subscription", sname, "created"))
|
||||
}
|
||||
|
||||
func SubscriptionList(r *KnRunResultCollector, args ...string) string {
|
||||
cmd := []string{"subscription", "list"}
|
||||
cmd = append(cmd, args...)
|
||||
out := r.KnTest().Kn().Run(cmd...)
|
||||
r.AssertNoError(out)
|
||||
return out.Stdout
|
||||
}
|
||||
|
||||
func SubscriptionDescribe(r *KnRunResultCollector, sname string, args ...string) string {
|
||||
cmd := []string{"subscription", "describe", sname}
|
||||
cmd = append(cmd, args...)
|
||||
out := r.KnTest().Kn().Run(cmd...)
|
||||
r.AssertNoError(out)
|
||||
return out.Stdout
|
||||
}
|
||||
|
||||
func SubscriptionDelete(r *KnRunResultCollector, sname string) {
|
||||
out := r.KnTest().Kn().Run("subscription", "delete", sname)
|
||||
r.AssertNoError(out)
|
||||
assert.Check(r.T(), util.ContainsAllIgnoreCase(out.Stdout, "subscription", sname, "deleted"))
|
||||
}
|
||||
|
||||
func SubscriptionUpdate(r *KnRunResultCollector, sname string, args ...string) {
|
||||
cmd := []string{"subscription", "update", sname}
|
||||
cmd = append(cmd, args...)
|
||||
out := r.KnTest().Kn().Run(cmd...)
|
||||
r.AssertNoError(out)
|
||||
assert.Check(r.T(), util.ContainsAllIgnoreCase(out.Stdout, "subscription", sname, "updated"))
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import (
|
|||
dynamicfake "k8s.io/client-go/dynamic/fake"
|
||||
|
||||
eventingv1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1"
|
||||
messagingv1beta1 "knative.dev/eventing/pkg/apis/messaging/v1beta1"
|
||||
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
|
||||
|
||||
"knative.dev/client/pkg/dynamic"
|
||||
|
|
@ -30,6 +31,7 @@ func CreateFakeKnDynamicClient(testNamespace string, objects ...runtime.Object)
|
|||
scheme := runtime.NewScheme()
|
||||
scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "serving.knative.dev", Version: "v1", Kind: "Service"}, &servingv1.Service{})
|
||||
scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "eventing.knative.dev", Version: "v1beta1", Kind: "Broker"}, &eventingv1beta1.Broker{})
|
||||
scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "eventing.knative.dev", Version: "v1beta1", Kind: "Subscription"}, &messagingv1beta1.Subscription{})
|
||||
client := dynamicfake.NewSimpleDynamicClient(scheme, objects...)
|
||||
return dynamic.NewKnDynamicClient(client, testNamespace)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,17 +33,21 @@ type SinkFlags struct {
|
|||
sink string
|
||||
}
|
||||
|
||||
func (i *SinkFlags) Add(cmd *cobra.Command) {
|
||||
cmd.Flags().StringVarP(&i.sink,
|
||||
"sink",
|
||||
"s",
|
||||
"",
|
||||
"Addressable sink for events. "+
|
||||
// AddWithFlagName configures sink flag with given flag name and a short flag name
|
||||
// pass empty short flag name if you dont want to set one
|
||||
func (i *SinkFlags) AddWithFlagName(cmd *cobra.Command, fname, short string) {
|
||||
flag := "--" + fname
|
||||
if short == "" {
|
||||
cmd.Flags().StringVar(&i.sink, fname, "", "")
|
||||
} else {
|
||||
cmd.Flags().StringVarP(&i.sink, fname, short, "", "")
|
||||
}
|
||||
cmd.Flag(fname).Usage = "Addressable sink for events. " +
|
||||
"You can specify a broker, Knative service or URI. " +
|
||||
"Examples: '--sink broker:nest' for a broker 'nest', "+
|
||||
"'--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, "+
|
||||
"'--sink 'ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver'. "+
|
||||
"If prefix is not provided, it is considered as a Knative service.")
|
||||
"Examples: '" + flag + " broker:nest' for a broker 'nest', " +
|
||||
"'" + flag + " https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, " +
|
||||
"'" + flag + " ksvc:receiver' or simply '" + flag + " receiver' for a Knative service 'receiver'. " +
|
||||
"If a prefix is not provided, it is considered as a Knative service."
|
||||
|
||||
for _, p := range config.GlobalConfig.SinkMappings() {
|
||||
//user configration might override the default configuration
|
||||
|
|
@ -55,6 +59,11 @@ func (i *SinkFlags) Add(cmd *cobra.Command) {
|
|||
}
|
||||
}
|
||||
|
||||
// Add configures sink flag with name 'sink' amd short name 's'
|
||||
func (i *SinkFlags) Add(cmd *cobra.Command) {
|
||||
i.AddWithFlagName(cmd, "sink", "s")
|
||||
}
|
||||
|
||||
// sinkPrefixes maps prefixes used for sinks to their GroupVersionResources.
|
||||
var sinkMappings = map[string]schema.GroupVersionResource{
|
||||
"broker": {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package flags
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gotest.tools/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
eventingv1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1"
|
||||
|
|
@ -33,6 +34,40 @@ type resolveCase struct {
|
|||
errContents string
|
||||
}
|
||||
|
||||
type sinkFlagAddTestCases struct {
|
||||
flagName string
|
||||
expectedFlagName string
|
||||
expectedShortName string
|
||||
}
|
||||
|
||||
func TestSinkFlagAdd(t *testing.T) {
|
||||
cases := []*sinkFlagAddTestCases{
|
||||
{
|
||||
"",
|
||||
"sink",
|
||||
"s",
|
||||
},
|
||||
{
|
||||
"subscriber",
|
||||
"subscriber",
|
||||
"",
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
c := &cobra.Command{Use: "sinktest"}
|
||||
sinkFlags := new(SinkFlags)
|
||||
if tc.flagName == "" {
|
||||
sinkFlags.Add(c)
|
||||
assert.Equal(t, tc.expectedFlagName, c.Flag("sink").Name)
|
||||
assert.Equal(t, tc.expectedShortName, c.Flag("sink").Shorthand)
|
||||
} else {
|
||||
sinkFlags.AddWithFlagName(c, tc.flagName, "")
|
||||
assert.Equal(t, tc.expectedFlagName, c.Flag(tc.flagName).Name)
|
||||
assert.Equal(t, tc.expectedShortName, c.Flag(tc.flagName).Shorthand)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolve(t *testing.T) {
|
||||
targetExampleCom, err := apis.ParseURL("http://target.example.com")
|
||||
mysvc := &servingv1.Service{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
knerrors "knative.dev/client/pkg/errors"
|
||||
"knative.dev/client/pkg/kn/commands"
|
||||
"knative.dev/client/pkg/kn/commands/flags"
|
||||
knflags "knative.dev/client/pkg/kn/flags"
|
||||
knmessagingv1beta1 "knative.dev/client/pkg/messaging/v1beta1"
|
||||
)
|
||||
|
||||
// NewSubscriptionCreateCommand to create event subscriptions
|
||||
func NewSubscriptionCreateCommand(p *commands.KnParams) *cobra.Command {
|
||||
var (
|
||||
crefFlag knflags.ChannelRef
|
||||
subscriberFlag, replyFlag, dlsFlag flags.SinkFlags
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "create NAME",
|
||||
Short: "Create a subscription",
|
||||
Example: `
|
||||
# Create a subscription 'sub0' from InMemoryChannel 'pipe0' to a subscriber ksvc 'receiver'
|
||||
kn subscription create sub0 --channel imcv1beta1:pipe0 --sink ksvc:receiver
|
||||
|
||||
# Create a subscription 'sub1' from KafkaChannel 'k1' to ksvc 'mirror', reply to a broker 'nest' and DeadLetterSink to a ksvc 'bucket'
|
||||
kn subscription create sub1 --channel messaging.knative.dev:v1alpha1:KafkaChannel:k1 --sink mirror --sink-reply broker:nest --sink-dead-letter bucket`,
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
if len(args) != 1 {
|
||||
return errors.New("'kn subscription create' requires the subscription name given as single argument")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
if crefFlag.Cref == "" {
|
||||
return errors.New("'kn subscription create' requires the channel reference provided with --channel flag")
|
||||
}
|
||||
|
||||
namespace, err := p.GetNamespace(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dynamicClient, err := p.NewDynamicClient(namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newSubscriptionClient(p, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sb := knmessagingv1beta1.NewSubscriptionBuilder(name)
|
||||
|
||||
cref, err := crefFlag.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.Channel(cref)
|
||||
|
||||
sub, err := subscriberFlag.ResolveSink(dynamicClient, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.Subscriber(sub)
|
||||
|
||||
rep, err := replyFlag.ResolveSink(dynamicClient, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.Reply(rep)
|
||||
|
||||
ds, err := dlsFlag.ResolveSink(dynamicClient, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.DeadLetterSink(ds)
|
||||
|
||||
err = client.CreateSubscription(sb.Build())
|
||||
if err != nil {
|
||||
return knerrors.GetError(err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "Subscription '%s' created in namespace '%s'.\n", name, namespace)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
commands.AddNamespaceFlags(cmd.Flags(), false)
|
||||
crefFlag.Add(cmd.Flags())
|
||||
// add subscriber flag as `--sink`
|
||||
subscriberFlag.Add(cmd)
|
||||
replyFlag.AddWithFlagName(cmd, "sink-reply", "")
|
||||
dlsFlag.AddWithFlagName(cmd, "sink-dead-letter", "")
|
||||
return cmd
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
"knative.dev/client/pkg/messaging/v1beta1"
|
||||
|
||||
dynamicfake "knative.dev/client/pkg/dynamic/fake"
|
||||
"knative.dev/client/pkg/util"
|
||||
)
|
||||
|
||||
func TestCreateSubscriptionErrorCase(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
dynamicClient := dynamicfake.CreateFakeKnDynamicClient("default")
|
||||
|
||||
cRecorder := cClient.Recorder()
|
||||
_, err := executeSubscriptionCommand(cClient, dynamicClient, "create")
|
||||
assert.Error(t, err, "'kn subscription create' requires the subscription name given as single argument")
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
||||
func TestCreateSubscriptionErrorCaseRequiredChannelFlag(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
dynamicClient := dynamicfake.CreateFakeKnDynamicClient("default")
|
||||
|
||||
cRecorder := cClient.Recorder()
|
||||
_, err := executeSubscriptionCommand(cClient, dynamicClient, "create", "sub0")
|
||||
assert.Error(t, err, "'kn subscription create' requires the channel reference provided with --channel flag")
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
||||
func TestCreateSubscriptionErrorCaseChannelFormat(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
dynamicClient := dynamicfake.CreateFakeKnDynamicClient("default")
|
||||
|
||||
cRecorder := cClient.Recorder()
|
||||
_, err := executeSubscriptionCommand(cClient, dynamicClient, "create", "sub0", "--channel", "foo::bar")
|
||||
assert.Error(t, err, "Error: incorrect value 'foo::bar' for '--channel', must be in the format 'Group:Version:Kind:Name' or configure an alias in kn config and refer as: '--channel ALIAS:NAME'")
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
||||
func TestCreateSubscription(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
dynamicClient := dynamicfake.CreateFakeKnDynamicClient("default",
|
||||
createService("ksvc0"),
|
||||
createBroker("b0"),
|
||||
createBroker("b1"))
|
||||
|
||||
cRecorder := cClient.Recorder()
|
||||
cRecorder.CreateSubscription(createSubscription("sub0",
|
||||
"imc0",
|
||||
"ksvc0",
|
||||
"b0",
|
||||
"b1"),
|
||||
nil)
|
||||
|
||||
out, err := executeSubscriptionCommand(cClient, dynamicClient, "create", "sub0",
|
||||
"--channel", "imcv1beta1:imc0",
|
||||
"--sink", "ksvc0",
|
||||
"--sink-reply", "broker:b0",
|
||||
"--sink-dead-letter", "broker:b1")
|
||||
assert.NilError(t, err, "subscription should be created")
|
||||
assert.Assert(t, util.ContainsAll(out, "created", "sub0", "default"))
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"knative.dev/client/pkg/kn/commands"
|
||||
)
|
||||
|
||||
// NewSubscriptionDeleteCommand is for deleting a Subscription
|
||||
func NewSubscriptionDeleteCommand(p *commands.KnParams) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete NAME",
|
||||
Short: "Delete a subscription",
|
||||
Example: `
|
||||
# Delete a subscription 'sub0'
|
||||
kn subscription delete sub0`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.New("'kn subscription delete' requires the subscription name as single argument")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
subscriptionClient, err := newSubscriptionClient(p, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = subscriptionClient.DeleteSubscription(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "Subscription '%s' deleted in namespace '%s'.\n", name, subscriptionClient.Namespace())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
commands.AddNamespaceFlags(cmd.Flags(), false)
|
||||
return cmd
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
|
||||
"knative.dev/client/pkg/messaging/v1beta1"
|
||||
"knative.dev/client/pkg/util"
|
||||
)
|
||||
|
||||
func TestDeleteSubscriptionErrorCase(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t, "test")
|
||||
cRecorder := cClient.Recorder()
|
||||
_, err := executeSubscriptionCommand(cClient, nil, "delete")
|
||||
assert.Error(t, err, "'kn subscription delete' requires the subscription name as single argument")
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
||||
func TestDeleteWithError(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t, "test")
|
||||
cRecorder := cClient.Recorder()
|
||||
cRecorder.DeleteSubscription("sub0", errors.New("not found"))
|
||||
_, err := executeSubscriptionCommand(cClient, nil, "delete", "sub0")
|
||||
assert.ErrorContains(t, err, "not found")
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
||||
func TestSubscriptionDelete(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t, "test")
|
||||
cRecorder := cClient.Recorder()
|
||||
cRecorder.DeleteSubscription("sub0", nil)
|
||||
out, err := executeSubscriptionCommand(cClient, nil, "delete", "sub0")
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, util.ContainsAll(out, "deleted", "sub0", "test"))
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
messagingv1beta1 "knative.dev/eventing/pkg/apis/messaging/v1beta1"
|
||||
|
||||
"knative.dev/client/lib/printing"
|
||||
knerrors "knative.dev/client/pkg/errors"
|
||||
"knative.dev/client/pkg/kn/commands"
|
||||
"knative.dev/client/pkg/printers"
|
||||
)
|
||||
|
||||
// NewSubscriptionDescribeCommand returns a new command for describe a subscription object
|
||||
func NewSubscriptionDescribeCommand(p *commands.KnParams) *cobra.Command {
|
||||
|
||||
// For machine readable output
|
||||
machineReadablePrintFlags := genericclioptions.NewPrintFlags("")
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "describe NAME",
|
||||
Short: "Show details of a subscription",
|
||||
Example: `
|
||||
# Describe a subscription 'pipe'
|
||||
kn subscription describe pipe`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.New("'kn subscription describe' requires the subscription name given as single argument")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
client, err := newSubscriptionClient(p, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
subscription, err := client.GetSubscription(name)
|
||||
if err != nil {
|
||||
return knerrors.GetError(err)
|
||||
}
|
||||
|
||||
out := cmd.OutOrStdout()
|
||||
|
||||
if machineReadablePrintFlags.OutputFlagSpecified() {
|
||||
printer, err := machineReadablePrintFlags.ToPrinter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(subscription, out)
|
||||
}
|
||||
|
||||
dw := printers.NewPrefixWriter(out)
|
||||
|
||||
printDetails, err := cmd.Flags().GetBool("verbose")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeSubscription(dw, subscription, printDetails)
|
||||
dw.WriteLine()
|
||||
if err := dw.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Condition info
|
||||
commands.WriteConditions(dw, subscription.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.")
|
||||
machineReadablePrintFlags.AddFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func writeSubscription(dw printers.PrefixWriter, subscription *messagingv1beta1.Subscription, printDetails bool) {
|
||||
commands.WriteMetadata(dw, &subscription.ObjectMeta, printDetails)
|
||||
ctype := fmt.Sprintf("%s:%s (%s)", subscription.Spec.Channel.Kind, subscription.Spec.Channel.Name, subscription.Spec.Channel.APIVersion)
|
||||
dw.WriteAttribute("Channel", ctype)
|
||||
printing.DescribeSink(dw, "Subscriber", subscription.Namespace, subscription.Spec.Subscriber)
|
||||
printing.DescribeSink(dw, "Reply", subscription.Namespace, subscription.Spec.Reply)
|
||||
if subscription.Spec.DeepCopy().Delivery != nil {
|
||||
printing.DescribeSink(dw, "DeadLetterSink", subscription.Namespace, subscription.Spec.Delivery.DeadLetterSink)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
"knative.dev/client/pkg/messaging/v1beta1"
|
||||
|
||||
"knative.dev/client/pkg/util"
|
||||
)
|
||||
|
||||
func TestDescribeSubscriptionErrorCase(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
cRecorder := cClient.Recorder()
|
||||
_, err := executeSubscriptionCommand(cClient, nil, "describe")
|
||||
assert.Error(t, err, "'kn subscription describe' requires the subscription name given as single argument")
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
||||
func TestDescribeSubscriptionErrorCaseNotFound(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
cRecorder := cClient.Recorder()
|
||||
cRecorder.GetSubscription("sub0", nil, errors.New("not found"))
|
||||
_, err := executeSubscriptionCommand(cClient, nil, "describe", "sub0")
|
||||
assert.Error(t, err, "not found")
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
||||
func TestDescribeSubscription(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
cRecorder := cClient.Recorder()
|
||||
cRecorder.GetSubscription("sub0", createSubscription("sub0", "imc0", "ksvc0", "b0", "b1"), nil)
|
||||
out, err := executeSubscriptionCommand(cClient, nil, "describe", "sub0")
|
||||
assert.NilError(t, err, "subscription should be described")
|
||||
assert.Assert(t, util.ContainsAll(out,
|
||||
"sub0",
|
||||
"Channel", "imc0", "messaging.knative.dev", "v1beta1", "InMemoryChannel",
|
||||
"Subscriber", "ksvc0", "serving.knative.dev", "v1", "Service",
|
||||
"Reply", "b0", "eventing.knative.dev", "v1beta1", "Broker",
|
||||
"DeadLetterSink", "b1"))
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"knative.dev/client/pkg/kn/commands"
|
||||
"knative.dev/client/pkg/kn/commands/flags"
|
||||
hprinters "knative.dev/client/pkg/printers"
|
||||
|
||||
messagingv1beta1 "knative.dev/eventing/pkg/apis/messaging/v1beta1"
|
||||
)
|
||||
|
||||
// ListHandlers handles printing human readable table for `kn subscription list` command's output
|
||||
func ListHandlers(h hprinters.PrintHandler) {
|
||||
subscriptionColumnDefinitions := []metav1beta1.TableColumnDefinition{
|
||||
{Name: "Namespace", Type: "string", Description: "Namespace of the subscription", Priority: 0},
|
||||
{Name: "Name", Type: "string", Description: "Name of the subscription", Priority: 1},
|
||||
{Name: "Channel", Type: "string", Description: "Channel of the subcription", Priority: 1},
|
||||
{Name: "Subscriber", Type: "string", Description: "Subscriber sink of the subscription", Priority: 1},
|
||||
{Name: "Reply", Type: "string", Description: "Reply sink of the subscription", Priority: 1},
|
||||
{Name: "Dead Letter Sink", Type: "string", Description: "DeadLetterSink of the subscription", Priority: 1},
|
||||
{Name: "Ready", Type: "string", Description: "Ready state of the subscription", Priority: 1},
|
||||
{Name: "Reason", Type: "string", Description: "Reason for non ready subscription", Priority: 1},
|
||||
}
|
||||
h.TableHandler(subscriptionColumnDefinitions, printSubscription)
|
||||
h.TableHandler(subscriptionColumnDefinitions, printSubscriptionList)
|
||||
}
|
||||
|
||||
// printSubscription populates a single row of Subscription list
|
||||
func printSubscription(subscription *messagingv1beta1.Subscription, options hprinters.PrintOptions) ([]metav1beta1.TableRow, error) {
|
||||
row := metav1beta1.TableRow{
|
||||
Object: runtime.RawExtension{Object: subscription},
|
||||
}
|
||||
|
||||
name := subscription.Name
|
||||
ctype := subscription.Spec.Channel.Kind
|
||||
channel := subscription.Spec.Channel.Name
|
||||
|
||||
var subscriber, reply, dls string
|
||||
if subscription.Spec.Subscriber != nil {
|
||||
subscriber = flags.SinkToString(*subscription.Spec.Subscriber)
|
||||
} else {
|
||||
subscriber = ""
|
||||
}
|
||||
if subscription.Spec.Reply != nil {
|
||||
reply = flags.SinkToString(*subscription.Spec.Reply)
|
||||
} else {
|
||||
reply = ""
|
||||
}
|
||||
if subscription.Spec.Delivery != nil && subscription.Spec.Delivery.DeadLetterSink != nil {
|
||||
dls = flags.SinkToString(*subscription.Spec.Delivery.DeadLetterSink)
|
||||
} else {
|
||||
dls = ""
|
||||
}
|
||||
ready := commands.ReadyCondition(subscription.Status.Conditions)
|
||||
reason := commands.NonReadyConditionReason(subscription.Status.Conditions)
|
||||
|
||||
if options.AllNamespaces {
|
||||
row.Cells = append(row.Cells, subscription.Namespace)
|
||||
}
|
||||
|
||||
row.Cells = append(row.Cells, name, ctype+":"+channel, subscriber, reply, dls, ready, reason)
|
||||
return []metav1beta1.TableRow{row}, nil
|
||||
}
|
||||
|
||||
// printSubscriptionList populates the Subscription list table rows
|
||||
func printSubscriptionList(subscriptionList *messagingv1beta1.SubscriptionList, options hprinters.PrintOptions) ([]metav1beta1.TableRow, error) {
|
||||
if options.AllNamespaces {
|
||||
return printSubscriptionListWithNamespace(subscriptionList, options)
|
||||
}
|
||||
|
||||
rows := make([]metav1beta1.TableRow, 0, len(subscriptionList.Items))
|
||||
|
||||
sort.SliceStable(subscriptionList.Items, func(i, j int) bool {
|
||||
return subscriptionList.Items[i].GetName() < subscriptionList.Items[j].GetName()
|
||||
})
|
||||
|
||||
for _, item := range subscriptionList.Items {
|
||||
row, err := printSubscription(&item, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows = append(rows, row...)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
// printSubscriptionListWithNamespace populates the knative service table rows with namespace column
|
||||
func printSubscriptionListWithNamespace(subscriptionList *messagingv1beta1.SubscriptionList, options hprinters.PrintOptions) ([]metav1beta1.TableRow, error) {
|
||||
rows := make([]metav1beta1.TableRow, 0, len(subscriptionList.Items))
|
||||
|
||||
// temporary slice for sorting services in non-default namespace
|
||||
var others []metav1beta1.TableRow
|
||||
|
||||
for _, subscription := range subscriptionList.Items {
|
||||
// Fill in with services in `default` namespace at first
|
||||
if subscription.Namespace == "default" {
|
||||
r, err := printSubscription(&subscription, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows = append(rows, r...)
|
||||
continue
|
||||
}
|
||||
// put other services in temporary slice
|
||||
r, err := printSubscription(&subscription, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
others = append(others, r...)
|
||||
}
|
||||
|
||||
// sort other services list alphabetically by namespace
|
||||
sort.SliceStable(others, func(i, j int) bool {
|
||||
return others[i].Cells[0].(string) < others[j].Cells[0].(string)
|
||||
})
|
||||
|
||||
return append(rows, others...), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"knative.dev/client/pkg/kn/commands"
|
||||
"knative.dev/client/pkg/kn/commands/flags"
|
||||
)
|
||||
|
||||
// NewSubscriptionListCommand is for listing subscription objects
|
||||
func NewSubscriptionListCommand(p *commands.KnParams) *cobra.Command {
|
||||
listFlags := flags.NewListPrintFlags(ListHandlers)
|
||||
|
||||
listCommand := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List subscriptions",
|
||||
Example: `
|
||||
# List all subscriptions
|
||||
kn subscription list
|
||||
|
||||
# List subscriptions in YAML format
|
||||
kn subscription list -o yaml`,
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
// TODO: filter list by given subscription name
|
||||
|
||||
client, err := newSubscriptionClient(p, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
subscriptionList, err := client.ListSubscription()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if subscriptionList == nil || len(subscriptionList.Items) == 0 {
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "No subscriptions found.\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
if client.Namespace() == "" {
|
||||
listFlags.EnsureWithNamespace()
|
||||
}
|
||||
|
||||
err = listFlags.Print(subscriptionList, cmd.OutOrStdout())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
commands.AddNamespaceFlags(listCommand.Flags(), true)
|
||||
listFlags.AddFlags(listCommand)
|
||||
return listCommand
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
messagingv1beta1 "knative.dev/eventing/pkg/apis/messaging/v1beta1"
|
||||
|
||||
v1beta1 "knative.dev/client/pkg/messaging/v1beta1"
|
||||
"knative.dev/client/pkg/util"
|
||||
)
|
||||
|
||||
func TestSubscriptionListNoSubscriptionsFound(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
cRecorder := cClient.Recorder()
|
||||
cRecorder.ListSubscription(nil, nil)
|
||||
out, err := executeSubscriptionCommand(cClient, nil, "list")
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, util.ContainsAll(out, "No subscriptions found"))
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
||||
func TestSubscriptionList(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
cRecorder := cClient.Recorder()
|
||||
clist := &messagingv1beta1.SubscriptionList{}
|
||||
clist.Items = []messagingv1beta1.Subscription{
|
||||
*createSubscription("s0", "imc0", "ksvc0", "b00", "b01"),
|
||||
*createSubscription("s1", "imc1", "ksvc1", "b10", "b11"),
|
||||
*createSubscription("s2", "imc2", "ksvc2", "b20", "b21"),
|
||||
}
|
||||
|
||||
t.Run("default list output", func(t *testing.T) {
|
||||
cRecorder.ListSubscription(clist, nil)
|
||||
out, err := executeSubscriptionCommand(cClient, nil, "list")
|
||||
assert.NilError(t, err)
|
||||
ol := strings.Split(out, "\n")
|
||||
assert.Check(t, util.ContainsAll(ol[0], "NAME", "CHANNEL", "SUBSCRIBER", "REPLY", "DEAD LETTER SINK", "READY", "REASON"))
|
||||
assert.Check(t, util.ContainsAll(ol[1], "s0", "InMemoryChannel:imc0", "ksvc:ksvc0", "broker:b00", "broker:b01"))
|
||||
assert.Check(t, util.ContainsAll(ol[2], "s1", "imc1", "ksvc1", "b10", "b11"))
|
||||
assert.Check(t, util.ContainsAll(ol[3], "s2", "imc2", "ksvc2", "b20", "b21"))
|
||||
})
|
||||
|
||||
t.Run("no headers list output", func(t *testing.T) {
|
||||
cRecorder.ListSubscription(clist, nil)
|
||||
out, err := executeSubscriptionCommand(cClient, nil, "list", "--no-headers")
|
||||
assert.NilError(t, err)
|
||||
ol := strings.Split(out, "\n")
|
||||
assert.Check(t, util.ContainsNone(ol[0], "NAME", "CHANNEL", "SUBSCRIBER", "REPLY", "DEAD LETTER SINK", "READY", "REASON"))
|
||||
assert.Check(t, util.ContainsAll(ol[0], "s0", "imc0", "ksvc0", "b00", "b01"))
|
||||
})
|
||||
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientv1beta1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/messaging/v1beta1"
|
||||
|
||||
"knative.dev/client/pkg/kn/commands"
|
||||
messagingv1beta1 "knative.dev/client/pkg/messaging/v1beta1"
|
||||
)
|
||||
|
||||
// NewSubscriptionCommand to manage event subscriptions
|
||||
func NewSubscriptionCommand(p *commands.KnParams) *cobra.Command {
|
||||
subscriptionCmd := &cobra.Command{
|
||||
Use: "subscription COMMAND",
|
||||
Short: "Manage event subscriptions",
|
||||
Aliases: []string{"subscriptions", "sub"},
|
||||
}
|
||||
subscriptionCmd.AddCommand(NewSubscriptionCreateCommand(p))
|
||||
subscriptionCmd.AddCommand(NewSubscriptionUpdateCommand(p))
|
||||
subscriptionCmd.AddCommand(NewSubscriptionListCommand(p))
|
||||
subscriptionCmd.AddCommand(NewSubscriptionDeleteCommand(p))
|
||||
subscriptionCmd.AddCommand(NewSubscriptionDescribeCommand(p))
|
||||
return subscriptionCmd
|
||||
}
|
||||
|
||||
var subscriptionClientFactory func(config clientcmd.ClientConfig, namespace string) (messagingv1beta1.KnSubscriptionsClient, error)
|
||||
|
||||
func newSubscriptionClient(p *commands.KnParams, cmd *cobra.Command) (messagingv1beta1.KnSubscriptionsClient, error) {
|
||||
namespace, err := p.GetNamespace(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if subscriptionClientFactory != nil {
|
||||
config, err := p.GetClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return subscriptionClientFactory(config, namespace)
|
||||
}
|
||||
|
||||
clientConfig, err := p.RestConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := clientv1beta1.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return messagingv1beta1.NewKnMessagingClient(client, namespace).SubscriptionsClient(), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
eventingv1beta1 "knative.dev/eventing/pkg/apis/eventing/v1beta1"
|
||||
"knative.dev/eventing/pkg/apis/messaging/v1beta1"
|
||||
duckv1 "knative.dev/pkg/apis/duck/v1"
|
||||
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
|
||||
|
||||
kndynamic "knative.dev/client/pkg/dynamic"
|
||||
"knative.dev/client/pkg/kn/commands"
|
||||
clientv1beta1 "knative.dev/client/pkg/messaging/v1beta1"
|
||||
)
|
||||
|
||||
// Helper methods
|
||||
var blankConfig clientcmd.ClientConfig
|
||||
|
||||
// TODO: Remove that blankConfig hack for tests in favor of overwriting GetConfig()
|
||||
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 executeSubscriptionCommand(subscriptionClient clientv1beta1.KnSubscriptionsClient, dynamicClient kndynamic.KnDynamicClient, args ...string) (string, error) {
|
||||
knParams := &commands.KnParams{}
|
||||
knParams.ClientConfig = blankConfig
|
||||
|
||||
output := new(bytes.Buffer)
|
||||
knParams.Output = output
|
||||
knParams.NewDynamicClient = func(namespace string) (kndynamic.KnDynamicClient, error) {
|
||||
return dynamicClient, nil
|
||||
}
|
||||
|
||||
cmd := NewSubscriptionCommand(knParams)
|
||||
cmd.SetArgs(args)
|
||||
cmd.SetOutput(output)
|
||||
|
||||
subscriptionClientFactory = func(config clientcmd.ClientConfig, namespace string) (clientv1beta1.KnSubscriptionsClient, error) {
|
||||
return subscriptionClient, nil
|
||||
}
|
||||
defer cleanupSubscriptionMockClient()
|
||||
|
||||
err := cmd.Execute()
|
||||
|
||||
return output.String(), err
|
||||
}
|
||||
|
||||
func cleanupSubscriptionMockClient() {
|
||||
subscriptionClientFactory = nil
|
||||
}
|
||||
|
||||
func createSubscription(name, channel, subscriber, reply, dls string) *v1beta1.Subscription {
|
||||
return clientv1beta1.
|
||||
NewSubscriptionBuilder(name).
|
||||
Channel(createIMCObjectReference(channel)).
|
||||
Subscriber(createServiceSink(subscriber)).
|
||||
Reply(createBrokerSink(reply)).
|
||||
DeadLetterSink(createBrokerSink(dls)).
|
||||
Build()
|
||||
}
|
||||
|
||||
func createIMCObjectReference(channel string) *corev1.ObjectReference {
|
||||
return &corev1.ObjectReference{
|
||||
APIVersion: "messaging.knative.dev/v1beta1",
|
||||
Kind: "InMemoryChannel",
|
||||
Name: channel,
|
||||
}
|
||||
}
|
||||
|
||||
func createServiceSink(service string) *duckv1.Destination {
|
||||
if service == "" {
|
||||
return nil
|
||||
}
|
||||
return &duckv1.Destination{
|
||||
Ref: &duckv1.KReference{
|
||||
Kind: "Service",
|
||||
APIVersion: "serving.knative.dev/v1",
|
||||
Name: service,
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createBrokerSink(broker string) *duckv1.Destination {
|
||||
if broker == "" {
|
||||
return nil
|
||||
}
|
||||
return &duckv1.Destination{
|
||||
Ref: &duckv1.KReference{
|
||||
Kind: "Broker",
|
||||
APIVersion: "eventing.knative.dev/v1beta1",
|
||||
Name: broker,
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createService(name string) *servingv1.Service {
|
||||
return &servingv1.Service{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "Service", APIVersion: "serving.knative.dev/v1"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "default"},
|
||||
}
|
||||
}
|
||||
|
||||
func createBroker(name string) *eventingv1beta1.Broker {
|
||||
return &eventingv1beta1.Broker{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "Broker", APIVersion: "eventing.knative.dev/v1beta1"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "default"},
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
knerrors "knative.dev/client/pkg/errors"
|
||||
"knative.dev/client/pkg/kn/commands"
|
||||
"knative.dev/client/pkg/kn/commands/flags"
|
||||
knmessagingv1beta1 "knative.dev/client/pkg/messaging/v1beta1"
|
||||
)
|
||||
|
||||
// NewSubscriptionUpdateCommand to update event subscriptions
|
||||
func NewSubscriptionUpdateCommand(p *commands.KnParams) *cobra.Command {
|
||||
var subscriberFlag, replyFlag, dlsFlag flags.SinkFlags
|
||||
cmd := &cobra.Command{
|
||||
Use: "update NAME",
|
||||
Short: "Update an event subscription",
|
||||
Example: `
|
||||
# Update a subscription 'sub0' with a subscriber ksvc 'receiver'
|
||||
kn subscription update sub0 --sink ksvc:receiver
|
||||
|
||||
# Update a subscription 'sub1' with subscriber ksvc 'mirror', reply to a broker 'nest' and DeadLetterSink to a ksvc 'bucket'
|
||||
kn subscription update sub1 --sink mirror --sink-reply broker:nest --sink-dead-letter bucket`,
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
if len(args) != 1 {
|
||||
return errors.New("'kn subscription update' requires the subscription name given as single argument")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
namespace, err := p.GetNamespace(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dynamicClient, err := p.NewDynamicClient(namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newSubscriptionClient(p, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
foundSub, err := client.GetSubscription(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sb := knmessagingv1beta1.NewSubscriptionBuilderFromExisting(foundSub)
|
||||
|
||||
sub, err := subscriberFlag.ResolveSink(dynamicClient, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.Subscriber(sub)
|
||||
|
||||
rep, err := replyFlag.ResolveSink(dynamicClient, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.Reply(rep)
|
||||
|
||||
ds, err := dlsFlag.ResolveSink(dynamicClient, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sb.DeadLetterSink(ds)
|
||||
|
||||
err = client.UpdateSubscription(sb.Build())
|
||||
if err != nil {
|
||||
return knerrors.GetError(err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "Subscription '%s' updated in namespace '%s'.\n", name, namespace)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
commands.AddNamespaceFlags(cmd.Flags(), false)
|
||||
// add subscriber flag as `--sink`
|
||||
subscriberFlag.Add(cmd)
|
||||
replyFlag.AddWithFlagName(cmd, "sink-reply", "")
|
||||
dlsFlag.AddWithFlagName(cmd, "sink-dead-letter", "")
|
||||
return cmd
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package subscription
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
"knative.dev/client/pkg/messaging/v1beta1"
|
||||
|
||||
dynamicfake "knative.dev/client/pkg/dynamic/fake"
|
||||
"knative.dev/client/pkg/util"
|
||||
)
|
||||
|
||||
func TestUpdateSubscriptionErrorCase(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
dynamicClient := dynamicfake.CreateFakeKnDynamicClient("default")
|
||||
|
||||
cRecorder := cClient.Recorder()
|
||||
_, err := executeSubscriptionCommand(cClient, dynamicClient, "update")
|
||||
assert.Error(t, err, "'kn subscription update' requires the subscription name given as single argument")
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
||||
func TestUpdateSubscriptionErrorCaseUnknownChannelFlag(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
dynamicClient := dynamicfake.CreateFakeKnDynamicClient("default")
|
||||
|
||||
cRecorder := cClient.Recorder()
|
||||
_, err := executeSubscriptionCommand(cClient, dynamicClient, "update", "sub0", "--channel", "imc:i1")
|
||||
assert.Error(t, err, "unknown flag: --channel")
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
||||
func TestUpdateSubscription(t *testing.T) {
|
||||
cClient := v1beta1.NewMockKnSubscriptionsClient(t)
|
||||
sub0 := createSubscription("sub0", "imc0", "ksvc0", "", "")
|
||||
dynamicClient := dynamicfake.CreateFakeKnDynamicClient("default",
|
||||
sub0,
|
||||
createService("ksvc1"),
|
||||
createBroker("b0"),
|
||||
createBroker("b1"))
|
||||
|
||||
cRecorder := cClient.Recorder()
|
||||
cRecorder.GetSubscription("sub0", sub0, nil)
|
||||
cRecorder.UpdateSubscription(createSubscription("sub0",
|
||||
"imc0",
|
||||
"ksvc1",
|
||||
"b0",
|
||||
"b1"),
|
||||
nil)
|
||||
|
||||
out, err := executeSubscriptionCommand(cClient, dynamicClient, "update", "sub0",
|
||||
"--sink", "ksvc1",
|
||||
"--sink-reply", "broker:b0",
|
||||
"--sink-dead-letter", "broker:b1")
|
||||
assert.NilError(t, err, "subscription should be updated")
|
||||
assert.Assert(t, util.ContainsAll(out, "updated", "sub0", "default"))
|
||||
cRecorder.Validate()
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"knative.dev/client/pkg/kn/config"
|
||||
|
|
@ -28,6 +29,25 @@ type ChannelTypeFlags struct {
|
|||
ctype string
|
||||
}
|
||||
|
||||
type ChannelRef struct {
|
||||
Cref string
|
||||
}
|
||||
|
||||
// ctypeMappings maps aliases used for channel types to their GroupVersionKind
|
||||
var ctypeMappings = map[string]schema.GroupVersionKind{
|
||||
"imcv1beta1": {
|
||||
Group: "messaging.knative.dev",
|
||||
Version: "v1beta1",
|
||||
Kind: "InMemoryChannel",
|
||||
},
|
||||
"imc": {
|
||||
Group: "messaging.knative.dev",
|
||||
Version: "v1",
|
||||
Kind: "InMemoryChannel",
|
||||
},
|
||||
}
|
||||
|
||||
// Add sets channel type flag definition to given flagset
|
||||
func (i *ChannelTypeFlags) Add(f *pflag.FlagSet) {
|
||||
f.StringVar(&i.ctype,
|
||||
"type",
|
||||
|
|
@ -48,20 +68,7 @@ func (i *ChannelTypeFlags) Add(f *pflag.FlagSet) {
|
|||
}
|
||||
}
|
||||
|
||||
// ctypeMappings maps aliases used for channel types to their GroupVersionKind
|
||||
var ctypeMappings = map[string]schema.GroupVersionKind{
|
||||
"imcv1beta1": {
|
||||
Group: "messaging.knative.dev",
|
||||
Version: "v1beta1",
|
||||
Kind: "InMemoryChannel",
|
||||
},
|
||||
"imc": {
|
||||
Group: "messaging.knative.dev",
|
||||
Version: "v1",
|
||||
Kind: "InMemoryChannel",
|
||||
},
|
||||
}
|
||||
|
||||
// Parse parses the CLI value for channel type flag and populates GVK or returns error
|
||||
func (i *ChannelTypeFlags) Parse() (*schema.GroupVersionKind, error) {
|
||||
parts := strings.Split(i.ctype, ":")
|
||||
switch len(parts) {
|
||||
|
|
@ -79,3 +86,38 @@ func (i *ChannelTypeFlags) Parse() (*schema.GroupVersionKind, error) {
|
|||
return nil, fmt.Errorf("Error: incorrect value '%s' for '--type', must be in the format 'Group:Version:Kind' or configure an alias in kn config", i.ctype)
|
||||
}
|
||||
}
|
||||
|
||||
// Add sets channel reference flag definition to given flagset
|
||||
func (i *ChannelRef) Add(f *pflag.FlagSet) {
|
||||
f.StringVar(&i.Cref,
|
||||
"channel",
|
||||
"",
|
||||
"Specify the channel to subscribe to. For the default channel, "+
|
||||
"just use the name (e.g. 'mychannel'). A mapped channel type like 'imc' "+
|
||||
"can be used as a prefix (e.g. 'imc:mychannel'). "+
|
||||
"Finally you can specify the full coordinates to the referenced channel "+
|
||||
"with Group:Version:Kind:Name (e.g. 'messaging.knative.dev:v1alpha1:KafkaChannel:mychannel').")
|
||||
}
|
||||
|
||||
// Parse parses the CLI value for channel ref flag and populates object reference or return error
|
||||
func (i *ChannelRef) Parse() (*corev1.ObjectReference, error) {
|
||||
parts := strings.Split(i.Cref, ":")
|
||||
switch len(parts) {
|
||||
// if no prefix is given, defer to "messaging.knative.dev/v1beta1:Channel"
|
||||
case 1:
|
||||
return &corev1.ObjectReference{Kind: "Channel", APIVersion: "messaging.knative.dev/v1beta1", Name: parts[0]}, nil
|
||||
case 2:
|
||||
if typ, ok := ctypeMappings[parts[0]]; ok {
|
||||
return &corev1.ObjectReference{Kind: typ.Kind, APIVersion: typ.GroupVersion().String(), Name: parts[1]}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Error: unknown alias '%s' for '--channel', please configure the alias in kn config or specify in the format '--channel Group:Version:Kind:Name'", parts[0])
|
||||
case 4:
|
||||
if parts[0] == "" || parts[1] == "" || parts[2] == "" || parts[3] == "" {
|
||||
return nil, fmt.Errorf("Error: incorrect value '%s' for '--channel', must be in the format 'Group:Version:Kind:Name' or configure an alias in kn config and refer as: '--channel ALIAS:NAME'", i.Cref)
|
||||
}
|
||||
return &corev1.ObjectReference{Kind: parts[2], APIVersion: parts[0] + "/" + parts[1], Name: parts[3]}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Error: incorrect value '%s' for '--channel', must be in the format 'Group:Version:Kind:Name' or configure an alias in kn config and refer as: '--channel ALIAS:NAME'", i.Cref)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
|
||||
"github.com/spf13/pflag"
|
||||
"gotest.tools/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
|
|
@ -29,6 +30,13 @@ type channelTypeFlagsTestCase struct {
|
|||
expectedErrText string
|
||||
}
|
||||
|
||||
type channelRefFlagsTestCase struct {
|
||||
name string
|
||||
arg string
|
||||
expectedObjectRef *corev1.ObjectReference
|
||||
expectedErrText string
|
||||
}
|
||||
|
||||
func TestChannelTypesFlags(t *testing.T) {
|
||||
cases := []*channelTypeFlagsTestCase{
|
||||
{
|
||||
|
|
@ -84,3 +92,65 @@ func TestChannelTypesFlags(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelRefFlags(t *testing.T) {
|
||||
cases := []*channelRefFlagsTestCase{
|
||||
{
|
||||
"inbuilt alias imcv1beta1 case",
|
||||
"imcv1beta1:i1",
|
||||
&corev1.ObjectReference{APIVersion: "messaging.knative.dev/v1beta1", Kind: "InMemoryChannel", Name: "i1"},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"inbuilt alias 'imc' case",
|
||||
"imc:i2",
|
||||
&corev1.ObjectReference{APIVersion: "messaging.knative.dev/v1", Kind: "InMemoryChannel", Name: "i2"},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"explicit GVK case",
|
||||
"messaging.knative.dev:v1alpha1:KafkaChannel:k1",
|
||||
&corev1.ObjectReference{APIVersion: "messaging.knative.dev/v1alpha1", Kind: "KafkaChannel", Name: "k1"},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"default channel type prefix case",
|
||||
"c1",
|
||||
&corev1.ObjectReference{APIVersion: "messaging.knative.dev/v1beta1", Kind: "Channel", Name: "c1"},
|
||||
"",
|
||||
},
|
||||
{
|
||||
"error case unknown alias",
|
||||
"natss:n1",
|
||||
nil,
|
||||
"Error: unknown alias 'natss' for '--channel', please configure the alias in kn config or specify in the format '--channel Group:Version:Kind:Name'",
|
||||
},
|
||||
{
|
||||
"error case incorrect gvk format, missing version",
|
||||
"foo::bar",
|
||||
nil,
|
||||
"Error: incorrect value 'foo::bar' for '--channel', must be in the format 'Group:Version:Kind:Name' or configure an alias in kn config and refer as: '--channel ALIAS:NAME'",
|
||||
},
|
||||
{
|
||||
"error case incorrect gvk format, additional field",
|
||||
"foo:bar::bat",
|
||||
nil,
|
||||
"Error: incorrect value 'foo:bar::bat' for '--channel', must be in the format 'Group:Version:Kind:Name' or configure an alias in kn config and refer as: '--channel ALIAS:NAME'",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
f := &ChannelRef{}
|
||||
flagset := &pflag.FlagSet{}
|
||||
f.Add(flagset)
|
||||
flagset.Set("channel", c.arg)
|
||||
obj, err := f.Parse()
|
||||
if c.expectedErrText != "" {
|
||||
assert.Equal(t, err.Error(), c.expectedErrText)
|
||||
} else {
|
||||
assert.Equal(t, *obj, *c.expectedObjectRef)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import (
|
|||
"knative.dev/client/pkg/kn/commands/route"
|
||||
"knative.dev/client/pkg/kn/commands/service"
|
||||
"knative.dev/client/pkg/kn/commands/source"
|
||||
"knative.dev/client/pkg/kn/commands/subscription"
|
||||
"knative.dev/client/pkg/kn/commands/trigger"
|
||||
"knative.dev/client/pkg/kn/commands/version"
|
||||
"knative.dev/client/pkg/kn/config"
|
||||
|
|
@ -95,6 +96,7 @@ func NewRootCommand(helpFuncs *template.FuncMap) (*cobra.Command, error) {
|
|||
broker.NewBrokerCommand(p),
|
||||
trigger.NewTriggerCommand(p),
|
||||
channel.NewChannelCommand(p),
|
||||
subscription.NewSubscriptionCommand(p),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ import (
|
|||
type KnMessagingClient interface {
|
||||
// Get the Channels client
|
||||
ChannelsClient() KnChannelsClient
|
||||
|
||||
// Get the Subscriptions client
|
||||
SubscriptionsClient() KnSubscriptionsClient
|
||||
}
|
||||
|
||||
// messagingClient holds Messaging client interface and namespace
|
||||
|
|
@ -49,6 +52,11 @@ func (c *messagingClient) ChannelsClient() KnChannelsClient {
|
|||
return newKnChannelsClient(c.client.Channels(c.namespace), c.namespace)
|
||||
}
|
||||
|
||||
// SubscriptionsClient for working with Subscriptions
|
||||
func (c *messagingClient) SubscriptionsClient() KnSubscriptionsClient {
|
||||
return newKnSubscriptionsClient(c.client.Subscriptions(c.namespace), c.namespace)
|
||||
}
|
||||
|
||||
// update GVK of object
|
||||
func updateMessagingGVK(obj runtime.Object) error {
|
||||
return util.UpdateGroupVersionKindWithScheme(obj, messagingv1beta1.SchemeGroupVersion, scheme.Scheme)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
eventingduckv1beta1 "knative.dev/eventing/pkg/apis/duck/v1beta1"
|
||||
"knative.dev/eventing/pkg/apis/messaging/v1beta1"
|
||||
clientv1beta1 "knative.dev/eventing/pkg/client/clientset/versioned/typed/messaging/v1beta1"
|
||||
duckv1 "knative.dev/pkg/apis/duck/v1"
|
||||
|
||||
knerrors "knative.dev/client/pkg/errors"
|
||||
)
|
||||
|
||||
// KnSubscriptionsClient for interacting with Subscriptions
|
||||
type KnSubscriptionsClient interface {
|
||||
|
||||
// GetSubscription returns a Subscription by its name
|
||||
GetSubscription(name string) (*v1beta1.Subscription, error)
|
||||
|
||||
// CreteSubscription creates a Subscription with given spec
|
||||
CreateSubscription(subscription *v1beta1.Subscription) error
|
||||
|
||||
// UpdateSubscription updates a Subscription with given spec
|
||||
UpdateSubscription(subscription *v1beta1.Subscription) error
|
||||
|
||||
// DeleteSubscription deletes a Subscription by its name
|
||||
DeleteSubscription(name string) error
|
||||
|
||||
// ListSubscription lists all Subscriptions
|
||||
ListSubscription() (*v1beta1.SubscriptionList, error)
|
||||
|
||||
// Namespace returns the namespace for this subscription client
|
||||
Namespace() string
|
||||
}
|
||||
|
||||
// subscriptionsClient struct holds the client interface and namespace
|
||||
type subscriptionsClient struct {
|
||||
client clientv1beta1.SubscriptionInterface
|
||||
namespace string
|
||||
}
|
||||
|
||||
// newKnSubscriptionsClient returns kn subscriptions client
|
||||
func newKnSubscriptionsClient(client clientv1beta1.SubscriptionInterface, namespace string) KnSubscriptionsClient {
|
||||
return &subscriptionsClient{
|
||||
client: client,
|
||||
namespace: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
// Get the namespace for which this client is created
|
||||
func (c *subscriptionsClient) Namespace() string {
|
||||
return c.namespace
|
||||
}
|
||||
|
||||
// GetSubscription gets Subscription by its name
|
||||
func (c *subscriptionsClient) GetSubscription(name string) (*v1beta1.Subscription, error) {
|
||||
subscription, err := c.client.Get(context.TODO(), name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, knerrors.GetError(err)
|
||||
}
|
||||
err = updateMessagingGVK(subscription)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return subscription, nil
|
||||
}
|
||||
|
||||
// CreateSubscription creates Subscription with given spec
|
||||
func (c *subscriptionsClient) CreateSubscription(subscription *v1beta1.Subscription) error {
|
||||
_, err := c.client.Create(context.TODO(), subscription, metav1.CreateOptions{})
|
||||
return knerrors.GetError(err)
|
||||
}
|
||||
|
||||
// UpdateSubscription creates Subscription with given spec
|
||||
func (c *subscriptionsClient) UpdateSubscription(subscription *v1beta1.Subscription) error {
|
||||
_, err := c.client.Update(context.TODO(), subscription, metav1.UpdateOptions{})
|
||||
return knerrors.GetError(err)
|
||||
}
|
||||
|
||||
// DeleteSubscription deletes Subscription by its name
|
||||
func (c *subscriptionsClient) DeleteSubscription(name string) error {
|
||||
return knerrors.GetError(c.client.Delete(context.TODO(), name, metav1.DeleteOptions{}))
|
||||
}
|
||||
|
||||
// ListSubscription lists subscriptions in configured namespace
|
||||
func (c *subscriptionsClient) ListSubscription() (*v1beta1.SubscriptionList, error) {
|
||||
subscriptionList, err := c.client.List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, knerrors.GetError(err)
|
||||
}
|
||||
|
||||
return updateSubscriptionListGVK(subscriptionList)
|
||||
}
|
||||
|
||||
func updateSubscriptionListGVK(subscriptionList *v1beta1.SubscriptionList) (*v1beta1.SubscriptionList, error) {
|
||||
subscriptionListNew := subscriptionList.DeepCopy()
|
||||
err := updateMessagingGVK(subscriptionListNew)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subscriptionListNew.Items = make([]v1beta1.Subscription, len(subscriptionList.Items))
|
||||
for idx, subscription := range subscriptionList.Items {
|
||||
subscriptionClone := subscription.DeepCopy()
|
||||
err := updateMessagingGVK(subscriptionClone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subscriptionListNew.Items[idx] = *subscriptionClone
|
||||
}
|
||||
return subscriptionListNew, nil
|
||||
}
|
||||
|
||||
// SubscriptionBuilder is for building the Subscription object
|
||||
type SubscriptionBuilder struct {
|
||||
subscription *v1beta1.Subscription
|
||||
}
|
||||
|
||||
// NewSubscriptionBuilder for building Subscription object
|
||||
func NewSubscriptionBuilder(name string) *SubscriptionBuilder {
|
||||
return &SubscriptionBuilder{subscription: &v1beta1.Subscription{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
// NewSubscriptionBuilderFromExisting for building Subscription object from existing Subscription object
|
||||
func NewSubscriptionBuilderFromExisting(subs *v1beta1.Subscription) *SubscriptionBuilder {
|
||||
return &SubscriptionBuilder{subscription: subs.DeepCopy()}
|
||||
}
|
||||
|
||||
// Channel sets the channel reference for this subscription
|
||||
func (s *SubscriptionBuilder) Channel(channel *corev1.ObjectReference) *SubscriptionBuilder {
|
||||
if channel == nil {
|
||||
return s
|
||||
}
|
||||
|
||||
s.subscription.Spec.Channel = *channel
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *SubscriptionBuilder) Subscriber(subs *duckv1.Destination) *SubscriptionBuilder {
|
||||
if subs == nil {
|
||||
return s
|
||||
}
|
||||
|
||||
s.subscription.Spec.Subscriber = subs
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *SubscriptionBuilder) Reply(reply *duckv1.Destination) *SubscriptionBuilder {
|
||||
if reply == nil {
|
||||
return s
|
||||
}
|
||||
|
||||
s.subscription.Spec.Reply = reply
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *SubscriptionBuilder) DeadLetterSink(dls *duckv1.Destination) *SubscriptionBuilder {
|
||||
if dls == nil {
|
||||
return s
|
||||
}
|
||||
|
||||
ds := &eventingduckv1beta1.DeliverySpec{}
|
||||
ds.DeadLetterSink = dls
|
||||
s.subscription.Spec.Delivery = ds
|
||||
return s
|
||||
}
|
||||
|
||||
// Build returns the Subscription object from the builder
|
||||
func (s *SubscriptionBuilder) Build() *v1beta1.Subscription {
|
||||
return s.subscription
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"knative.dev/eventing/pkg/apis/messaging/v1beta1"
|
||||
|
||||
"knative.dev/client/pkg/util/mock"
|
||||
)
|
||||
|
||||
type MockKnSubscriptionsClient struct {
|
||||
t *testing.T
|
||||
recorder *SubscriptionsRecorder
|
||||
namespace string
|
||||
}
|
||||
|
||||
// NewMockKnSubscriptionsClient returns a new mock instance which you need to record for
|
||||
func NewMockKnSubscriptionsClient(t *testing.T, ns ...string) *MockKnSubscriptionsClient {
|
||||
namespace := "default"
|
||||
if len(ns) > 0 {
|
||||
namespace = ns[0]
|
||||
}
|
||||
return &MockKnSubscriptionsClient{
|
||||
t: t,
|
||||
recorder: &SubscriptionsRecorder{mock.NewRecorder(t, namespace)},
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the interface is implemented
|
||||
var _ KnSubscriptionsClient = &MockKnSubscriptionsClient{}
|
||||
|
||||
// recorder for service
|
||||
type SubscriptionsRecorder struct {
|
||||
r *mock.Recorder
|
||||
}
|
||||
|
||||
// Recorder returns the recorder for registering API calls
|
||||
func (c *MockKnSubscriptionsClient) Recorder() *SubscriptionsRecorder {
|
||||
return c.recorder
|
||||
}
|
||||
|
||||
// Namespace of this client
|
||||
func (c *MockKnSubscriptionsClient) Namespace() string {
|
||||
return c.recorder.r.Namespace()
|
||||
}
|
||||
|
||||
// CreateSubscription records a call for CreateSubscription with the expected error
|
||||
func (sr *SubscriptionsRecorder) CreateSubscription(subscription interface{}, err error) {
|
||||
sr.r.Add("CreateSubscription", []interface{}{subscription}, []interface{}{err})
|
||||
}
|
||||
|
||||
// CreateSubscription performs a previously recorded action, failing if non has been registered
|
||||
func (c *MockKnSubscriptionsClient) CreateSubscription(subscription *v1beta1.Subscription) error {
|
||||
call := c.recorder.r.VerifyCall("CreateSubscription", subscription)
|
||||
return mock.ErrorOrNil(call.Result[0])
|
||||
}
|
||||
|
||||
// GetSubscription records a call for GetSubscription with the expected object or error. Either subscriptions or err should be nil
|
||||
func (sr *SubscriptionsRecorder) GetSubscription(name interface{}, subscription *v1beta1.Subscription, err error) {
|
||||
sr.r.Add("GetSubscription", []interface{}{name}, []interface{}{subscription, err})
|
||||
}
|
||||
|
||||
// GetSubscription performs a previously recorded action, failing if non has been registered
|
||||
func (c *MockKnSubscriptionsClient) GetSubscription(name string) (*v1beta1.Subscription, error) {
|
||||
call := c.recorder.r.VerifyCall("GetSubscription", name)
|
||||
return call.Result[0].(*v1beta1.Subscription), mock.ErrorOrNil(call.Result[1])
|
||||
}
|
||||
|
||||
// DeleteSubscription records a call for DeleteSubscription with the expected error (nil if none)
|
||||
func (sr *SubscriptionsRecorder) DeleteSubscription(name interface{}, err error) {
|
||||
sr.r.Add("DeleteSubscription", []interface{}{name}, []interface{}{err})
|
||||
}
|
||||
|
||||
// DeleteSubscription performs a previously recorded action, failing if non has been registered
|
||||
func (c *MockKnSubscriptionsClient) DeleteSubscription(name string) error {
|
||||
call := c.recorder.r.VerifyCall("DeleteSubscription", name)
|
||||
return mock.ErrorOrNil(call.Result[0])
|
||||
}
|
||||
|
||||
// ListSubscription records a call for ListSubscription with the expected error (nil if none)
|
||||
func (sr *SubscriptionsRecorder) ListSubscription(subscriptionsList *v1beta1.SubscriptionList, err error) {
|
||||
sr.r.Add("ListSubscription", []interface{}{}, []interface{}{subscriptionsList, err})
|
||||
}
|
||||
|
||||
// ListSubscription performs a previously recorded action, failing if non has been registered
|
||||
func (c *MockKnSubscriptionsClient) ListSubscription() (*v1beta1.SubscriptionList, error) {
|
||||
call := c.recorder.r.VerifyCall("ListSubscription")
|
||||
return call.Result[0].(*v1beta1.SubscriptionList), mock.ErrorOrNil(call.Result[1])
|
||||
}
|
||||
|
||||
// UpdateSubscription records a call for CreateSubscription with the expected error
|
||||
func (sr *SubscriptionsRecorder) UpdateSubscription(subscription interface{}, err error) {
|
||||
sr.r.Add("UpdateSubscription", []interface{}{subscription}, []interface{}{err})
|
||||
}
|
||||
|
||||
// UpdateSubscription performs a previously recorded action, failing if non has been registered
|
||||
func (c *MockKnSubscriptionsClient) UpdateSubscription(subscription *v1beta1.Subscription) error {
|
||||
call := c.recorder.r.VerifyCall("UpdateSubscription", subscription)
|
||||
return mock.ErrorOrNil(call.Result[0])
|
||||
}
|
||||
|
||||
// Validates validates whether every recorded action has been called
|
||||
func (sr *SubscriptionsRecorder) Validate() {
|
||||
sr.r.CheckThatAllRecordedMethodsHaveBeenCalled()
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// 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 im
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build e2e
|
||||
// +build !serving
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
|
||||
"knative.dev/client/lib/test"
|
||||
"knative.dev/client/pkg/util"
|
||||
)
|
||||
|
||||
func TestSubscriptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
it, err := test.NewKnTest()
|
||||
assert.NilError(t, err)
|
||||
defer func() {
|
||||
assert.NilError(t, it.Teardown())
|
||||
}()
|
||||
|
||||
r := test.NewKnRunResultCollector(t, it)
|
||||
defer r.DumpIfFailed()
|
||||
|
||||
t.Log("Create a subscription with all the flags")
|
||||
test.ChannelCreate(r, "c0")
|
||||
test.ServiceCreate(r, "svc0")
|
||||
test.ServiceCreate(r, "svc1")
|
||||
test.ServiceCreate(r, "svc2")
|
||||
test.SubscriptionCreate(r, "sub0", "--channel", "c0", "--sink", "ksvc:svc0", "--sink-reply", "ksvc:svc1", "--sink-dead-letter", "ksvc:svc2")
|
||||
|
||||
t.Log("Update a subscription")
|
||||
test.ServiceCreate(r, "svc3")
|
||||
test.SubscriptionUpdate(r, "sub0", "--sink", "ksvc:svc3")
|
||||
|
||||
t.Log("List subscriptions")
|
||||
slist := test.SubscriptionList(r)
|
||||
assert.Check(t, util.ContainsAll(slist, "NAME", "CHANNEL", "SUBSCRIBER", "REPLY", "DEAD LETTER SINK", "READY", "REASON"))
|
||||
assert.Check(t, util.ContainsAll(slist, "sub0", "c0", "ksvc:svc3", "ksvc:svc1", "ksvc:svc2", "True"))
|
||||
|
||||
t.Log("Describe subscription")
|
||||
sdesc := test.SubscriptionDescribe(r, "sub0")
|
||||
assert.Check(t, util.ContainsAll(sdesc, "sub0", "Age", "Channel", "Channel", "c0", "Subscriber", "svc3", "Resource", "Service", "serving.knative.dev/v1", "Reply", "svc1", "DeadLetterSink", "svc2", "Conditions"))
|
||||
|
||||
t.Log("Delete subscription")
|
||||
test.SubscriptionDelete(r, "sub0")
|
||||
test.ServiceDelete(r, "svc0")
|
||||
test.ServiceDelete(r, "svc1")
|
||||
test.ServiceDelete(r, "svc2")
|
||||
test.ServiceDelete(r, "svc3")
|
||||
test.ChannelDelete(r, "c0")
|
||||
}
|
||||
Loading…
Reference in New Issue