Skip to content

Commit

Permalink
feat(source): Add 'kn source list'
Browse files Browse the repository at this point in the history
 Fixes #480
 - Add 'kn source list' listing the available sources COs
 - Use dynamic client to
       - find out available source types
       - find the COs in given namespace for each source type
 - Add --type flag to filter source list based on source type
 - Add unit tests
 - Add e2e tests
 - Add CHANGELOG entry
  • Loading branch information
navidshaikh committed Feb 18, 2020
1 parent c2dcb6b commit 247fa41
Show file tree
Hide file tree
Showing 16 changed files with 738 additions and 214 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
|===
| | Description | PR

| 🎁
| Add `kn source list`
| https://github.com/knative/client/pull/666[#666]

| 🧽
| Support multiple revisions on `kn revision delete`
| https://github.com/knative/client/pull/657[#657]
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_source.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ kn source [flags]
* [kn source apiserver](kn_source_apiserver.md) - Kubernetes API Server Event Source command group
* [kn source binding](kn_source_binding.md) - Sink binding command group
* [kn source cronjob](kn_source_cronjob.md) - CronJob source command group
* [kn source list](kn_source_list.md) - List available sources
* [kn source list-types](kn_source_list-types.md) - List available source types

51 changes: 51 additions & 0 deletions docs/cmd/kn_source_list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## kn source list

List available sources

### Synopsis

List available sources

```
kn source list [flags]
```

### Examples

```
# List available eventing sources
kn source list
# List PingSource type sources
kn source list --type=PingSource
# List PingSource and ApiServerSource types sources
kn source list --type=PingSource --type=apiserversource
```

### Options

```
-A, --all-namespaces If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.
--allow-missing-template-keys If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. (default true)
-h, --help help for list
-n, --namespace string Specify the namespace to operate in.
--no-headers When using the default output format, don't print headers (default: print headers).
-o, --output string Output format. One of: json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file.
--template string Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].
-t, --type strings Filter list on given source type. This flag can be given multiple times.
```

### Options inherited from parent commands

```
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--log-http log http traffic
```

### SEE ALSO

* [kn source](kn_source.md) - Event source command group

1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ require (
contrib.go.opencensus.io/exporter/prometheus v0.1.0 // indirect
contrib.go.opencensus.io/exporter/stackdriver v0.13.0 // indirect
github.com/google/go-containerregistry v0.0.0-20200212224832-c629a66d7231 // indirect
github.com/magiconair/properties v1.8.0
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/openzipkin/zipkin-go v0.2.2 // indirect
Expand Down
100 changes: 100 additions & 0 deletions pkg/dynamic/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
package dynamic

import (
"fmt"
"strings"

"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
Expand All @@ -31,6 +35,16 @@ const (
sourcesLabelValue = "true"
)

// SourceListFilters defines flags used for kn source list to filter sources on types
type SourceListFilters struct {
filters []string
}

// Add attaches the SourceListFilters flags to given command
func (s *SourceListFilters) Add(cmd *cobra.Command) {
cmd.Flags().StringSliceVarP(&s.filters, "type", "t", nil, "Filter list on given source type. This flag can be given multiple times.")
}

// KnDynamicClient to client-go Dynamic client. All methods are relative to the
// namespace specified during construction
type KnDynamicClient interface {
Expand All @@ -43,6 +57,9 @@ type KnDynamicClient interface {
// ListSourceCRDs returns list of eventing sources CRDs
ListSourcesTypes() (*unstructured.UnstructuredList, error)

// ListSources returns list of available sources COs
ListSources(f *SourceListFilters) (*unstructured.UnstructuredList, error)

// RawClient returns the raw dynamic client interface
RawClient() dynamic.Interface
}
Expand Down Expand Up @@ -94,3 +111,86 @@ func (c *knDynamicClient) ListSourcesTypes() (*unstructured.UnstructuredList, er
func (c knDynamicClient) RawClient() dynamic.Interface {
return c.client
}

// ListSources returns list of available sources COs
func (c *knDynamicClient) ListSources(f *SourceListFilters) (*unstructured.UnstructuredList, error) {
var sourceList unstructured.UnstructuredList
options := metav1.ListOptions{}
sourceTypes, err := c.ListSourcesTypes()
if err != nil {
return nil, err
}

namespace := c.Namespace()
// For each source type available, find out CO
for _, source := range sourceTypes.Items {
// only find COs if this source type is given in filter
if f != nil && f.filters != nil {
// find source kind before hand to fail early
sourceKind, err := kindFromUnstructured(&source)
if err != nil {
return nil, err
}

// if this source is not given in filter flags continue
if !sliceContainsIgnoreCase(sourceKind, f.filters) {
continue
}
}

gvr, err := gvrFromUnstructured(&source)
if err != nil {
return nil, err
}

sList, err := c.client.Resource(gvr).Namespace(namespace).List(options)
if err != nil {
return nil, err
}

if len(sList.Items) > 0 {
sourceList.Items = append(sourceList.Items, sList.Items...)
}
sourceList.SetGroupVersionKind(sList.GetObjectKind().GroupVersionKind())
}
return &sourceList, nil
}

func gvrFromUnstructured(u *unstructured.Unstructured) (gvr schema.GroupVersionResource, err error) {
content := u.UnstructuredContent()
group, found, err := unstructured.NestedString(content, "spec", "group")
if err != nil || !found {
return gvr, fmt.Errorf("can't find source GVR: %v", err)
}
version, found, err := unstructured.NestedString(content, "spec", "version")
if err != nil || !found {
return gvr, fmt.Errorf("can't find source GVR: %v", err)
}
resource, found, err := unstructured.NestedString(content, "spec", "names", "plural")
if err != nil || !found {
return gvr, fmt.Errorf("can't find source GVR: %v", err)
}
return schema.GroupVersionResource{
Group: group,
Version: version,
Resource: resource,
}, nil
}

func kindFromUnstructured(u *unstructured.Unstructured) (string, error) {
content := u.UnstructuredContent()
kind, found, err := unstructured.NestedString(content, "spec", "names", "kind")
if !found || err != nil {
return "", fmt.Errorf("can't find source kind: %v", err)
}
return kind, nil
}

func sliceContainsIgnoreCase(s string, slice []string) bool {
for _, each := range slice {
if strings.EqualFold(s, each) {
return true
}
}
return false
}
Loading

0 comments on commit 247fa41

Please sign in to comment.