// Copyright © 2019 The Knative Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package dynamic import ( "context" "errors" "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "knative.dev/client/pkg/util" "knative.dev/eventing/pkg/apis/messaging" ) const ( crdGroup = "apiextensions.k8s.io" crdVersion = "v1" crdKind = "CustomResourceDefinition" crdKinds = "customresourcedefinitions" sourcesLabelKey = "duck.knative.dev/source" sourcesLabelValue = "true" sourceListGroup = "client.knative.dev" sourceListVersion = "v1alpha1" sourceListKind = "SourceList" channelLabelValue = "true" channelListVersion = "v1" channelListKind = "ChannelList" channelKind = "Channel" ) // KnDynamicClient to client-go Dynamic client. All methods are relative to the // namespace specified during construction type KnDynamicClient interface { // Namespace in which this client is operating for Namespace() string // ListCRDs returns list of CRDs with their type and name ListCRDs(ctx context.Context, options metav1.ListOptions) (*unstructured.UnstructuredList, error) // ListSourcesTypes returns list of eventing sources CRDs ListSourcesTypes(ctx context.Context) (*unstructured.UnstructuredList, error) // ListSources returns list of available source objects ListSources(ctx context.Context, types ...WithType) (*unstructured.UnstructuredList, error) // ListSourcesUsingGVKs returns list of available source objects using given list of GVKs ListSourcesUsingGVKs(context.Context, *[]schema.GroupVersionKind, ...WithType) (*unstructured.UnstructuredList, error) // ListChannelsTypes returns installed knative channel CRDs ListChannelsTypes(ctx context.Context) (*unstructured.UnstructuredList, error) // ListChannelsUsingGVKs returns list of available channel objects using given list of GVKs ListChannelsUsingGVKs(context.Context, *[]schema.GroupVersionKind, ...WithType) (*unstructured.UnstructuredList, error) // RawClient returns the raw dynamic client interface RawClient() dynamic.Interface } // knDynamicClient is a combination of client-go Dynamic client interface and namespace type knDynamicClient struct { client dynamic.Interface namespace string } // NewKnDynamicClient is to invoke Eventing Sources Client API to create object func NewKnDynamicClient(client dynamic.Interface, namespace string) KnDynamicClient { return &knDynamicClient{ client: client, namespace: namespace, } } // Return the client's namespace func (c *knDynamicClient) Namespace() string { return c.namespace } // TODO(navidshaikh): Use ListConfigs here instead of ListOptions // ListCRDs returns list of installed CRDs in the cluster and filters based on the given options func (c *knDynamicClient) ListCRDs(ctx context.Context, options metav1.ListOptions) (*unstructured.UnstructuredList, error) { gvr := schema.GroupVersionResource{ Group: crdGroup, Version: crdVersion, Resource: crdKinds, } uList, err := c.client.Resource(gvr).List(ctx, options) if err != nil { return nil, err } return uList, nil } // ListSourcesTypes returns installed knative eventing sources CRDs func (c *knDynamicClient) ListSourcesTypes(ctx context.Context) (*unstructured.UnstructuredList, error) { options := metav1.ListOptions{} sourcesLabels := labels.Set{sourcesLabelKey: sourcesLabelValue} options.LabelSelector = sourcesLabels.String() return c.ListCRDs(ctx, options) } // ListChannelsTypes returns installed knative channel CRDs func (c *knDynamicClient) ListChannelsTypes(ctx context.Context) (*unstructured.UnstructuredList, error) { var ChannelTypeList unstructured.UnstructuredList options := metav1.ListOptions{} channelsLabels := labels.Set{messaging.SubscribableDuckVersionAnnotation: channelLabelValue} options.LabelSelector = channelsLabels.String() uList, err := c.ListCRDs(ctx, options) if err != nil { return nil, err } ChannelTypeList.Object = uList.Object for _, channelType := range uList.Items { content := channelType.UnstructuredContent() channelTypeKind, _, err := unstructured.NestedString(content, "spec", "names", "kind") if err != nil { return nil, err } if !util.SliceContainsIgnoreCase([]string{channelKind}, channelTypeKind) { ChannelTypeList.Items = append(ChannelTypeList.Items, channelType) } } return &ChannelTypeList, nil } func (c knDynamicClient) RawClient() dynamic.Interface { return c.client } // ListSources returns list of available sources objects // Provide the list of source types as for example: WithTypes("pingsource", "apiserversource"...) to list // only given types of source objects func (c *knDynamicClient) ListSources(ctx context.Context, types ...WithType) (*unstructured.UnstructuredList, error) { var ( sourceList unstructured.UnstructuredList options metav1.ListOptions ) sourceTypes, err := c.ListSourcesTypes(ctx) if err != nil { return nil, err } if sourceTypes == nil || len(sourceTypes.Items) == 0 { return nil, errors.New("no sources found on the backend, please verify the installation") } namespace := c.Namespace() filters := WithTypes(types).List() // For each source type available, find out each source types objects for i := range sourceTypes.Items { source := &sourceTypes.Items[i] // find source kind before hand to fail early sourceKind, err := kindFromUnstructured(source) if err != nil { return nil, err } if len(filters) > 0 && !util.SliceContainsIgnoreCase(filters, sourceKind) { continue } // find source's GVR from unstructured source type object gvr, err := gvrFromUnstructured(source) if err != nil { return nil, err } // list objects of source type with this GVR sList, err := c.client.Resource(gvr).Namespace(namespace).List(ctx, options) if err != nil { return nil, err } if len(sList.Items) > 0 { sourceList.Items = append(sourceList.Items, sList.Items...) } } if len(sourceList.Items) > 0 { sourceList.SetGroupVersionKind(schema.GroupVersionKind{Group: sourceListGroup, Version: sourceListVersion, Kind: sourceListKind}) } return &sourceList, nil } // ListSourcesUsingGVKs returns list of available source objects using given list of GVKs func (c *knDynamicClient) ListSourcesUsingGVKs(ctx context.Context, gvks *[]schema.GroupVersionKind, types ...WithType) (*unstructured.UnstructuredList, error) { if gvks == nil { return nil, nil } var ( sourceList unstructured.UnstructuredList options metav1.ListOptions ) namespace := c.Namespace() filters := WithTypes(types).List() for _, gvk := range *gvks { if len(filters) > 0 && !util.SliceContainsIgnoreCase(filters, gvk.Kind) { continue } gvr := gvk.GroupVersion().WithResource(strings.ToLower(gvk.Kind) + "s") // list objects of source type with this GVR sList, err := c.client.Resource(gvr).Namespace(namespace).List(ctx, options) if err != nil { return nil, err } if len(sList.Items) > 0 { sourceList.Items = append(sourceList.Items, sList.Items...) } } if len(sourceList.Items) > 0 { sourceList.SetGroupVersionKind(schema.GroupVersionKind{Group: sourceListGroup, Version: sourceListVersion, Kind: sourceListKind}) } return &sourceList, nil } // ListChannelsUsingGVKs returns list of available channel objects using given list of GVKs func (c *knDynamicClient) ListChannelsUsingGVKs(ctx context.Context, gvks *[]schema.GroupVersionKind, types ...WithType) (*unstructured.UnstructuredList, error) { if gvks == nil { return nil, nil } var ( channelList unstructured.UnstructuredList options metav1.ListOptions ) namespace := c.Namespace() filters := WithTypes(types).List() for _, gvk := range *gvks { if len(filters) > 0 && !util.SliceContainsIgnoreCase(filters, gvk.Kind) { continue } gvr := gvk.GroupVersion().WithResource(strings.ToLower(gvk.Kind) + "s") // list objects of channel type with this GVR cList, err := c.client.Resource(gvr).Namespace(namespace).List(ctx, options) if err != nil { return nil, err } if len(cList.Items) > 0 { channelList.Items = append(channelList.Items, cList.Items...) } } if len(channelList.Items) > 0 { channelList.SetGroupVersionKind(schema.GroupVersionKind{Group: messaging.GroupName, Version: channelListVersion, Kind: channelListKind}) } return &channelList, nil }