mirror of https://github.com/knative/client.git
💝 Extract reusable part of sink flag (#1968)
* Extract reuseable part of sink flag * Return nil on empty sink * Extract kube params * Compute with default mappings * Allow to change the default log level * Publicate Zap based logger, and allow custom loggers to be used.
This commit is contained in:
parent
fd0126d099
commit
c9f128423b
|
@ -26,7 +26,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:sourcesv1:key=value".
|
||||
"LabelSelector" is a list of comma separated key value pairs. "LabelSelector" can be omitted, e.g. "Event:sourcesv1".
|
||||
--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, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
@ -26,7 +26,7 @@ kn source apiserver update NAME
|
|||
--resource stringArray Specification for which events to listen, in the format Kind:APIVersion:LabelSelector, e.g. "Event:sourcesv1:key=value".
|
||||
"LabelSelector" is a list of comma separated key value pairs. "LabelSelector" can be omitted, e.g. "Event:sourcesv1".
|
||||
--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, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
@ -20,7 +20,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, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--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
|
||||
```
|
||||
|
||||
|
|
|
@ -20,7 +20,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, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--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
|
||||
```
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ kn source container create NAME --image IMAGE --sink SINK
|
|||
--request strings The resource requirement requests for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource request, append "-" to the resource name, e.g. '--request cpu-'.
|
||||
--security-context string Predefined security context for the service. Accepted values: 'none' for no security context and 'strict' for dropping all capabilities, running as non-root, and no privilege escalation. (default "none")
|
||||
--service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--toleration strings Add toleration to be set, works if the feature gate is enabled in Knative Serving feature flags configuration. Example: --tolerations Key="key1",Operator="Equal",Value="value1",Effect="NoSchedule"
|
||||
--user int The user ID to run the container (e.g., 1001).
|
||||
--volume stringArray Add a volume from a ConfigMap (prefix cm: or config-map:) a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:) or a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim). PersistentVolumeClaim only works if the feature gate is enabled in Knative Serving feature flags configuration. Example: --volume myvolume=cm:myconfigmap, --volume myvolume=secret:mysecret or --volume emptyDir:myvol:size=1Gi,type=Memory. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-.
|
||||
|
|
|
@ -41,7 +41,7 @@ kn source container update NAME --image IMAGE
|
|||
--request strings The resource requirement requests for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource request, append "-" to the resource name, e.g. '--request cpu-'.
|
||||
--security-context string Predefined security context for the service. Accepted values: 'none' for no security context and 'strict' for dropping all capabilities, running as non-root, and no privilege escalation. (default "none")
|
||||
--service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--toleration strings Add toleration to be set, works if the feature gate is enabled in Knative Serving feature flags configuration. Example: --tolerations Key="key1",Operator="Equal",Value="value1",Effect="NoSchedule"
|
||||
--user int The user ID to run the container (e.g., 1001).
|
||||
--volume stringArray Add a volume from a ConfigMap (prefix cm: or config-map:) a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:) or a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim). PersistentVolumeClaim only works if the feature gate is enabled in Knative Serving feature flags configuration. Example: --volume myvolume=cm:myconfigmap, --volume myvolume=secret:mysecret or --volume emptyDir:myvol:size=1Gi,type=Memory. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-.
|
||||
|
|
|
@ -23,7 +23,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, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
@ -23,7 +23,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, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
@ -23,9 +23,9 @@ kn subscription create NAME
|
|||
--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:v1beta1: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, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--sink-dead-letter string Addressable sink for events. You can specify a broker, channel, Knative service or URI. Examples: '--sink-dead-letter broker:nest' for a broker 'nest', '--sink-dead-letter channel:pipe' for a channel 'pipe', '--sink-dead-letter ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink-dead-letter https://event.receiver.uri' for an HTTP URI, '--sink-dead-letter ksvc:receiver' or simply '--sink-dead-letter receiver' for a Knative service 'receiver' in the current namespace. '--sink-dead-letter special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--sink-reply string Addressable sink for events. You can specify a broker, channel, Knative service or URI. Examples: '--sink-reply broker:nest' for a broker 'nest', '--sink-reply channel:pipe' for a channel 'pipe', '--sink-reply ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink-reply https://event.receiver.uri' for an HTTP URI, '--sink-reply ksvc:receiver' or simply '--sink-reply receiver' for a Knative service 'receiver' in the current namespace. '--sink-reply special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--sink-dead-letter string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink-dead-letter broker:nest' for a broker 'nest', '--sink-dead-letter channel:pipe' for a channel 'pipe', '--sink-dead-letter ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink-dead-letter https://event.receiver.uri' for an HTTP URI, '--sink-dead-letter ksvc:receiver' or simply '--sink-dead-letter receiver' for a Knative service 'receiver' in the current namespace, '--sink-dead-letter svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink-dead-letter special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--sink-reply string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink-reply broker:nest' for a broker 'nest', '--sink-reply channel:pipe' for a channel 'pipe', '--sink-reply ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink-reply https://event.receiver.uri' for an HTTP URI, '--sink-reply ksvc:receiver' or simply '--sink-reply receiver' for a Knative service 'receiver' in the current namespace, '--sink-reply svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink-reply special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
@ -22,9 +22,9 @@ kn subscription update NAME
|
|||
```
|
||||
-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, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--sink-dead-letter string Addressable sink for events. You can specify a broker, channel, Knative service or URI. Examples: '--sink-dead-letter broker:nest' for a broker 'nest', '--sink-dead-letter channel:pipe' for a channel 'pipe', '--sink-dead-letter ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink-dead-letter https://event.receiver.uri' for an HTTP URI, '--sink-dead-letter ksvc:receiver' or simply '--sink-dead-letter receiver' for a Knative service 'receiver' in the current namespace. '--sink-dead-letter special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--sink-reply string Addressable sink for events. You can specify a broker, channel, Knative service or URI. Examples: '--sink-reply broker:nest' for a broker 'nest', '--sink-reply channel:pipe' for a channel 'pipe', '--sink-reply ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink-reply https://event.receiver.uri' for an HTTP URI, '--sink-reply ksvc:receiver' or simply '--sink-reply receiver' for a Knative service 'receiver' in the current namespace. '--sink-reply special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--sink-dead-letter string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink-dead-letter broker:nest' for a broker 'nest', '--sink-dead-letter channel:pipe' for a channel 'pipe', '--sink-dead-letter ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink-dead-letter https://event.receiver.uri' for an HTTP URI, '--sink-dead-letter ksvc:receiver' or simply '--sink-dead-letter receiver' for a Knative service 'receiver' in the current namespace, '--sink-dead-letter svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink-dead-letter special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
--sink-reply string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink-reply broker:nest' for a broker 'nest', '--sink-reply channel:pipe' for a channel 'pipe', '--sink-reply ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink-reply https://event.receiver.uri' for an HTTP URI, '--sink-reply ksvc:receiver' or simply '--sink-reply receiver' for a Knative service 'receiver' in the current namespace, '--sink-reply svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink-reply special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
@ -24,7 +24,7 @@ kn trigger create NAME --sink SINK
|
|||
--filter strings Key-value pair for exact CloudEvent attribute matching against incoming events, e.g type=dev.knative.foo
|
||||
-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, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
|
@ -27,7 +27,7 @@ kn trigger update NAME
|
|||
--filter strings Key-value pair for exact CloudEvent attribute matching against incoming events, e.g type=dev.knative.foo
|
||||
-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, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
-s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service, Kubernetes service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an HTTP URI, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace, '--sink svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, '--sink special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. If a prefix is not provided, it is considered as a Knative service in the current namespace.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
|
1
go.mod
1
go.mod
|
@ -21,6 +21,7 @@ replace knative.dev/client/pkg => ./pkg
|
|||
require (
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d // indirect
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect
|
||||
emperror.dev/errors v0.8.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -40,6 +40,8 @@ contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d/g
|
|||
contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg=
|
||||
contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
emperror.dev/errors v0.8.1 h1:UavXZ5cSX/4u9iyvH6aDcuGkVjeexUGJ7Ij7G4VfQT0=
|
||||
emperror.dev/errors v0.8.1/go.mod h1:YcRvLPh626Ubn2xqtoprejnA5nFha+TJ+2vew48kWuE=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
|
@ -421,11 +423,13 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
|||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY=
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
|
|
|
@ -16,19 +16,18 @@ package flags
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"knative.dev/client/pkg/config"
|
||||
"knative.dev/pkg/apis"
|
||||
duckv1 "knative.dev/pkg/apis/duck/v1"
|
||||
|
||||
clientdynamic "knative.dev/client/pkg/dynamic"
|
||||
"knative.dev/client/pkg/flags/sink"
|
||||
"knative.dev/client/pkg/util/errors"
|
||||
duckv1 "knative.dev/pkg/apis/duck/v1"
|
||||
)
|
||||
|
||||
// SinkFlags holds information about given sink together with optional mappings
|
||||
// to allow ease of referencing the common types.
|
||||
type SinkFlags struct {
|
||||
Sink string
|
||||
SinkMappings map[string]schema.GroupVersionResource
|
||||
|
@ -42,156 +41,69 @@ func NewSinkFlag(mapping map[string]schema.GroupVersionResource) *SinkFlags {
|
|||
}
|
||||
|
||||
// AddWithFlagName configures Sink flag with given flag name and a short flag name
|
||||
// pass empty short flag name if you don't want to set one
|
||||
// pass empty short flag name if you don't want to set one.
|
||||
func (i *SinkFlags) AddWithFlagName(cmd *cobra.Command, fname, short string) {
|
||||
flag := "--" + fname
|
||||
i.AddToFlagSet(cmd.Flags(), fname, short)
|
||||
}
|
||||
|
||||
// AddToFlagSet configures Sink flag with given flag name and a short flag name
|
||||
// pass empty short flag name if you don't want to set one
|
||||
func (i *SinkFlags) AddToFlagSet(fs *pflag.FlagSet, fname, short string) {
|
||||
if short == "" {
|
||||
cmd.Flags().StringVar(&i.Sink, fname, "", "")
|
||||
fs.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, channel, Knative service or URI. " +
|
||||
"Examples: '" + flag + " broker:nest' for a broker 'nest', " +
|
||||
"'" + flag + " channel:pipe' for a channel 'pipe', " +
|
||||
"'" + flag + " ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', " +
|
||||
"'" + flag + " https://event.receiver.uri' for an HTTP URI, " +
|
||||
"'" + flag + " ksvc:receiver' or simply '" + flag + " receiver' for a Knative service 'receiver' in the current namespace. " +
|
||||
"'" + flag + " special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. " +
|
||||
"If a prefix is not provided, it is considered as a Knative service in the current namespace."
|
||||
// Use default mapping if empty
|
||||
if i.SinkMappings == nil {
|
||||
i.SinkMappings = defaultSinkMappings
|
||||
}
|
||||
for _, p := range config.GlobalConfig.SinkMappings() {
|
||||
//user configuration might override the default configuration
|
||||
i.SinkMappings[p.Prefix] = schema.GroupVersionResource{
|
||||
Resource: p.Resource,
|
||||
Group: p.Group,
|
||||
Version: p.Version,
|
||||
}
|
||||
fs.StringVarP(&i.Sink, fname, short, "", "")
|
||||
}
|
||||
fs.Lookup(fname).Usage = sink.Usage(fname)
|
||||
}
|
||||
|
||||
// Add configures Sink flag with name 'Sink' amd short name 's'
|
||||
func (i *SinkFlags) Add(cmd *cobra.Command) {
|
||||
i.AddWithFlagName(cmd, "sink", "s")
|
||||
i.AddWithFlagName(cmd, sink.DefaultFlagName, sink.DefaultFlagShorthand)
|
||||
}
|
||||
|
||||
// SinkPrefixes maps prefixes used for sinks to their GroupVersionResources.
|
||||
var defaultSinkMappings = map[string]schema.GroupVersionResource{
|
||||
"broker": {
|
||||
Resource: "brokers",
|
||||
Group: "eventing.knative.dev",
|
||||
Version: "v1",
|
||||
},
|
||||
// Shorthand alias for service
|
||||
"ksvc": {
|
||||
Resource: "services",
|
||||
Group: "serving.knative.dev",
|
||||
Version: "v1",
|
||||
},
|
||||
"channel": {
|
||||
Resource: "channels",
|
||||
Group: "messaging.knative.dev",
|
||||
Version: "v1",
|
||||
},
|
||||
// WithDefaultMappings will return a copy of SinkFlags with provided mappings
|
||||
// and the default ones.
|
||||
func (i *SinkFlags) WithDefaultMappings() *SinkFlags {
|
||||
return &SinkFlags{
|
||||
Sink: i.Sink,
|
||||
SinkMappings: sink.ComputeWithDefaultMappings(i.SinkMappings),
|
||||
}
|
||||
}
|
||||
|
||||
// Parse returns the sink reference, which may refer to URL or to Kubernetes
|
||||
// resource. The namespace given should be the current namespace within the
|
||||
// context.
|
||||
func (i *SinkFlags) Parse(namespace string) (*sink.Reference, error) {
|
||||
// Use default mapping if empty
|
||||
sf := i.WithDefaultMappings()
|
||||
return sink.Parse(sf.Sink, namespace, sf.SinkMappings)
|
||||
}
|
||||
|
||||
// ResolveSink returns the Destination referred to by the flags in the acceptor.
|
||||
// It validates that any object the user is referring to exists.
|
||||
func (i *SinkFlags) ResolveSink(ctx context.Context, knclient clientdynamic.KnDynamicClient, namespace string) (*duckv1.Destination, error) {
|
||||
client := knclient.RawClient()
|
||||
if i.Sink == "" {
|
||||
return nil, nil
|
||||
}
|
||||
// Use default mapping if empty
|
||||
if i.SinkMappings == nil {
|
||||
i.SinkMappings = defaultSinkMappings
|
||||
}
|
||||
prefix, name, ns := parseSink(i.Sink)
|
||||
if prefix == "" {
|
||||
// URI target
|
||||
uri, err := apis.ParseURL(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &duckv1.Destination{URI: uri}, nil
|
||||
}
|
||||
gvr, ok := i.SinkMappings[prefix]
|
||||
if !ok {
|
||||
if prefix == "svc" || prefix == "service" {
|
||||
return nil, fmt.Errorf("unsupported Sink prefix: '%s', please use prefix 'ksvc' for knative service", prefix)
|
||||
}
|
||||
idx := strings.LastIndex(prefix, "/")
|
||||
var groupVersion string
|
||||
var kind string
|
||||
if idx != -1 && idx < len(prefix)-1 {
|
||||
groupVersion, kind = prefix[:idx], prefix[idx+1:]
|
||||
} else {
|
||||
kind = prefix
|
||||
}
|
||||
parsedVersion, err := schema.ParseGroupVersion(groupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// For the RAWclient the resource name must be in lower case plural form.
|
||||
// This is the best effort to sanitize the inputs, but the safest way is to provide
|
||||
// the appropriate form in user's input.
|
||||
if !strings.HasSuffix(kind, "s") {
|
||||
kind = kind + "s"
|
||||
}
|
||||
kind = strings.ToLower(kind)
|
||||
gvr = parsedVersion.WithResource(kind)
|
||||
}
|
||||
if ns != "" {
|
||||
namespace = ns
|
||||
}
|
||||
obj, err := client.Resource(gvr).Namespace(namespace).Get(ctx, name, metav1.GetOptions{})
|
||||
s, err := i.Parse(namespace)
|
||||
if err != nil {
|
||||
if errors.Is(err, sink.ErrSinkIsRequired) {
|
||||
// returns nil, if sink isn't provided to keep the current contract
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
destination := &duckv1.Destination{
|
||||
Ref: &duckv1.KReference{
|
||||
Kind: obj.GetKind(),
|
||||
APIVersion: obj.GetAPIVersion(),
|
||||
Name: obj.GetName(),
|
||||
Namespace: namespace,
|
||||
},
|
||||
}
|
||||
return destination, nil
|
||||
}
|
||||
|
||||
// parseSink takes the string given by the user into the prefix, name and namespace of
|
||||
// the object. If the user put a URI instead, the prefix is empty and the name
|
||||
// is the whole URI.
|
||||
func parseSink(sink string) (string, string, string) {
|
||||
parts := strings.SplitN(sink, ":", 3)
|
||||
switch {
|
||||
case len(parts) == 1:
|
||||
return "ksvc", parts[0], ""
|
||||
case parts[0] == "http" || parts[0] == "https":
|
||||
return "", sink, ""
|
||||
case len(parts) == 3:
|
||||
return parts[0], parts[1], parts[2]
|
||||
default:
|
||||
return parts[0], parts[1], ""
|
||||
var dest *duckv1.Destination
|
||||
dest, err = s.Resolve(ctx, knclient)
|
||||
if err != nil {
|
||||
// Returning original error that caused sink.ErrSinkIsInvalid as it is
|
||||
// directly presented to the end-user.
|
||||
return nil, errors.CauseOf(err, sink.ErrSinkIsInvalid)
|
||||
}
|
||||
return dest, nil
|
||||
}
|
||||
|
||||
// SinkToString prepares a Sink for list output
|
||||
func SinkToString(sink duckv1.Destination) string {
|
||||
if sink.Ref != nil {
|
||||
if sink.Ref.Kind == "Service" && strings.HasPrefix(sink.Ref.APIVersion, defaultSinkMappings["ksvc"].Group) {
|
||||
return fmt.Sprintf("ksvc:%s", sink.Ref.Name)
|
||||
} else {
|
||||
return fmt.Sprintf("%s:%s", strings.ToLower(sink.Ref.Kind), sink.Ref.Name)
|
||||
}
|
||||
}
|
||||
if sink.URI != nil {
|
||||
return sink.URI.String()
|
||||
}
|
||||
return ""
|
||||
// Deprecated: use (*sink.Reference).AsText instead.
|
||||
func SinkToString(dest duckv1.Destination) string {
|
||||
ref := sink.GuessFromDestination(dest)
|
||||
return ref.String()
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package flags
|
||||
package flags_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -20,7 +20,9 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
"gotest.tools/v3/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"knative.dev/client/pkg/commands/flags"
|
||||
eventingv1 "knative.dev/eventing/pkg/apis/eventing/v1"
|
||||
messagingv1 "knative.dev/eventing/pkg/apis/messaging/v1"
|
||||
"knative.dev/eventing/pkg/apis/sources/v1beta2"
|
||||
|
@ -44,37 +46,33 @@ type sinkFlagAddTestCases struct {
|
|||
}
|
||||
|
||||
func TestSinkFlagAdd(t *testing.T) {
|
||||
cases := []*sinkFlagAddTestCases{
|
||||
{
|
||||
"",
|
||||
"sink",
|
||||
"s",
|
||||
},
|
||||
{
|
||||
"subscriber",
|
||||
"subscriber",
|
||||
"",
|
||||
},
|
||||
}
|
||||
cases := []sinkFlagAddTestCases{{
|
||||
"",
|
||||
"sink",
|
||||
"s",
|
||||
}, {
|
||||
"subscriber",
|
||||
"subscriber",
|
||||
"",
|
||||
}}
|
||||
for _, tc := range cases {
|
||||
c := &cobra.Command{Use: "sinktest"}
|
||||
sinkFlags := 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)
|
||||
}
|
||||
t.Run(tc.flagName, func(t *testing.T) {
|
||||
c := &cobra.Command{Use: "sinktest"}
|
||||
sinkFlags := flags.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")
|
||||
assert.NilError(t, err)
|
||||
|
||||
mysvc := &servingv1.Service{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "Service", APIVersion: "serving.knative.dev/v1"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "default"},
|
||||
|
@ -91,6 +89,10 @@ func TestResolve(t *testing.T) {
|
|||
TypeMeta: metav1.TypeMeta{Kind: "PingSource", APIVersion: "sources.knative.dev/v1"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||
}
|
||||
k8sService := &corev1.Service{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "Service", APIVersion: "v1"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
|
||||
}
|
||||
cases := []resolveCase{
|
||||
{"ksvc:mysvc", &duckv1.Destination{
|
||||
Ref: &duckv1.KReference{Kind: "Service",
|
||||
|
@ -143,24 +145,42 @@ func TestResolve(t *testing.T) {
|
|||
Name: "foo",
|
||||
}}, ""},
|
||||
{"http://target.example.com", &duckv1.Destination{
|
||||
URI: targetExampleCom,
|
||||
URI: url(t, "http://target.example.com"),
|
||||
}, ""},
|
||||
{"k8ssvc:foo", nil, "k8ssvcs \"foo\" not found"},
|
||||
{"svc:foo", nil, "please use prefix 'ksvc' for knative service"},
|
||||
{"service:foo", nil, "please use prefix 'ksvc' for knative service"},
|
||||
{"svc:foo", &duckv1.Destination{Ref: &duckv1.KReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Service",
|
||||
Namespace: "default",
|
||||
Name: "foo",
|
||||
}}, ""},
|
||||
{"service:foo", &duckv1.Destination{Ref: &duckv1.KReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Service",
|
||||
Namespace: "default",
|
||||
Name: "foo",
|
||||
}}, ""},
|
||||
{"absent:foo", nil, "absents \"foo\" not found"},
|
||||
{"", nil, ""},
|
||||
}
|
||||
dynamicClient := dynamicfake.CreateFakeKnDynamicClient("default", mysvc, defaultBroker, pipeChannel, pingSource)
|
||||
dynamicClient := dynamicfake.CreateFakeKnDynamicClient(
|
||||
"default",
|
||||
mysvc, defaultBroker, pipeChannel, pingSource, k8sService,
|
||||
)
|
||||
|
||||
for _, c := range cases {
|
||||
i := &SinkFlags{Sink: c.sink}
|
||||
result, err := i.ResolveSink(context.Background(), dynamicClient, "default")
|
||||
if c.destination != nil {
|
||||
assert.DeepEqual(t, result, c.destination)
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
assert.ErrorContains(t, err, c.errContents)
|
||||
}
|
||||
t.Run(c.sink, func(t *testing.T) {
|
||||
sf := &flags.SinkFlags{Sink: c.sink}
|
||||
result, err := sf.ResolveSink(context.Background(), dynamicClient, "default")
|
||||
if c.errContents == "" {
|
||||
assert.DeepEqual(t, result, c.destination)
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
assert.Check(t, err != nil && err.Error() != "",
|
||||
"error ins't empty")
|
||||
assert.ErrorContains(t, err, c.errContents)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,47 +217,73 @@ func TestResolveWithNamespace(t *testing.T) {
|
|||
}
|
||||
dynamicClient := dynamicfake.CreateFakeKnDynamicClient("my-namespace", mysvc, defaultBroker, pipeChannel)
|
||||
for _, c := range cases {
|
||||
i := &SinkFlags{Sink: c.sink}
|
||||
result, err := i.ResolveSink(context.Background(), dynamicClient, "default")
|
||||
if c.destination != nil {
|
||||
assert.DeepEqual(t, result, c.destination)
|
||||
assert.Equal(t, c.destination.Ref.Namespace, "my-namespace")
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
assert.ErrorContains(t, err, c.errContents)
|
||||
}
|
||||
t.Run(c.sink, func(t *testing.T) {
|
||||
i := &flags.SinkFlags{Sink: c.sink}
|
||||
result, err := i.ResolveSink(context.Background(), dynamicClient, "default")
|
||||
if c.destination != nil {
|
||||
assert.DeepEqual(t, result, c.destination)
|
||||
assert.Equal(t, c.destination.Ref.Namespace, "my-namespace")
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
assert.ErrorContains(t, err, c.errContents)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSinkToString(t *testing.T) {
|
||||
sink := duckv1.Destination{
|
||||
Ref: &duckv1.KReference{Kind: "Service",
|
||||
tcs := []resolveCase{{
|
||||
sink: "ksvc:mysvc",
|
||||
destination: &duckv1.Destination{Ref: &duckv1.KReference{
|
||||
Kind: "Service",
|
||||
APIVersion: "serving.knative.dev/v1",
|
||||
Namespace: "my-namespace",
|
||||
Name: "mysvc"}}
|
||||
expected := "ksvc:mysvc"
|
||||
assert.Equal(t, expected, SinkToString(sink))
|
||||
sink = duckv1.Destination{
|
||||
Ref: &duckv1.KReference{Kind: "Broker",
|
||||
Name: "mysvc",
|
||||
}},
|
||||
}, {
|
||||
sink: "broker:default",
|
||||
destination: &duckv1.Destination{Ref: &duckv1.KReference{
|
||||
Kind: "Broker",
|
||||
APIVersion: "eventing.knative.dev/v1",
|
||||
Namespace: "my-namespace",
|
||||
Name: "default"}}
|
||||
expected = "broker:default"
|
||||
assert.Equal(t, expected, SinkToString(sink))
|
||||
sink = duckv1.Destination{
|
||||
Ref: &duckv1.KReference{Kind: "Service",
|
||||
Name: "default",
|
||||
}},
|
||||
}, {
|
||||
sink: "svc:mysvc",
|
||||
destination: &duckv1.Destination{Ref: &duckv1.KReference{
|
||||
Kind: "Service",
|
||||
APIVersion: "v1",
|
||||
Namespace: "my-namespace",
|
||||
Name: "mysvc"}}
|
||||
expected = "service:mysvc"
|
||||
assert.Equal(t, expected, SinkToString(sink))
|
||||
|
||||
uri := "http://target.example.com"
|
||||
targetExampleCom, err := apis.ParseURL(uri)
|
||||
assert.NilError(t, err)
|
||||
sink = duckv1.Destination{
|
||||
URI: targetExampleCom,
|
||||
Name: "mysvc",
|
||||
}},
|
||||
}, {
|
||||
sink: "things.acme.dev/v1alpha1:abc",
|
||||
destination: &duckv1.Destination{Ref: &duckv1.KReference{
|
||||
Kind: "Thing",
|
||||
APIVersion: "acme.dev/v1alpha1",
|
||||
Namespace: "my-namespace",
|
||||
Name: "abc",
|
||||
}},
|
||||
}, {
|
||||
sink: "http://target.example.com",
|
||||
destination: &duckv1.Destination{
|
||||
URI: url(t, "http://target.example.com"),
|
||||
},
|
||||
}, {
|
||||
sink: "",
|
||||
destination: &duckv1.Destination{},
|
||||
}}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.sink, func(t *testing.T) {
|
||||
got := flags.SinkToString(*tc.destination)
|
||||
assert.Equal(t, got, tc.sink)
|
||||
})
|
||||
}
|
||||
assert.Equal(t, uri, SinkToString(sink))
|
||||
assert.Equal(t, "", SinkToString(duckv1.Destination{}))
|
||||
}
|
||||
|
||||
func url(t testing.TB, uri string) *apis.URL {
|
||||
t.Helper()
|
||||
u, err := apis.ParseURL(uri)
|
||||
assert.NilError(t, err)
|
||||
return u
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"gotest.tools/v3/assert"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"knative.dev/client/pkg/k8s"
|
||||
"knative.dev/client/pkg/util/test"
|
||||
)
|
||||
|
||||
|
@ -41,7 +42,7 @@ func TestGetNamespaceSample(t *testing.T) {
|
|||
testCmd := testCommandGenerator(true)
|
||||
expectedNamespace := "test1"
|
||||
testCmd.SetArgs([]string{"--namespace", expectedNamespace})
|
||||
testCmd.Execute()
|
||||
assert.NilError(t, testCmd.Execute())
|
||||
kp := &KnParams{fixedCurrentNamespace: FakeNamespace}
|
||||
actualNamespace, err := kp.GetNamespace(testCmd)
|
||||
if err != nil {
|
||||
|
@ -63,7 +64,7 @@ func TestGetNamespaceSample(t *testing.T) {
|
|||
func TestGetNamespaceDefault(t *testing.T) {
|
||||
testCmd := testCommandGenerator(true)
|
||||
expectedNamespace := "current"
|
||||
testCmd.Execute()
|
||||
assert.NilError(t, testCmd.Execute())
|
||||
kp := &KnParams{fixedCurrentNamespace: FakeNamespace}
|
||||
actualNamespace, err := kp.GetNamespace(testCmd)
|
||||
if err != nil {
|
||||
|
@ -84,7 +85,7 @@ func TestGetNamespaceAllNamespacesSet(t *testing.T) {
|
|||
// Test both variants of the "all namespaces" flag
|
||||
for _, arg := range []string{"--all-namespaces", "-A"} {
|
||||
testCmd.SetArgs([]string{"--namespace", sampleNamespace, arg})
|
||||
testCmd.Execute()
|
||||
assert.NilError(t, testCmd.Execute())
|
||||
kp := &KnParams{fixedCurrentNamespace: FakeNamespace}
|
||||
actualNamespace, err := kp.GetNamespace(testCmd)
|
||||
if err != nil {
|
||||
|
@ -106,7 +107,7 @@ func TestGetNamespaceDefaultAllNamespacesUnset(t *testing.T) {
|
|||
// Test both variants of the "all namespaces" flag
|
||||
for _, arg := range []string{"--all-namespaces", "-A"} {
|
||||
testCmd.SetArgs([]string{arg})
|
||||
testCmd.Execute()
|
||||
assert.NilError(t, testCmd.Execute())
|
||||
kp := &KnParams{fixedCurrentNamespace: FakeNamespace}
|
||||
actualNamespace, err := kp.GetNamespace(testCmd)
|
||||
if err != nil {
|
||||
|
@ -124,7 +125,7 @@ func TestGetNamespaceAllNamespacesNotDefined(t *testing.T) {
|
|||
testCmd := testCommandGenerator(false)
|
||||
expectedNamespace := "test1"
|
||||
testCmd.SetArgs([]string{"--namespace", expectedNamespace})
|
||||
testCmd.Execute()
|
||||
assert.NilError(t, testCmd.Execute())
|
||||
kp := &KnParams{fixedCurrentNamespace: FakeNamespace}
|
||||
actualNamespace, err := kp.GetNamespace(testCmd)
|
||||
if err != nil {
|
||||
|
@ -143,9 +144,9 @@ func TestGetNamespaceFallback(t *testing.T) {
|
|||
err := os.WriteFile(tempFile, []byte(BASIC_KUBECONFIG), test.FileModeReadWrite)
|
||||
assert.NilError(t, err)
|
||||
|
||||
kp := &KnParams{KubeCfgPath: tempFile}
|
||||
kp := &KnParams{Params: k8s.Params{KubeCfgPath: tempFile}}
|
||||
testCmd := testCommandGenerator(true)
|
||||
testCmd.Execute()
|
||||
assert.NilError(t, testCmd.Execute())
|
||||
actual, err := kp.GetNamespace(testCmd)
|
||||
assert.NilError(t, err)
|
||||
if isInCluster() {
|
||||
|
@ -161,9 +162,9 @@ func TestGetNamespaceFallback(t *testing.T) {
|
|||
err := os.WriteFile(tempFile, []byte(""), test.FileModeReadWrite)
|
||||
assert.NilError(t, err)
|
||||
|
||||
kp := &KnParams{KubeCfgPath: tempFile}
|
||||
kp := &KnParams{Params: k8s.Params{KubeCfgPath: tempFile}}
|
||||
testCmd := testCommandGenerator(true)
|
||||
testCmd.Execute()
|
||||
assert.NilError(t, testCmd.Execute())
|
||||
actual, err := kp.GetNamespace(testCmd)
|
||||
assert.NilError(t, err)
|
||||
if isInCluster() {
|
||||
|
@ -175,11 +176,11 @@ func TestGetNamespaceFallback(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("MissingConfig", func(t *testing.T) {
|
||||
kp := &KnParams{KubeCfgPath: filepath.Join(tempDir, "missing")}
|
||||
kp := &KnParams{Params: k8s.Params{KubeCfgPath: filepath.Join(tempDir, "missing")}}
|
||||
testCmd := testCommandGenerator(true)
|
||||
testCmd.Execute()
|
||||
assert.NilError(t, testCmd.Execute())
|
||||
actual, err := kp.GetNamespace(testCmd)
|
||||
assert.ErrorContains(t, err, "can not be found")
|
||||
assert.ErrorIs(t, err, k8s.ErrCantFindConfigFile)
|
||||
assert.Equal(t, actual, "")
|
||||
})
|
||||
}
|
||||
|
@ -193,7 +194,7 @@ func TestCurrentNamespace(t *testing.T) {
|
|||
err := os.WriteFile(tempFile, []byte(""), test.FileModeReadWrite)
|
||||
assert.NilError(t, err)
|
||||
|
||||
kp := &KnParams{KubeCfgPath: tempFile}
|
||||
kp := &KnParams{Params: k8s.Params{KubeCfgPath: tempFile}}
|
||||
actual, err := kp.CurrentNamespace()
|
||||
if isInCluster() {
|
||||
// In-cluster config overrides the mocked one in OpenShift CI
|
||||
|
@ -209,10 +210,10 @@ func TestCurrentNamespace(t *testing.T) {
|
|||
|
||||
t.Run("MissingConfig", func(t *testing.T) {
|
||||
// Missing kubeconfig
|
||||
kp := &KnParams{KubeCfgPath: filepath.Join(tempDir, "missing")}
|
||||
kp := &KnParams{Params: k8s.Params{KubeCfgPath: filepath.Join(tempDir, "missing")}}
|
||||
actual, err := kp.CurrentNamespace()
|
||||
assert.Assert(t, err != nil)
|
||||
assert.ErrorContains(t, err, "can not be found")
|
||||
assert.ErrorIs(t, err, k8s.ErrCantFindConfigFile)
|
||||
assert.Assert(t, actual == "")
|
||||
})
|
||||
|
||||
|
@ -229,7 +230,7 @@ func TestCurrentNamespace(t *testing.T) {
|
|||
tempFile := filepath.Join(tempDir, "mock")
|
||||
err := os.WriteFile(tempFile, []byte(BASIC_KUBECONFIG), test.FileModeReadWrite)
|
||||
assert.NilError(t, err)
|
||||
kp := &KnParams{KubeCfgPath: tempFile}
|
||||
kp := &KnParams{Params: k8s.Params{KubeCfgPath: tempFile}}
|
||||
actual, err := kp.CurrentNamespace()
|
||||
assert.NilError(t, err)
|
||||
if isInCluster() {
|
||||
|
|
|
@ -69,7 +69,7 @@ func TestSubscriptionList(t *testing.T) {
|
|||
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[1], "s0", "InMemoryChannel:imc0", "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"))
|
||||
})
|
||||
|
|
|
@ -15,12 +15,11 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"knative.dev/client/pkg/k8s"
|
||||
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
|
@ -48,14 +47,8 @@ import (
|
|||
|
||||
// KnParams for creating commands. Useful for inserting mocks for testing.
|
||||
type KnParams struct {
|
||||
k8s.Params
|
||||
Output io.Writer
|
||||
KubeCfgPath string
|
||||
KubeContext string
|
||||
KubeCluster string
|
||||
KubeAsUser string
|
||||
KubeAsUID string
|
||||
KubeAsGroup []string
|
||||
ClientConfig clientcmd.ClientConfig
|
||||
NewKubeClient func() (kubernetes.Interface, error)
|
||||
NewServingClient func(namespace string) (clientservingv1.KnServingClient, error)
|
||||
NewServingV1beta1Client func(namespace string) (clientservingv1beta1.KnServingClient, error)
|
||||
|
@ -72,8 +65,12 @@ type KnParams struct {
|
|||
|
||||
// Set this if you want to nail down the namespace
|
||||
fixedCurrentNamespace string
|
||||
|
||||
// Memorizes the loaded config
|
||||
clientcmd.ClientConfig
|
||||
}
|
||||
|
||||
// Initialize will initialize the default factories for the clients.
|
||||
func (params *KnParams) Initialize() {
|
||||
if params.NewKubeClient == nil {
|
||||
params.NewKubeClient = params.newKubeClient
|
||||
|
@ -246,44 +243,3 @@ func (params *KnParams) RestConfig() (*rest.Config, error) {
|
|||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// GetClientConfig gets ClientConfig from KubeCfgPath
|
||||
func (params *KnParams) GetClientConfig() (clientcmd.ClientConfig, error) {
|
||||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
configOverrides := &clientcmd.ConfigOverrides{}
|
||||
if params.KubeContext != "" {
|
||||
configOverrides.CurrentContext = params.KubeContext
|
||||
}
|
||||
if params.KubeCluster != "" {
|
||||
configOverrides.Context.Cluster = params.KubeCluster
|
||||
}
|
||||
if params.KubeAsUser != "" {
|
||||
configOverrides.AuthInfo.Impersonate = params.KubeAsUser
|
||||
}
|
||||
if params.KubeAsUID != "" {
|
||||
configOverrides.AuthInfo.ImpersonateUID = params.KubeAsUID
|
||||
}
|
||||
if len(params.KubeAsGroup) > 0 {
|
||||
configOverrides.AuthInfo.ImpersonateGroups = params.KubeAsGroup
|
||||
}
|
||||
if len(params.KubeCfgPath) == 0 {
|
||||
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides), nil
|
||||
}
|
||||
|
||||
_, err := os.Stat(params.KubeCfgPath)
|
||||
if err == nil {
|
||||
loadingRules.ExplicitPath = params.KubeCfgPath
|
||||
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides), nil
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paths := filepath.SplitList(params.KubeCfgPath)
|
||||
if len(paths) > 1 {
|
||||
return nil, fmt.Errorf("can not find config file. '%s' looks like a path. "+
|
||||
"Please use the env var KUBECONFIG if you want to check for multiple configuration files", params.KubeCfgPath)
|
||||
}
|
||||
return nil, fmt.Errorf("config file '%s' can not be found", params.KubeCfgPath)
|
||||
}
|
||||
|
|
|
@ -15,18 +15,14 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"knative.dev/client/pkg/util/test"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"knative.dev/client/pkg/util"
|
||||
"knative.dev/client/pkg/k8s"
|
||||
)
|
||||
|
||||
type configTestCase struct {
|
||||
|
@ -106,149 +102,7 @@ func TestPrepareConfig(t *testing.T) {
|
|||
kpEmptyConfig = &KnParams{}
|
||||
kpEmptyConfig.KubeCfgPath = filepath.Join("non", "existing", "file")
|
||||
_, err = kpEmptyConfig.RestConfig()
|
||||
assert.ErrorContains(t, err, "can not be found")
|
||||
}
|
||||
|
||||
type typeTestCase struct {
|
||||
kubeCfgPath string
|
||||
kubeContext string
|
||||
kubeAsUser string
|
||||
kubeAsUID string
|
||||
kubeAsGroup []string
|
||||
kubeCluster string
|
||||
explicitPath string
|
||||
expectedError string
|
||||
}
|
||||
|
||||
func TestGetClientConfig(t *testing.T) {
|
||||
multiConfigs := fmt.Sprintf("%s%s%s", "/testing/assets/kube-config-01.yml", string(os.PathListSeparator), "/testing/assets/kube-config-02.yml")
|
||||
|
||||
tempDir := t.TempDir()
|
||||
tempFile := filepath.Join(tempDir, "mock")
|
||||
err := os.WriteFile(tempFile, []byte(BASIC_KUBECONFIG), test.FileModeReadWrite)
|
||||
assert.NilError(t, err)
|
||||
|
||||
for _, tc := range []typeTestCase{
|
||||
{
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
[]string{},
|
||||
"",
|
||||
clientcmd.NewDefaultClientConfigLoadingRules().ExplicitPath,
|
||||
"",
|
||||
},
|
||||
{
|
||||
tempFile,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
[]string{},
|
||||
"",
|
||||
tempFile,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"/testing/assets/kube-config-01.yml",
|
||||
"foo",
|
||||
"",
|
||||
"",
|
||||
[]string{},
|
||||
"bar",
|
||||
"",
|
||||
fmt.Sprintf("config file '%s' can not be found", "/testing/assets/kube-config-01.yml"),
|
||||
},
|
||||
{
|
||||
multiConfigs,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
[]string{},
|
||||
"",
|
||||
"",
|
||||
fmt.Sprintf("can not find config file. '%s' looks like a path. Please use the env var KUBECONFIG if you want to check for multiple configuration files", multiConfigs),
|
||||
},
|
||||
{
|
||||
tempFile,
|
||||
"",
|
||||
"admin",
|
||||
"",
|
||||
[]string{},
|
||||
"",
|
||||
tempFile,
|
||||
"",
|
||||
},
|
||||
{
|
||||
tempFile,
|
||||
"",
|
||||
"admin",
|
||||
"",
|
||||
[]string{"system:authenticated", "system:masters"},
|
||||
"",
|
||||
tempFile,
|
||||
"",
|
||||
},
|
||||
{
|
||||
tempFile,
|
||||
"",
|
||||
"admin",
|
||||
"abc123",
|
||||
[]string{},
|
||||
"",
|
||||
tempFile,
|
||||
"",
|
||||
},
|
||||
} {
|
||||
p := &KnParams{
|
||||
KubeCfgPath: tc.kubeCfgPath,
|
||||
KubeContext: tc.kubeContext,
|
||||
KubeAsUser: tc.kubeAsUser,
|
||||
KubeAsUID: tc.kubeAsUID,
|
||||
KubeAsGroup: tc.kubeAsGroup,
|
||||
KubeCluster: tc.kubeCluster,
|
||||
}
|
||||
|
||||
clientConfig, err := p.GetClientConfig()
|
||||
if tc.expectedError != "" {
|
||||
assert.Assert(t, util.ContainsAll(err.Error(), tc.expectedError))
|
||||
} else {
|
||||
assert.Assert(t, err == nil, err)
|
||||
}
|
||||
|
||||
if clientConfig != nil {
|
||||
configAccess := clientConfig.ConfigAccess()
|
||||
assert.Assert(t, configAccess.GetExplicitFile() == tc.explicitPath)
|
||||
|
||||
if tc.kubeContext != "" {
|
||||
config, err := clientConfig.RawConfig()
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, config.CurrentContext == tc.kubeContext)
|
||||
assert.Assert(t, config.Contexts[tc.kubeContext].Cluster == tc.kubeCluster)
|
||||
}
|
||||
|
||||
if tc.kubeAsUser != "" {
|
||||
config, err := clientConfig.ClientConfig()
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, config.Impersonate.UserName == tc.kubeAsUser)
|
||||
}
|
||||
|
||||
if tc.kubeAsUID != "" {
|
||||
config, err := clientConfig.ClientConfig()
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, config.Impersonate.UID == tc.kubeAsUID)
|
||||
}
|
||||
|
||||
if len(tc.kubeAsGroup) > 0 {
|
||||
config, err := clientConfig.ClientConfig()
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, len(config.Impersonate.Groups) == len(tc.kubeAsGroup))
|
||||
for i := range tc.kubeAsGroup {
|
||||
assert.Assert(t, config.Impersonate.Groups[i] == tc.kubeAsGroup[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.ErrorIs(t, err, k8s.ErrCantFindConfigFile)
|
||||
}
|
||||
|
||||
func TestNewSourcesClient(t *testing.T) {
|
||||
|
|
|
@ -16,7 +16,9 @@ package fake
|
|||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
|
@ -32,13 +34,17 @@ import (
|
|||
|
||||
// CreateFakeKnDynamicClient gives you a dynamic client for testing containing the given objects.
|
||||
func CreateFakeKnDynamicClient(testNamespace string, objects ...runtime.Object) dynamic.KnDynamicClient {
|
||||
if !testing.Testing() {
|
||||
panic("For test usage only!")
|
||||
}
|
||||
scheme := runtime.NewScheme()
|
||||
servingv1.AddToScheme(scheme)
|
||||
eventingv1.AddToScheme(scheme)
|
||||
messagingv1.AddToScheme(scheme)
|
||||
sourcesv1.AddToScheme(scheme)
|
||||
sourcesv1beta2.AddToScheme(scheme)
|
||||
apiextensionsv1.AddToScheme(scheme)
|
||||
_ = corev1.AddToScheme(scheme)
|
||||
_ = servingv1.AddToScheme(scheme)
|
||||
_ = eventingv1.AddToScheme(scheme)
|
||||
_ = messagingv1.AddToScheme(scheme)
|
||||
_ = sourcesv1.AddToScheme(scheme)
|
||||
_ = sourcesv1beta2.AddToScheme(scheme)
|
||||
_ = apiextensionsv1.AddToScheme(scheme)
|
||||
_, dynamicClient := dynamicclientfake.With(context.TODO(), scheme, objects...)
|
||||
return dynamic.NewKnDynamicClient(dynamicClient, testNamespace)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Copyright 2024 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package sink
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultFlagName is a default command-line flag name.
|
||||
DefaultFlagName = "sink"
|
||||
// DefaultFlagShorthand is a default command-line flag shorthand.
|
||||
DefaultFlagShorthand = "s"
|
||||
)
|
||||
|
||||
// Usage returns a usage text which can be used to define sink-like flag.
|
||||
func Usage(fname string) string {
|
||||
flag := "--" + fname
|
||||
return "Addressable sink for events. " +
|
||||
"You can specify a broker, channel, Knative service, Kubernetes service or URI. " +
|
||||
"Examples: '" + flag + " broker:nest' for a broker 'nest', " +
|
||||
"'" + flag + " channel:pipe' for a channel 'pipe', " +
|
||||
"'" + flag + " ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', " +
|
||||
"'" + flag + " https://event.receiver.uri' for an HTTP URI, " +
|
||||
"'" + flag + " ksvc:receiver' or simply '" + flag + " receiver' for a Knative service 'receiver' in the current namespace, " +
|
||||
"'" + flag + " svc:receiver:mynamespace' for a Kubernetes service 'receiver' in the 'mynamespace' namespace, " +
|
||||
"'" + flag + " special.eventing.dev/v1alpha1/channels:pipe' for GroupVersionResource of v1alpha1 'pipe'. " +
|
||||
"If a prefix is not provided, it is considered as a Knative service in the current namespace."
|
||||
}
|
||||
|
||||
// parseSink takes the string given by the user into the prefix, name and namespace of
|
||||
// the object. If the user put a URI instead, the prefix is empty and the name
|
||||
// is the whole URI.
|
||||
func parseSink(sink string) (string, string, string) {
|
||||
parts := strings.SplitN(sink, ":", 3)
|
||||
switch {
|
||||
case len(parts) == 1:
|
||||
return knativeServiceShorthand, parts[0], ""
|
||||
case parts[0] == "http" || parts[0] == "https":
|
||||
return "", sink, ""
|
||||
case len(parts) == 3:
|
||||
return parts[0], parts[1], parts[2]
|
||||
default:
|
||||
return parts[0], parts[1], ""
|
||||
}
|
||||
}
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
Copyright 2024 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package sink
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"knative.dev/client/pkg/config"
|
||||
clientdynamic "knative.dev/client/pkg/dynamic"
|
||||
"knative.dev/pkg/apis"
|
||||
duckv1 "knative.dev/pkg/apis/duck/v1"
|
||||
)
|
||||
|
||||
// ErrSinkIsRequired is returned when no sink is given.
|
||||
var ErrSinkIsRequired = errors.New("sink is required")
|
||||
|
||||
// ErrSinkIsInvalid is returned when the sink has invalid format.
|
||||
var ErrSinkIsInvalid = errors.New("sink has invalid format")
|
||||
|
||||
// Type is a type of Reference.
|
||||
type Type int
|
||||
|
||||
const (
|
||||
// TypeURL is a URL version of the sink.
|
||||
TypeURL Type = iota
|
||||
// TypeReference is a Kuberentes version of the sink.
|
||||
TypeReference
|
||||
)
|
||||
|
||||
// Reference represents either a URL or Kubernetes resource.
|
||||
type Reference struct {
|
||||
*KubeReference
|
||||
*apis.URL
|
||||
}
|
||||
|
||||
// KubeReference represents a Kubernetes resource as given by command-line args.
|
||||
type KubeReference struct {
|
||||
GVR schema.GroupVersionResource
|
||||
Name string
|
||||
Namespace string
|
||||
}
|
||||
|
||||
// DefaultMappings are used to easily map prefixes for sinks to their
|
||||
// GroupVersionResources.
|
||||
var DefaultMappings = withAliasses(map[string]schema.GroupVersionResource{
|
||||
"kservice": {
|
||||
Resource: "services",
|
||||
Group: "serving.knative.dev",
|
||||
Version: "v1",
|
||||
},
|
||||
"broker": {
|
||||
Resource: "brokers",
|
||||
Group: "eventing.knative.dev",
|
||||
Version: "v1",
|
||||
},
|
||||
"channel": {
|
||||
Resource: "channels",
|
||||
Group: "messaging.knative.dev",
|
||||
Version: "v1",
|
||||
},
|
||||
"service": { // K8s' service
|
||||
Resource: "services",
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
},
|
||||
}, defaultMappingAliasses)
|
||||
|
||||
var defaultMappingAliasses = map[string]string{
|
||||
knativeServiceShorthand: "kservice",
|
||||
"svc": "service",
|
||||
}
|
||||
|
||||
const knativeServiceShorthand = "ksvc"
|
||||
|
||||
// Type returns the type of the reference.
|
||||
func (r *Reference) Type() Type {
|
||||
if r.KubeReference != nil {
|
||||
return TypeReference
|
||||
}
|
||||
if r.URL != nil {
|
||||
return TypeURL
|
||||
}
|
||||
return Type(-1) // unknown type, unexpected
|
||||
}
|
||||
|
||||
// Resolve returns the Destination referred to by the sink. It validates that
|
||||
// any object the user is referring to exists.
|
||||
func (r *Reference) Resolve(ctx context.Context, knclient clientdynamic.KnDynamicClient) (*duckv1.Destination, error) {
|
||||
if r.Type() == TypeURL {
|
||||
return &duckv1.Destination{URI: r.URL}, nil
|
||||
}
|
||||
if r.Type() != TypeReference {
|
||||
return nil, fmt.Errorf("%w: unexpected type %q",
|
||||
ErrSinkIsInvalid, r.Type())
|
||||
}
|
||||
client := knclient.RawClient()
|
||||
obj, err := client.Resource(r.GVR).
|
||||
Namespace(r.Namespace).
|
||||
Get(ctx, r.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", ErrSinkIsInvalid, err)
|
||||
}
|
||||
|
||||
destination := &duckv1.Destination{
|
||||
Ref: &duckv1.KReference{
|
||||
Kind: obj.GetKind(),
|
||||
APIVersion: obj.GetAPIVersion(),
|
||||
Name: obj.GetName(),
|
||||
Namespace: r.Namespace,
|
||||
},
|
||||
}
|
||||
return destination, nil
|
||||
}
|
||||
|
||||
// String creates a text representation of the reference
|
||||
// Deprecated: use AsText instead
|
||||
func (r *Reference) String() string {
|
||||
if r == nil {
|
||||
return ""
|
||||
}
|
||||
// unexpected random-like value
|
||||
ns := "vaizaeso3sheem5ebie5eeh9Aew5eekei3thie4ezooy9geef6iesh9auPhai7na"
|
||||
if r.KubeReference != nil {
|
||||
ns = r.Namespace
|
||||
}
|
||||
return r.AsText(ns)
|
||||
}
|
||||
|
||||
// AsText creates a text representation of the resource, and should
|
||||
// be used by giving a current namespace.
|
||||
func (r *Reference) AsText(currentNamespace string) string {
|
||||
if r.Type() == TypeURL {
|
||||
return r.URL.String()
|
||||
}
|
||||
if r.Type() == TypeReference {
|
||||
repr := r.GvrAsText() + ":" + r.Name
|
||||
if currentNamespace != r.Namespace {
|
||||
repr = fmt.Sprintf("%s:%s", repr, r.Namespace)
|
||||
}
|
||||
return repr
|
||||
}
|
||||
return fmt.Errorf("%w: unexpected type %q",
|
||||
ErrSinkIsInvalid, r.Type()).Error()
|
||||
}
|
||||
|
||||
// GvrAsText returns the
|
||||
func (r *Reference) GvrAsText() string {
|
||||
if r == nil || r.KubeReference == nil {
|
||||
return fmt.Errorf("%w: unexpected type %#v",
|
||||
ErrSinkIsInvalid, r).Error()
|
||||
}
|
||||
for alias, as := range defaultMappingAliasses {
|
||||
if gvr, ok := DefaultMappings[as]; ok && gvr == r.GVR {
|
||||
return alias
|
||||
}
|
||||
}
|
||||
for alias, gvr := range DefaultMappings {
|
||||
if r.GVR == gvr {
|
||||
return alias
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s.%s/%s",
|
||||
r.GVR.Resource, r.GVR.Group, r.GVR.Version)
|
||||
|
||||
}
|
||||
|
||||
// Parse returns the sink reference of given sink representation, which may
|
||||
// refer to URL or to the Kubernetes resource. The namespace given should be
|
||||
// the current namespace within the context.
|
||||
func Parse(sinkRepr, namespace string, mappings map[string]schema.GroupVersionResource) (*Reference, error) {
|
||||
if sinkRepr == "" {
|
||||
return nil, ErrSinkIsRequired
|
||||
}
|
||||
prefix, name, ns := parseSink(sinkRepr)
|
||||
if prefix == "" {
|
||||
// URI target
|
||||
uri, err := apis.ParseURL(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", ErrSinkIsInvalid, err)
|
||||
}
|
||||
return &Reference{URL: uri}, nil
|
||||
}
|
||||
gvr, ok := mappings[prefix]
|
||||
if !ok {
|
||||
idx := strings.LastIndex(prefix, "/")
|
||||
var groupVersion string
|
||||
var kind string
|
||||
if idx != -1 && idx < len(prefix)-1 {
|
||||
groupVersion, kind = prefix[:idx], prefix[idx+1:]
|
||||
} else {
|
||||
kind = prefix
|
||||
}
|
||||
parsedVersion, err := schema.ParseGroupVersion(groupVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// For the RAWclient the resource name must be in lower case plural form.
|
||||
// This is the best effort to sanitize the inputs, but the safest way is to provide
|
||||
// the appropriate form in user's input.
|
||||
if !strings.HasSuffix(kind, "s") {
|
||||
kind = kind + "s"
|
||||
}
|
||||
kind = strings.ToLower(kind)
|
||||
gvr = parsedVersion.WithResource(kind)
|
||||
}
|
||||
if ns != "" {
|
||||
namespace = ns
|
||||
}
|
||||
return &Reference{KubeReference: &KubeReference{
|
||||
GVR: gvr,
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
// ComputeWithDefaultMappings will compute mapping by including default mappings
|
||||
// and mappings provided by the end-user.
|
||||
func ComputeWithDefaultMappings(mappings map[string]schema.GroupVersionResource) map[string]schema.GroupVersionResource {
|
||||
sm := make(map[string]schema.GroupVersionResource,
|
||||
len(mappings)+len(DefaultMappings))
|
||||
for k, v := range DefaultMappings {
|
||||
sm[k] = v
|
||||
}
|
||||
for k, v := range mappings {
|
||||
sm[k] = v
|
||||
}
|
||||
for _, p := range config.GlobalConfig.SinkMappings() {
|
||||
// user configuration might override the default configuration
|
||||
sm[p.Prefix] = schema.GroupVersionResource{
|
||||
Resource: p.Resource,
|
||||
Group: p.Group,
|
||||
Version: p.Version,
|
||||
}
|
||||
}
|
||||
return sm
|
||||
}
|
||||
|
||||
// GuessFromDestination converts the duckv1.Destination to the Reference by guessing
|
||||
// the type by convention.
|
||||
// Will return nil, if given empty destination.
|
||||
func GuessFromDestination(dest duckv1.Destination) *Reference {
|
||||
if dest.URI != nil {
|
||||
return &Reference{URL: dest.URI}
|
||||
}
|
||||
if dest.Ref == nil {
|
||||
return nil
|
||||
}
|
||||
ref := &corev1.ObjectReference{
|
||||
Kind: dest.Ref.Kind,
|
||||
Namespace: dest.Ref.Namespace,
|
||||
Name: dest.Ref.Name,
|
||||
APIVersion: dest.Ref.APIVersion,
|
||||
}
|
||||
gvk := ref.GroupVersionKind()
|
||||
gvr, _ := meta.UnsafeGuessKindToResource(gvk)
|
||||
return &Reference{KubeReference: &KubeReference{
|
||||
GVR: gvr,
|
||||
Name: ref.Name,
|
||||
Namespace: ref.Namespace,
|
||||
}}
|
||||
}
|
||||
|
||||
func withAliasses(
|
||||
mappings map[string]schema.GroupVersionResource,
|
||||
aliases map[string]string,
|
||||
) map[string]schema.GroupVersionResource {
|
||||
result := make(map[string]schema.GroupVersionResource, len(aliases)+len(mappings))
|
||||
for k, v := range mappings {
|
||||
result[k] = v
|
||||
}
|
||||
for as, alias := range aliases {
|
||||
if val, ok := result[alias]; ok {
|
||||
result[as] = val.GroupResource().WithVersion(val.Version)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
Copyright 2024 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"emperror.dev/errors"
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
// ErrCantFindConfigFile is returned when given config file can't be found.
|
||||
var ErrCantFindConfigFile = errors.New("can not find config file")
|
||||
|
||||
// Params contain Kubernetes specific params, that CLI should comply to.
|
||||
type Params struct {
|
||||
KubeCfgPath string
|
||||
KubeContext string
|
||||
KubeCluster string
|
||||
KubeAsUser string
|
||||
KubeAsUID string
|
||||
KubeAsGroup []string
|
||||
}
|
||||
|
||||
// SetFlags is used set flags to the given flagset.
|
||||
func (kp *Params) SetFlags(flags *pflag.FlagSet) {
|
||||
flags.StringVar(&kp.KubeCfgPath, "kubeconfig", "",
|
||||
"kubectl configuration file (default: ~/.kube/config)")
|
||||
flags.StringVar(&kp.KubeContext, "context", "",
|
||||
"name of the kubeconfig context to use")
|
||||
flags.StringVar(&kp.KubeCluster, "cluster", "",
|
||||
"name of the kubeconfig cluster to use")
|
||||
flags.StringVar(&kp.KubeAsUser, "as", "",
|
||||
"username to impersonate for the operation")
|
||||
flags.StringVar(&kp.KubeAsUID, "as-uid", "",
|
||||
"uid to impersonate for the operation")
|
||||
flags.StringArrayVar(&kp.KubeAsGroup, "as-group",
|
||||
[]string{}, "group to impersonate for the operation, this flag can "+
|
||||
"be repeated to specify multiple groups")
|
||||
}
|
||||
|
||||
// GetClientConfig gets ClientConfig from Kube' configuration params.
|
||||
func (kp *Params) GetClientConfig() (clientcmd.ClientConfig, error) {
|
||||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
configOverrides := &clientcmd.ConfigOverrides{}
|
||||
if kp.KubeContext != "" {
|
||||
configOverrides.CurrentContext = kp.KubeContext
|
||||
}
|
||||
if kp.KubeCluster != "" {
|
||||
configOverrides.Context.Cluster = kp.KubeCluster
|
||||
}
|
||||
if kp.KubeAsUser != "" {
|
||||
configOverrides.AuthInfo.Impersonate = kp.KubeAsUser
|
||||
}
|
||||
if kp.KubeAsUID != "" {
|
||||
configOverrides.AuthInfo.ImpersonateUID = kp.KubeAsUID
|
||||
}
|
||||
if len(kp.KubeAsGroup) > 0 {
|
||||
configOverrides.AuthInfo.ImpersonateGroups = kp.KubeAsGroup
|
||||
}
|
||||
if len(kp.KubeCfgPath) == 0 {
|
||||
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides), nil
|
||||
}
|
||||
|
||||
_, err := os.Stat(kp.KubeCfgPath)
|
||||
if err == nil {
|
||||
loadingRules.ExplicitPath = kp.KubeCfgPath
|
||||
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides), nil
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paths := filepath.SplitList(kp.KubeCfgPath)
|
||||
if len(paths) > 1 {
|
||||
return nil, fmt.Errorf("%w. '%s' looks "+
|
||||
"like a path. Please use the env var KUBECONFIG if you want to "+
|
||||
"check for multiple configuration files", ErrCantFindConfigFile, kp.KubeCfgPath)
|
||||
}
|
||||
return nil, fmt.Errorf("%w: '%s'", ErrCantFindConfigFile, kp.KubeCfgPath)
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
Copyright 2024 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package k8s_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"knative.dev/client/pkg/k8s"
|
||||
"knative.dev/client/pkg/util"
|
||||
"knative.dev/client/pkg/util/test"
|
||||
)
|
||||
|
||||
func TestGetClientConfig(t *testing.T) {
|
||||
multiConfigs := fmt.Sprintf("%s%s%s",
|
||||
"/testing/assets/kube-config-01.yml",
|
||||
string(os.PathListSeparator),
|
||||
"/testing/assets/kube-config-02.yml")
|
||||
|
||||
tempDir := t.TempDir()
|
||||
tempFile := filepath.Join(tempDir, "mock")
|
||||
assert.NilError(t, os.WriteFile(tempFile, []byte(basicKubeconfig), test.FileModeReadWrite))
|
||||
|
||||
for _, tc := range []typeTestCase{{
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
[]string{},
|
||||
"",
|
||||
clientcmd.NewDefaultClientConfigLoadingRules().ExplicitPath,
|
||||
"",
|
||||
}, {
|
||||
tempFile,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
[]string{},
|
||||
"",
|
||||
tempFile,
|
||||
"",
|
||||
}, {
|
||||
"/testing/assets/kube-config-01.yml",
|
||||
"foo",
|
||||
"",
|
||||
"",
|
||||
[]string{},
|
||||
"bar",
|
||||
"",
|
||||
fmt.Sprintf("can not find config file: '%s'", "/testing/assets/kube-config-01.yml"),
|
||||
}, {
|
||||
multiConfigs,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
[]string{},
|
||||
"",
|
||||
"",
|
||||
fmt.Sprintf("can not find config file. '%s' looks like a path. Please use the env var KUBECONFIG if you want to check for multiple configuration files", multiConfigs),
|
||||
}, {
|
||||
tempFile,
|
||||
"",
|
||||
"admin",
|
||||
"",
|
||||
[]string{},
|
||||
"",
|
||||
tempFile,
|
||||
"",
|
||||
}, {
|
||||
tempFile,
|
||||
"",
|
||||
"admin",
|
||||
"",
|
||||
[]string{"system:authenticated", "system:masters"},
|
||||
"",
|
||||
tempFile,
|
||||
"",
|
||||
}, {
|
||||
tempFile,
|
||||
"",
|
||||
"admin",
|
||||
"abc123",
|
||||
[]string{},
|
||||
"",
|
||||
tempFile,
|
||||
"",
|
||||
}} {
|
||||
p := &k8s.Params{
|
||||
KubeCfgPath: tc.kubeCfgPath,
|
||||
KubeContext: tc.kubeContext,
|
||||
KubeAsUser: tc.kubeAsUser,
|
||||
KubeAsUID: tc.kubeAsUID,
|
||||
KubeAsGroup: tc.kubeAsGroup,
|
||||
KubeCluster: tc.kubeCluster,
|
||||
}
|
||||
var clientConfig clientcmd.ClientConfig
|
||||
{
|
||||
cc, err := p.GetClientConfig()
|
||||
if tc.expectedError != "" {
|
||||
assert.Assert(t, util.ContainsAll(err.Error(), tc.expectedError))
|
||||
} else {
|
||||
assert.Assert(t, err == nil, err)
|
||||
}
|
||||
clientConfig = cc
|
||||
}
|
||||
|
||||
if clientConfig != nil {
|
||||
configAccess := clientConfig.ConfigAccess()
|
||||
assert.Assert(t, configAccess.GetExplicitFile() == tc.explicitPath)
|
||||
|
||||
if tc.kubeContext != "" {
|
||||
config, err := clientConfig.RawConfig()
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, config.CurrentContext == tc.kubeContext)
|
||||
assert.Assert(t, config.Contexts[tc.kubeContext].Cluster == tc.kubeCluster)
|
||||
}
|
||||
|
||||
if tc.kubeAsUser != "" {
|
||||
config, err := clientConfig.ClientConfig()
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, config.Impersonate.UserName == tc.kubeAsUser)
|
||||
}
|
||||
|
||||
if tc.kubeAsUID != "" {
|
||||
config, err := clientConfig.ClientConfig()
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, config.Impersonate.UID == tc.kubeAsUID)
|
||||
}
|
||||
|
||||
if len(tc.kubeAsGroup) > 0 {
|
||||
config, err := clientConfig.ClientConfig()
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, len(config.Impersonate.Groups) == len(tc.kubeAsGroup))
|
||||
for i := range tc.kubeAsGroup {
|
||||
assert.Assert(t, config.Impersonate.Groups[i] == tc.kubeAsGroup[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var basicKubeconfig = `apiVersion: v1
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: a
|
||||
user:
|
||||
client-certificate-data: ""
|
||||
client-key-data: ""
|
||||
clusters:
|
||||
- name: a
|
||||
cluster:
|
||||
insecure-skip-tls-verify: true
|
||||
server: https://127.0.0.1:8080
|
||||
contexts:
|
||||
- name: a
|
||||
context:
|
||||
cluster: a
|
||||
user: a
|
||||
current-context: a
|
||||
`
|
||||
|
||||
type typeTestCase struct {
|
||||
kubeCfgPath string
|
||||
kubeContext string
|
||||
kubeAsUser string
|
||||
kubeAsUID string
|
||||
kubeAsGroup []string
|
||||
kubeCluster string
|
||||
explicitPath string
|
||||
expectedError string
|
||||
}
|
|
@ -18,7 +18,6 @@ package logging
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
|
@ -40,14 +39,14 @@ var ErrCallEnsureLoggerFirst = errors.New("call EnsureLogger() before LoggerFrom
|
|||
// context will have a logger attached to it. Given fields will be added to the
|
||||
// logger, either new or existing.
|
||||
func EnsureLogger(ctx context.Context, fields ...Fields) context.Context {
|
||||
z, err := loggerFrom(ctx)
|
||||
z, err := zapLoggerFrom(ctx)
|
||||
if errors.Is(err, ErrCallEnsureLoggerFirst) {
|
||||
ctx = EnsureLogFile(ctx)
|
||||
z = setupLogging(ctx)
|
||||
}
|
||||
l := &zapLogger{SugaredLogger: z}
|
||||
l := &ZapLogger{SugaredLogger: z}
|
||||
for _, f := range fields {
|
||||
l = l.WithFields(f).(*zapLogger)
|
||||
l = l.WithFields(f).(*ZapLogger)
|
||||
}
|
||||
return WithLogger(ctx, l)
|
||||
}
|
||||
|
@ -55,24 +54,28 @@ func EnsureLogger(ctx context.Context, fields ...Fields) context.Context {
|
|||
// LoggerFrom returns the logger from the context. If EnsureLogger() was not
|
||||
// called before, it will panic.
|
||||
func LoggerFrom(ctx context.Context) Logger {
|
||||
z, err := loggerFrom(ctx)
|
||||
if l, ok := ctx.Value(loggerKey).(Logger); ok {
|
||||
return l
|
||||
}
|
||||
z, err := zapLoggerFrom(ctx)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
return &zapLogger{z}
|
||||
return &ZapLogger{z}
|
||||
}
|
||||
|
||||
// WithLogger attaches the given logger to the context.
|
||||
func WithLogger(ctx context.Context, l Logger) context.Context {
|
||||
if z, ok := l.(*zapLogger); ok {
|
||||
if z, ok := l.(*ZapLogger); ok {
|
||||
return logging.WithLogger(ctx, z.SugaredLogger)
|
||||
}
|
||||
fatal("unsupported logger type: " + fmt.Sprintf("%#v", l))
|
||||
return nil
|
||||
return context.WithValue(ctx, loggerKey, l)
|
||||
}
|
||||
|
||||
func loggerFrom(ctx context.Context) (*zap.SugaredLogger, error) {
|
||||
var loggerKey = struct{}{}
|
||||
|
||||
func zapLoggerFrom(ctx context.Context) (*zap.SugaredLogger, error) {
|
||||
l := logging.FromContext(ctx)
|
||||
if l.Desugar().Name() == "fallback" {
|
||||
return nil, ErrCallEnsureLoggerFirst
|
||||
|
@ -126,7 +129,7 @@ func createDefaultLogger(ctx context.Context) *zap.Logger {
|
|||
ec.EncodeTime = ElapsedMillisTimeEncoder(time.Now())
|
||||
ec.ConsoleSeparator = " "
|
||||
|
||||
lvl := activeLogLevel(zapcore.WarnLevel)
|
||||
lvl := activeLogLevel(LogLevelFromContext(ctx))
|
||||
logger := zap.New(zapcore.NewCore(
|
||||
zapcore.NewConsoleEncoder(ec),
|
||||
zapcore.AddSync(errout),
|
||||
|
@ -161,6 +164,8 @@ func activeLogLevel(defaultLevel zapcore.Level) zapcore.Level {
|
|||
return defaultLevel
|
||||
}
|
||||
|
||||
// ElapsedMillisTimeEncoder is a time encoder using elapsed time since the
|
||||
// logger setup.
|
||||
func ElapsedMillisTimeEncoder(setupTime time.Time) zapcore.TimeEncoder {
|
||||
return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
||||
enc.AppendInt64(t.Sub(setupTime).Milliseconds())
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright 2024 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
pkgcontext "knative.dev/client/pkg/context"
|
||||
)
|
||||
|
||||
var logLevelKey = struct{}{}
|
||||
|
||||
// LogLevelFromContext returns the default log level for the logger.
|
||||
func LogLevelFromContext(ctx context.Context) zapcore.Level {
|
||||
if val, ok := ctx.Value(logLevelKey).(zapcore.Level); ok {
|
||||
return val
|
||||
} else {
|
||||
if pkgcontext.TestingTFromContext(ctx) != nil {
|
||||
return zapcore.DebugLevel
|
||||
}
|
||||
return zapcore.WarnLevel
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogLevel will set given log level as the default one in given context.
|
||||
func WithLogLevel(ctx context.Context, level zapcore.Level) context.Context {
|
||||
return context.WithValue(ctx, logLevelKey, level)
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2024 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logging_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gotest.tools/v3/assert"
|
||||
pkgcontext "knative.dev/client/pkg/context"
|
||||
"knative.dev/client/pkg/output/logging"
|
||||
)
|
||||
|
||||
func TestLogLevel(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
assert.Equal(t, zapcore.WarnLevel, logging.LogLevelFromContext(ctx))
|
||||
ctx = pkgcontext.WithTestingT(ctx, t)
|
||||
assert.Equal(t, zapcore.DebugLevel, logging.LogLevelFromContext(ctx))
|
||||
ctx = logging.WithLogLevel(ctx, zapcore.InfoLevel)
|
||||
assert.Equal(t, zapcore.InfoLevel, logging.LogLevelFromContext(ctx))
|
||||
}
|
|
@ -18,23 +18,24 @@ package logging
|
|||
|
||||
import "go.uber.org/zap"
|
||||
|
||||
type zapLogger struct {
|
||||
// ZapLogger is a Google' zap logger based logger.
|
||||
type ZapLogger struct {
|
||||
*zap.SugaredLogger
|
||||
}
|
||||
|
||||
func (z zapLogger) WithName(name string) Logger {
|
||||
return &zapLogger{
|
||||
func (z ZapLogger) WithName(name string) Logger {
|
||||
return &ZapLogger{
|
||||
SugaredLogger: z.SugaredLogger.Named(name),
|
||||
}
|
||||
}
|
||||
|
||||
func (z zapLogger) WithFields(fields Fields) Logger {
|
||||
func (z ZapLogger) WithFields(fields Fields) Logger {
|
||||
a := make([]interface{}, 0, len(fields)*2)
|
||||
for k, v := range fields {
|
||||
a = append(a, k, v)
|
||||
}
|
||||
|
||||
return &zapLogger{
|
||||
return &ZapLogger{
|
||||
SugaredLogger: z.SugaredLogger.With(a...),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,13 +84,9 @@ Find more information about Knative at: https://knative.dev`, rootName),
|
|||
// Bootstrap flags (rebinding to avoid errors when parsing the full commands)
|
||||
config.AddBootstrapFlags(rootCmd.PersistentFlags())
|
||||
|
||||
// Global flags
|
||||
rootCmd.PersistentFlags().StringVar(&p.KubeCfgPath, "kubeconfig", "", "kubectl configuration file (default: ~/.kube/config)")
|
||||
rootCmd.PersistentFlags().StringVar(&p.KubeContext, "context", "", "name of the kubeconfig context to use")
|
||||
rootCmd.PersistentFlags().StringVar(&p.KubeCluster, "cluster", "", "name of the kubeconfig cluster to use")
|
||||
rootCmd.PersistentFlags().StringVar(&p.KubeAsUser, "as", "", "username to impersonate for the operation")
|
||||
rootCmd.PersistentFlags().StringVar(&p.KubeAsUID, "as-uid", "", "uid to impersonate for the operation")
|
||||
rootCmd.PersistentFlags().StringArrayVar(&p.KubeAsGroup, "as-group", []string{}, "group to impersonate for the operation, this flag can be repeated to specify multiple groups")
|
||||
// Global Kube' flags
|
||||
p.Params.SetFlags(rootCmd.PersistentFlags())
|
||||
|
||||
flags.AddBothBoolFlags(rootCmd.PersistentFlags(), &p.LogHTTP, "log-http", "", false, "log http traffic")
|
||||
|
||||
// Grouped commands
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Copyright 2024 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"emperror.dev/errors"
|
||||
)
|
||||
|
||||
// CauseOf will return the error that caused the returned error. This can be
|
||||
// used when multierr package is used or fmt.Errorf("%w: %w", ErrFront, cause)
|
||||
// from standard library is used, and we know the front error.
|
||||
func CauseOf(err, rootErr error) error {
|
||||
if errors.Is(err, rootErr) {
|
||||
for _, cause := range errors.GetErrors(err) {
|
||||
if errors.Is(cause, rootErr) {
|
||||
continue
|
||||
}
|
||||
return cause
|
||||
}
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2024 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"knative.dev/client/pkg/util/errors"
|
||||
)
|
||||
|
||||
func TestCauseOf(t *testing.T) {
|
||||
errExample := errors.New("example error")
|
||||
want := syscall.EINVAL
|
||||
err := fmt.Errorf("%w: %w", errExample, want)
|
||||
|
||||
got := errors.CauseOf(err, errExample)
|
||||
if !errors.Is(got, want) {
|
||||
t.Errorf("got error %v, want %v", got, want)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright 2024 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package errors
|
||||
|
||||
import "emperror.dev/errors"
|
||||
|
||||
// Is reports whether any error in err's chain matches target.
|
||||
//
|
||||
// An error is considered to match a target if it is equal to that target or if
|
||||
// it implements a method Is(error) bool such that Is(target) returns true.
|
||||
func Is(err, target error) bool {
|
||||
return errors.Is(err, target)
|
||||
}
|
||||
|
||||
// New returns a new error annotated with stack trace at the point New is called.
|
||||
func New(msg string) error {
|
||||
return errors.New(msg)
|
||||
}
|
Loading…
Reference in New Issue