Refactors service catalog and discovery client code (#4272)

* Refactors service catalog and discovery client code and moves them into the kclient package.

It also refactors the operator code.

* Renames ListClusterServiceVersion() to ListClusterServiceVersions()
This commit is contained in:
Mrinal Das
2020-12-04 00:08:14 +05:30
committed by GitHub
parent ed4a082b77
commit bf6b1b4c3a
24 changed files with 1366 additions and 1340 deletions

View File

@@ -59,7 +59,7 @@ func ListInProject(client *occlient.Client) ([]string, error) {
// Get all ServiceInstances with the "app" label
// Okay, so there is an edge-case here.. if Service Catalog is *not* enabled in the cluster, we shouldn't error out..
// however, we should at least warn the user.
serviceInstanceAppNames, err := client.GetServiceInstanceLabelValues(applabels.ApplicationLabel, applabels.ApplicationLabel)
serviceInstanceAppNames, err := client.GetKubeClient().ListServiceInstanceLabelValues(applabels.ApplicationLabel, applabels.ApplicationLabel)
if err != nil {
klog.V(4).Infof("Unable to list Service Catalog instances: %s", err)
} else {

View File

@@ -298,7 +298,7 @@ func ListServices(client *occlient.Client) (ServiceTypeList, error) {
// ListOperatorServices fetches a list of Operators from the cluster and
// returns only those Operators which are successfully installed on the cluster
func ListOperatorServices(client *kclient.Client) (*olm.ClusterServiceVersionList, error) {
allCsvs, err := client.GetClusterServiceVersionList()
allCsvs, err := client.ListClusterServiceVersions()
if err != nil {
return nil, err
}
@@ -345,12 +345,12 @@ func SearchService(client *occlient.Client, name string) (ServiceTypeList, error
func getClusterCatalogServices(client *occlient.Client) ([]ServiceType, error) {
var classNames []ServiceType
classes, err := client.GetClusterServiceClasses()
classes, err := client.GetKubeClient().ListClusterServiceClasses()
if err != nil {
return nil, errors.Wrap(err, "unable to get cluster service classes")
}
planListItems, err := client.GetAllClusterServicePlans()
planListItems, err := client.GetKubeClient().ListClusterServicePlans()
if err != nil {
return nil, errors.Wrap(err, "Unable to get service plans")
}

View File

@@ -1,16 +1,17 @@
package kclient
import (
fakeKubeClientset "k8s.io/client-go/kubernetes/fake"
fakeServiceCatalogClientSet "github.com/kubernetes-sigs/service-catalog/pkg/client/clientset_generated/clientset/fake"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fakeKubeClientset "k8s.io/client-go/kubernetes/fake"
)
// FakeClientset holds fake ClientSets
// this is returned by FakeNew to access methods of fake client sets
type FakeClientset struct {
Kubernetes *fakeKubeClientset.Clientset
Kubernetes *fakeKubeClientset.Clientset
ServiceCatalogClientSet *fakeServiceCatalogClientSet.Clientset
}
// FakeNew creates new fake client for testing
@@ -23,6 +24,9 @@ func FakeNew() (*Client, *FakeClientset) {
fkclientset.Kubernetes = fakeKubeClientset.NewSimpleClientset()
client.KubeClient = fkclientset.Kubernetes
fkclientset.ServiceCatalogClientSet = fakeServiceCatalogClientSet.NewSimpleClientset()
client.serviceCatalogClient = fkclientset.ServiceCatalogClientSet.ServicecatalogV1beta1()
return &client, &fkclientset
}

View File

@@ -4,10 +4,13 @@ import (
"strings"
"time"
servicecatalogclienset "github.com/kubernetes-sigs/service-catalog/pkg/client/clientset_generated/clientset/typed/servicecatalog/v1beta1"
"github.com/openshift/odo/pkg/util"
"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
appsclientset "k8s.io/client-go/kubernetes/typed/apps/v1"
"k8s.io/klog"
@@ -33,16 +36,19 @@ Consult your Kubernetes distribution's documentation for more details
// Client is a collection of fields used for client configuration and interaction
type Client struct {
KubeClient kubernetes.Interface
KubeConfig clientcmd.ClientConfig
KubeClientConfig *rest.Config
Namespace string
OperatorClient *operatorsclientset.OperatorsV1alpha1Client
appsClient appsclientset.AppsV1Interface
KubeClient kubernetes.Interface
KubeConfig clientcmd.ClientConfig
KubeClientConfig *rest.Config
Namespace string
OperatorClient *operatorsclientset.OperatorsV1alpha1Client
appsClient appsclientset.AppsV1Interface
serviceCatalogClient servicecatalogclienset.ServicecatalogV1beta1Interface
// DynamicClient interacts with client-go's `dynamic` package. It is used
// to dynamically create service from an operator. It can take an arbitrary
// yaml and create k8s/OpenShift resource from it.
DynamicClient dynamic.Interface
DynamicClient dynamic.Interface
discoveryClient discovery.DiscoveryInterface
supportedResources map[string]bool
}
// New creates a new client
@@ -93,6 +99,16 @@ func NewForConfig(config clientcmd.ClientConfig) (client *Client, err error) {
}
client.appsClient = appsClient
client.serviceCatalogClient, err = servicecatalogclienset.NewForConfig(client.KubeClientConfig)
if err != nil {
return nil, err
}
client.discoveryClient, err = discovery.NewDiscoveryClientForConfig(client.KubeClientConfig)
if err != nil {
return nil, err
}
return client, nil
}
@@ -178,3 +194,36 @@ func (c *Client) GeneratePortForwardReq(podName string) *rest.Request {
Name(podName).
SubResource("portforward")
}
func (c *Client) SetDiscoveryInterface(client discovery.DiscoveryInterface) {
c.discoveryClient = client
}
func (c *Client) IsResourceSupported(apiGroup, apiVersion, resourceName string) (bool, error) {
if c.supportedResources == nil {
c.supportedResources = make(map[string]bool, 7)
}
groupVersion := metav1.GroupVersion{Group: apiGroup, Version: apiVersion}.String()
supported, found := c.supportedResources[groupVersion]
if !found {
list, err := c.discoveryClient.ServerResourcesForGroupVersion(groupVersion)
if err != nil {
if kerrors.IsNotFound(err) {
supported = false
} else {
// don't record, just attempt again next time in case it's a transient error
return false, err
}
} else {
for _, resources := range list.APIResources {
if resources.Name == resourceName {
supported = true
break
}
}
}
c.supportedResources[groupVersion] = supported
}
return supported, nil
}

View File

@@ -6,6 +6,7 @@ import (
olm "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
"github.com/pkg/errors"
kerrors "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog"
)
@@ -18,12 +19,25 @@ const (
apiVersion = "odo.dev/v1alpha1"
)
// GetClusterServiceVersionList returns a list of CSVs in the cluster
// IsSBRSupported checks if resource of type service binding request present on the cluster
func (c *Client) IsSBRSupported() (bool, error) {
return c.IsResourceSupported("apps.openshift.io", "v1alpha1", "servicebindingrequests")
}
// IsCSVSupported checks if resource of type service binding request present on the cluster
func (c *Client) IsCSVSupported() (bool, error) {
return c.IsResourceSupported("operators.coreos.com", "v1alpha1", "clusterserviceversions")
}
// ListClusterServiceVersions returns a list of CSVs in the cluster
// It is equivalent to doing `oc get csvs` using oc cli
func (c *Client) GetClusterServiceVersionList() (*olm.ClusterServiceVersionList, error) {
func (c *Client) ListClusterServiceVersions() (*olm.ClusterServiceVersionList, error) {
klog.V(3).Infof("Fetching list of operators installed in cluster")
csvs, err := c.OperatorClient.ClusterServiceVersions(c.Namespace).List(v1.ListOptions{})
if err != nil {
if kerrors.IsNotFound(err) {
return &olm.ClusterServiceVersionList{}, ErrNoSuchOperator
}
return &olm.ClusterServiceVersionList{}, err
}
return csvs, nil
@@ -31,16 +45,11 @@ func (c *Client) GetClusterServiceVersionList() (*olm.ClusterServiceVersionList,
// GetClusterServiceVersion returns a particular CSV from a list of CSVs
func (c *Client) GetClusterServiceVersion(name string) (olm.ClusterServiceVersion, error) {
csvs, err := c.GetClusterServiceVersionList()
csv, err := c.OperatorClient.ClusterServiceVersions(c.Namespace).Get(name, v1.GetOptions{})
if err != nil {
return olm.ClusterServiceVersion{}, err
}
for _, item := range csvs.Items {
if item.Name == name {
return item, nil
}
}
return olm.ClusterServiceVersion{}, ErrNoSuchOperator
return *csv, nil
}
// GetCustomResourcesFromCSV returns a list of CRs provided by an operator/CSV.
@@ -53,7 +62,7 @@ func (c *Client) GetCustomResourcesFromCSV(csv *olm.ClusterServiceVersion) *[]ol
// given keyword then return it
func (c *Client) SearchClusterServiceVersionList(name string) (*olm.ClusterServiceVersionList, error) {
var result []olm.ClusterServiceVersion
csvs, err := c.GetClusterServiceVersionList()
csvs, err := c.ListClusterServiceVersions()
if err != nil {
return &olm.ClusterServiceVersionList{}, errors.Wrap(err, "unable to list services")
}
@@ -83,7 +92,7 @@ func (c *Client) SearchClusterServiceVersionList(name string) (*olm.ClusterServi
// GetCustomResource returns the CR matching the name
func (c *Client) GetCustomResource(customResource string) (*olm.CRDDescription, error) {
// Get all csvs in the namespace
csvs, err := c.GetClusterServiceVersionList()
csvs, err := c.ListClusterServiceVersions()
if err != nil {
return &olm.CRDDescription{}, err
}
@@ -105,7 +114,7 @@ func (c *Client) GetCustomResource(customResource string) (*olm.CRDDescription,
// GetCSVWithCR returns the CSV (Operator) that contains the CR (service)
func (c *Client) GetCSVWithCR(name string) (*olm.ClusterServiceVersion, error) {
csvs, err := c.GetClusterServiceVersionList()
csvs, err := c.ListClusterServiceVersions()
if err != nil {
return &olm.ClusterServiceVersion{}, errors.Wrap(err, "unable to list services")
}

View File

@@ -0,0 +1,250 @@
package kclient
import (
"encoding/json"
"fmt"
scv1beta1 "github.com/kubernetes-sigs/service-catalog/pkg/apis/servicecatalog/v1beta1"
"github.com/openshift/odo/pkg/util"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog"
"sort"
)
// serviceInstanceParameters converts a map of variable assignments to a byte encoded json document,
// which is what the ServiceCatalog API consumes.
func serviceInstanceParameters(params map[string]string) (*runtime.RawExtension, error) {
paramsJSON, err := json.Marshal(params)
if err != nil {
return nil, err
}
return &runtime.RawExtension{Raw: paramsJSON}, nil
}
// CreateServiceInstance creates service instance from service catalog
func (c *Client) CreateServiceInstance(serviceName string, serviceType string, servicePlan string, parameters map[string]string, labels map[string]string) error {
serviceInstanceParameters, err := serviceInstanceParameters(parameters)
if err != nil {
return errors.Wrap(err, "unable to create the service instance parameters")
}
_, err = c.serviceCatalogClient.ServiceInstances(c.Namespace).Create(
&scv1beta1.ServiceInstance{
TypeMeta: metav1.TypeMeta{
Kind: "ServiceInstance",
APIVersion: "servicecatalog.k8s.io/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: serviceName,
Namespace: c.Namespace,
Labels: labels,
},
Spec: scv1beta1.ServiceInstanceSpec{
PlanReference: scv1beta1.PlanReference{
ClusterServiceClassExternalName: serviceType,
ClusterServicePlanExternalName: servicePlan,
},
Parameters: serviceInstanceParameters,
},
})
if err != nil {
return errors.Wrapf(err, "unable to create the service instance %s for the service type %s and plan %s", serviceName, serviceType, servicePlan)
}
// Create the secret containing the parameters of the plan selected.
err = c.CreateServiceBinding(serviceName, c.Namespace, labels)
if err != nil {
return errors.Wrapf(err, "unable to create the secret %s for the service instance", serviceName)
}
return nil
}
// ListServiceInstances returns list service instances
func (c *Client) ListServiceInstances(selector string) ([]scv1beta1.ServiceInstance, error) {
// List ServiceInstance according to given selectors
svcList, err := c.serviceCatalogClient.ServiceInstances(c.Namespace).List(metav1.ListOptions{LabelSelector: selector})
if err != nil {
return nil, errors.Wrap(err, "unable to list ServiceInstances")
}
return svcList.Items, nil
}
// DeleteServiceInstance takes labels as a input and based on it, deletes respective service instance
func (c *Client) DeleteServiceInstance(labels map[string]string) error {
klog.V(3).Infof("Deleting Service Instance")
// convert labels to selector
selector := util.ConvertLabelsToSelector(labels)
klog.V(3).Infof("Selectors used for deletion: %s", selector)
// Listing out serviceInstance because `DeleteCollection` method don't work on serviceInstance
serviceInstances, err := c.ListServiceInstances(selector)
if err != nil {
return errors.Wrap(err, "unable to list service instance")
}
// Iterating over serviceInstance List and deleting one by one
for _, serviceInstance := range serviceInstances {
// we need to delete the ServiceBinding before deleting the ServiceInstance
err = c.serviceCatalogClient.ServiceBindings(c.Namespace).Delete(serviceInstance.Name, &metav1.DeleteOptions{})
if err != nil {
return errors.Wrap(err, "unable to delete serviceBinding")
}
// now we perform the actual deletion
err = c.serviceCatalogClient.ServiceInstances(c.Namespace).Delete(serviceInstance.Name, &metav1.DeleteOptions{})
if err != nil {
return errors.Wrap(err, "unable to delete serviceInstance")
}
}
return nil
}
// GetClusterServiceClass returns the required service class from the service name
// serviceName is the name of the service
// returns the required service class and the error
func (c *Client) GetClusterServiceClass(serviceName string) (*scv1beta1.ClusterServiceClass, error) {
opts := metav1.ListOptions{
FieldSelector: fields.OneTermEqualSelector("spec.externalName", serviceName).String(),
}
searchResults, err := c.serviceCatalogClient.ClusterServiceClasses().List(opts)
if err != nil {
return nil, fmt.Errorf("unable to search classes by name (%s)", err)
}
if len(searchResults.Items) == 0 {
return nil, fmt.Errorf("class '%s' not found", serviceName)
}
if len(searchResults.Items) > 1 {
return nil, fmt.Errorf("more than one matching class found for '%s'", serviceName)
}
return &searchResults.Items[0], nil
}
// ListClusterServiceClasses queries the service service catalog to get available clusterServiceClasses
func (c *Client) ListClusterServiceClasses() ([]scv1beta1.ClusterServiceClass, error) {
classList, err := c.serviceCatalogClient.ClusterServiceClasses().List(metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "unable to list cluster service classes")
}
return classList.Items, nil
}
// ListServiceClassesByCategory retrieves a map associating category name to ClusterServiceClasses matching the category
func (c *Client) ListServiceClassesByCategory() (categories map[string][]scv1beta1.ClusterServiceClass, err error) {
categories = make(map[string][]scv1beta1.ClusterServiceClass)
classes, err := c.ListClusterServiceClasses()
if err != nil {
return nil, err
}
// TODO: Should we replicate the classification performed in
// https://github.com/openshift/console/blob/master/frontend/public/components/catalog/catalog-items.jsx?
for _, class := range classes {
tags := class.Spec.Tags
category := "other"
if len(tags) > 0 && len(tags[0]) > 0 {
category = tags[0]
}
categories[category] = append(categories[category], class)
}
return categories, err
}
// ListClusterServicePlans returns list of available plans
func (c *Client) ListClusterServicePlans() ([]scv1beta1.ClusterServicePlan, error) {
planList, err := c.serviceCatalogClient.ClusterServicePlans().List(metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "unable to get cluster service plan")
}
return planList.Items, nil
}
// ListClusterServicePlansByServiceName returns the plans associated with a service class
// serviceName is the name (the actual id, NOT the external name) of the service class whose plans are required
// returns array of ClusterServicePlans or error
func (c *Client) ListClusterServicePlansByServiceName(serviceName string) ([]scv1beta1.ClusterServicePlan, error) {
opts := metav1.ListOptions{
FieldSelector: fields.OneTermEqualSelector("spec.clusterServiceClassRef.name", serviceName).String(),
}
searchResults, err := c.serviceCatalogClient.ClusterServicePlans().List(opts)
if err != nil {
return nil, fmt.Errorf("unable to search plans for service name '%s', (%s)", serviceName, err)
}
return searchResults.Items, nil
}
// GetServiceBinding returns the ServiceBinding named serviceName in the namespace namespace
func (c *Client) GetServiceBinding(serviceName string, namespace string) (*scv1beta1.ServiceBinding, error) {
return c.serviceCatalogClient.ServiceBindings(namespace).Get(serviceName, metav1.GetOptions{})
}
// CreateServiceBinding creates a ServiceBinding (essentially a secret) within the namespace of the
// service instance created using the service's parameters.
func (c *Client) CreateServiceBinding(bindingName string, namespace string, labels map[string]string) error {
_, err := c.serviceCatalogClient.ServiceBindings(namespace).Create(
&scv1beta1.ServiceBinding{
ObjectMeta: metav1.ObjectMeta{
Name: bindingName,
Namespace: namespace,
Labels: labels,
},
Spec: scv1beta1.ServiceBindingSpec{
InstanceRef: scv1beta1.LocalObjectReference{
Name: bindingName,
},
SecretName: bindingName,
},
})
if err != nil {
return errors.Wrap(err, "Creation of the secret failed")
}
return nil
}
// ListMatchingPlans retrieves a map associating service plan name to service plan instance associated with the specified service
// class
func (c *Client) ListMatchingPlans(class scv1beta1.ClusterServiceClass) (plans map[string]scv1beta1.ClusterServicePlan, err error) {
planList, err := c.serviceCatalogClient.ClusterServicePlans().List(metav1.ListOptions{
FieldSelector: "spec.clusterServiceClassRef.name==" + class.Spec.ExternalID,
})
plans = make(map[string]scv1beta1.ClusterServicePlan)
for _, v := range planList.Items {
plans[v.Spec.ExternalName] = v
}
return plans, err
}
// ListServiceInstanceLabelValues get label values of given label from objects in project that match the selector
func (c *Client) ListServiceInstanceLabelValues(label string, selector string) ([]string, error) {
// List ServiceInstance according to given selectors
svcList, err := c.serviceCatalogClient.ServiceInstances(c.Namespace).List(metav1.ListOptions{LabelSelector: selector})
if err != nil {
return nil, errors.Wrap(err, "unable to list ServiceInstances")
}
// Grab all the matched strings
var values []string
for _, elem := range svcList.Items {
val, ok := elem.Labels[label]
if ok {
values = append(values, val)
}
}
// Sort alphabetically
sort.Strings(values)
return values, nil
}

View File

@@ -0,0 +1,980 @@
package kclient
import (
"encoding/json"
"fmt"
"github.com/kylelemons/godebug/pretty"
applabels "github.com/openshift/odo/pkg/application/labels"
componentlabels "github.com/openshift/odo/pkg/component/labels"
"github.com/openshift/odo/pkg/testingutil"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"reflect"
"strings"
"testing"
scv1beta1 "github.com/kubernetes-sigs/service-catalog/pkg/apis/servicecatalog/v1beta1"
ktesting "k8s.io/client-go/testing"
)
func fakePlanExternalMetaDataRaw() ([][]byte, error) {
planExternalMetaData1 := make(map[string]string)
planExternalMetaData1["displayName"] = "plan-name-1"
planExternalMetaData2 := make(map[string]string)
planExternalMetaData2["displayName"] = "plan-name-2"
planExternalMetaDataRaw1, err := json.Marshal(planExternalMetaData1)
if err != nil {
return nil, errors.Wrap(err, "")
}
planExternalMetaDataRaw2, err := json.Marshal(planExternalMetaData2)
if err != nil {
return nil, errors.Wrap(err, "")
}
var data [][]byte
data = append(data, planExternalMetaDataRaw1)
data = append(data, planExternalMetaDataRaw2)
return data, nil
}
func fakePlanInstanceCreateParameterSchemasRaw() ([][]byte, error) {
planInstanceCreateParameterSchema1 := make(map[string][]string)
planInstanceCreateParameterSchema1["required"] = []string{"PLAN_DATABASE_URI", "PLAN_DATABASE_USERNAME", "PLAN_DATABASE_PASSWORD"}
planInstanceCreateParameterSchema2 := make(map[string][]string)
planInstanceCreateParameterSchema2["required"] = []string{"PLAN_DATABASE_USERNAME_2", "PLAN_DATABASE_PASSWORD"}
planInstanceCreateParameterSchemaRaw1, err := json.Marshal(planInstanceCreateParameterSchema1)
if err != nil {
if err != nil {
return nil, errors.Wrap(err, "")
}
}
planInstanceCreateParameterSchemaRaw2, err := json.Marshal(planInstanceCreateParameterSchema2)
if err != nil {
if err != nil {
return nil, errors.Wrap(err, "")
}
}
var data [][]byte
data = append(data, planInstanceCreateParameterSchemaRaw1)
data = append(data, planInstanceCreateParameterSchemaRaw2)
return data, nil
}
func TestDeleteServiceInstance(t *testing.T) {
tests := []struct {
name string
serviceName string
labels map[string]string
serviceList scv1beta1.ServiceInstanceList
wantErr bool
}{
{
name: "Delete service instance",
serviceName: "mongodb",
labels: map[string]string{
applabels.ApplicationLabel: "app",
componentlabels.ComponentLabel: "mongodb",
},
serviceList: scv1beta1.ServiceInstanceList{
Items: []scv1beta1.ServiceInstance{
{
ObjectMeta: metav1.ObjectMeta{
Name: "mongodb",
Labels: map[string]string{
applabels.ApplicationLabel: "app",
componentlabels.ComponentLabel: "mongodb",
componentlabels.ComponentTypeLabel: "mongodb-persistent",
},
},
},
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fkclient, fkclientset := FakeNew()
//fake the services listing
fkclientset.ServiceCatalogClientSet.PrependReactor("list", "serviceinstances", func(action ktesting.Action) (bool, runtime.Object, error) {
return true, &tt.serviceList, nil
})
// Fake the servicebinding delete
fkclientset.ServiceCatalogClientSet.PrependReactor("delete", "servicebindings", func(action ktesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
// Fake the serviceinstance delete
fkclientset.ServiceCatalogClientSet.PrependReactor("delete", "serviceinstances", func(action ktesting.Action) (bool, runtime.Object, error) {
return true, nil, nil
})
err := fkclient.DeleteServiceInstance(tt.labels)
// Checks for error in positive cases
if !tt.wantErr && (err != nil) {
t.Errorf(" client.DeleteServiceInstance(labels) unexpected error %v, wantErr %v", err, tt.wantErr)
}
// Check for validating actions performed
// deleting based on the labels means listing the services and then delete the instance and binding for each
// thus we have 1 list action that always takes place, plus another 2 (delete instance, delete binding)
// for each service
expectedNumberOfServiceCatalogActions := 1 + (2 * len(tt.serviceList.Items))
if len(fkclientset.ServiceCatalogClientSet.Actions()) != expectedNumberOfServiceCatalogActions && !tt.wantErr {
t.Errorf("expected %d action in CreateServiceInstace got: %v",
expectedNumberOfServiceCatalogActions, fkclientset.ServiceCatalogClientSet.Actions())
}
// Check that the correct service binding was deleted
DeletedServiceBinding := fkclientset.ServiceCatalogClientSet.Actions()[1].(ktesting.DeleteAction).GetName()
if DeletedServiceBinding != tt.serviceName {
t.Errorf("Delete action is performed with wrong ServiceBinding, expected: %s, got %s", tt.serviceName, DeletedServiceBinding)
}
// Check that the correct service instance was deleted
DeletedServiceInstance := fkclientset.ServiceCatalogClientSet.Actions()[2].(ktesting.DeleteAction).GetName()
if DeletedServiceInstance != tt.serviceName {
t.Errorf("Delete action is performed with wrong ServiceInstance, expected: %s, got %s", tt.serviceName, DeletedServiceInstance)
}
})
}
}
func TestListServiceInstances(t *testing.T) {
type args struct {
Project string
Selector string
}
tests := []struct {
name string
args args
serviceList scv1beta1.ServiceInstanceList
output []scv1beta1.ServiceInstance
wantErr bool
}{
{
name: "test case 1",
args: args{
Project: "myproject",
Selector: "app.kubernetes.io/instance=mysql-persistent,app.kubernetes.io/part-of=app",
},
serviceList: scv1beta1.ServiceInstanceList{
Items: []scv1beta1.ServiceInstance{
{
ObjectMeta: metav1.ObjectMeta{
Name: "mysql-persistent",
Finalizers: []string{"kubernetes-incubator/service-catalog"},
Labels: map[string]string{
applabels.ApplicationLabel: "app",
componentlabels.ComponentLabel: "mysql-persistent",
componentlabels.ComponentTypeLabel: "mysql-persistent",
},
Namespace: "myproject",
},
Spec: scv1beta1.ServiceInstanceSpec{
PlanReference: scv1beta1.PlanReference{
ClusterServiceClassExternalName: "mysql-persistent",
ClusterServicePlanExternalName: "default",
},
},
Status: scv1beta1.ServiceInstanceStatus{
Conditions: []scv1beta1.ServiceInstanceCondition{
{
Reason: "ProvisionedSuccessfully",
},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "jenkins-persistent",
Finalizers: []string{"kubernetes-incubator/service-catalog"},
Labels: map[string]string{
applabels.ApplicationLabel: "app",
componentlabels.ComponentLabel: "jenkins-persistent",
componentlabels.ComponentTypeLabel: "jenkins-persistent",
},
Namespace: "myproject",
},
Spec: scv1beta1.ServiceInstanceSpec{
PlanReference: scv1beta1.PlanReference{
ClusterServiceClassExternalName: "jenkins-persistent",
ClusterServicePlanExternalName: "default",
},
},
Status: scv1beta1.ServiceInstanceStatus{
Conditions: []scv1beta1.ServiceInstanceCondition{
{
Reason: "ProvisionedSuccessfully",
},
},
},
},
},
},
output: []scv1beta1.ServiceInstance{
{
ObjectMeta: metav1.ObjectMeta{
Name: "mysql-persistent",
Finalizers: []string{"kubernetes-incubator/service-catalog"},
Labels: map[string]string{
applabels.ApplicationLabel: "app",
componentlabels.ComponentLabel: "mysql-persistent",
componentlabels.ComponentTypeLabel: "mysql-persistent",
},
Namespace: "myproject",
},
Spec: scv1beta1.ServiceInstanceSpec{
PlanReference: scv1beta1.PlanReference{
ClusterServiceClassExternalName: "mysql-persistent",
ClusterServicePlanExternalName: "default",
},
},
Status: scv1beta1.ServiceInstanceStatus{
Conditions: []scv1beta1.ServiceInstanceCondition{
{
Reason: "ProvisionedSuccessfully",
},
},
},
},
},
wantErr: false,
},
}
for _, tt := range tests {
client, fakeClientSet := FakeNew()
fakeClientSet.ServiceCatalogClientSet.PrependReactor("list", "serviceinstances", func(action ktesting.Action) (bool, runtime.Object, error) {
if !reflect.DeepEqual(action.(ktesting.ListAction).GetListRestrictions().Labels.String(), tt.args.Selector) {
return true, nil, fmt.Errorf("labels not matching with expected values, expected:%s, got:%s", tt.args.Selector, action.(ktesting.ListAction).GetListRestrictions())
}
return true, &tt.serviceList, nil
})
svcInstanceList, err := client.ListServiceInstances(tt.args.Selector)
if !reflect.DeepEqual(tt.output, svcInstanceList) {
t.Errorf("expected output: %#v,got: %#v", tt.serviceList, svcInstanceList)
}
if err == nil && !tt.wantErr {
if (len(fakeClientSet.ServiceCatalogClientSet.Actions()) != 1) && (tt.wantErr != true) {
t.Errorf("expected 1 action in ListServicecatalog got: %v", fakeClientSet.ServiceCatalogClientSet.Actions())
}
} else if err == nil && tt.wantErr {
t.Error("test failed, expected: false, got true")
} else if err != nil && !tt.wantErr {
t.Errorf("test failed, expected: no error, got error: %s", err.Error())
}
}
}
func TestGetClusterServiceClass(t *testing.T) {
classExternalMetaData := make(map[string]interface{})
classExternalMetaData["longDescription"] = "example long description"
classExternalMetaData["dependencies"] = []string{"docker.io/centos/7", "docker.io/centos/8"}
classExternalMetaDataRaw, err := json.Marshal(classExternalMetaData)
if err != nil {
fmt.Printf("error occured %v during marshalling", err)
return
}
type args struct {
serviceName string
}
tests := []struct {
name string
args args
returnedServicesClasses *scv1beta1.ClusterServiceClassList
wantedServiceClass *scv1beta1.ClusterServiceClass
wantErr bool
}{
{
name: "test case 1: with one valid service class returned",
args: args{
serviceName: "class name",
},
returnedServicesClasses: &scv1beta1.ClusterServiceClassList{
Items: []scv1beta1.ClusterServiceClass{
{
ObjectMeta: metav1.ObjectMeta{Name: "1dda1477cace09730bd8ed7a6505607e"},
Spec: scv1beta1.ClusterServiceClassSpec{
CommonServiceClassSpec: scv1beta1.CommonServiceClassSpec{
ExternalName: "class name",
Bindable: false,
Description: "example description",
Tags: []string{"php", "java"},
ExternalMetadata: &runtime.RawExtension{Raw: classExternalMetaDataRaw},
},
ClusterServiceBrokerName: "broker name",
},
},
},
},
wantedServiceClass: &scv1beta1.ClusterServiceClass{
ObjectMeta: metav1.ObjectMeta{Name: "1dda1477cace09730bd8ed7a6505607e"},
Spec: scv1beta1.ClusterServiceClassSpec{
CommonServiceClassSpec: scv1beta1.CommonServiceClassSpec{
ExternalName: "class name",
Bindable: false,
Description: "example description",
Tags: []string{"php", "java"},
ExternalMetadata: &runtime.RawExtension{Raw: classExternalMetaDataRaw},
},
ClusterServiceBrokerName: "broker name",
},
},
wantErr: false,
},
{
name: "test case 2: with two service classes returned",
args: args{
serviceName: "class name",
},
returnedServicesClasses: &scv1beta1.ClusterServiceClassList{
Items: []scv1beta1.ClusterServiceClass{
{
ObjectMeta: metav1.ObjectMeta{Name: "1dda1477cace09730bd8ed7a6505607e"},
Spec: scv1beta1.ClusterServiceClassSpec{
CommonServiceClassSpec: scv1beta1.CommonServiceClassSpec{
ExternalName: "class name",
Bindable: false,
Description: "example description",
Tags: []string{"php", "java"},
ExternalMetadata: &runtime.RawExtension{Raw: classExternalMetaDataRaw},
},
ClusterServiceBrokerName: "broker name",
},
},
{
ObjectMeta: metav1.ObjectMeta{Name: "1dda1477cace09730bd8ed7a6505607e"},
Spec: scv1beta1.ClusterServiceClassSpec{
CommonServiceClassSpec: scv1beta1.CommonServiceClassSpec{
ExternalName: "class name",
Bindable: false,
Description: "example description",
Tags: []string{"java"},
ExternalMetadata: &runtime.RawExtension{Raw: classExternalMetaDataRaw},
},
ClusterServiceBrokerName: "broker name 1",
},
},
},
},
wantedServiceClass: &scv1beta1.ClusterServiceClass{},
wantErr: true,
},
{
name: "test case 3: with no service classes returned",
args: args{
serviceName: "class name",
},
returnedServicesClasses: &scv1beta1.ClusterServiceClassList{
Items: []scv1beta1.ClusterServiceClass{},
},
wantedServiceClass: &scv1beta1.ClusterServiceClass{},
wantErr: true,
},
}
for _, tt := range tests {
client, fakeClientSet := FakeNew()
fakeClientSet.ServiceCatalogClientSet.PrependReactor("list", "clusterserviceclasses", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
if action.(ktesting.ListAction).GetListRestrictions().Fields.String() != fmt.Sprintf("spec.externalName=%v", tt.args.serviceName) {
t.Errorf("got a different service name got: %v , expected: %v", action.(ktesting.ListAction).GetListRestrictions().Fields.String(), fmt.Sprintf("spec.externalName=%v", tt.args.serviceName))
}
return true, tt.returnedServicesClasses, nil
})
gotServiceClass, err := client.GetClusterServiceClass(tt.args.serviceName)
if err == nil && !tt.wantErr {
if len(fakeClientSet.ServiceCatalogClientSet.Actions()) != 1 {
t.Errorf("expected 1 action in GetServiceClassAndPlans got: %v", fakeClientSet.ServiceCatalogClientSet.Actions())
}
if !reflect.DeepEqual(gotServiceClass.Spec, tt.wantedServiceClass.Spec) {
t.Errorf("different service class spec value expected: %v", pretty.Compare(gotServiceClass.Spec, tt.wantedServiceClass.Spec))
}
if !reflect.DeepEqual(gotServiceClass.Name, tt.wantedServiceClass.Name) {
t.Errorf("different service class name value expected got: %v , expected: %v", gotServiceClass.Name, tt.wantedServiceClass.Name)
}
} else if err == nil && tt.wantErr {
t.Error("test failed, expected: false, got true")
} else if err != nil && !tt.wantErr {
t.Errorf("test failed, expected: no error, got error: %s", err.Error())
}
}
}
func TestListClusterServicePlansByServiceName(t *testing.T) {
planExternalMetaDataRaw, err := fakePlanExternalMetaDataRaw()
if err != nil {
fmt.Printf("error occured %v during marshalling", err)
return
}
planInstanceCreateParameterSchemasRaw, err := fakePlanInstanceCreateParameterSchemasRaw()
if err != nil {
fmt.Printf("error occured %v during marshalling", err)
return
}
type args struct {
serviceClassName string
}
tests := []struct {
name string
args args
want []scv1beta1.ClusterServicePlan
wantErr bool
}{
{
name: "test case 1 : plans found for the service class",
args: args{serviceClassName: "1dda1477cace09730bd8ed7a6505607e"},
wantErr: false,
want: []scv1beta1.ClusterServicePlan{
{
ObjectMeta: metav1.ObjectMeta{
Name: "67042296c7c95e84142f21f58da2ebfe",
},
Spec: scv1beta1.ClusterServicePlanSpec{
ClusterServiceClassRef: scv1beta1.ClusterObjectReference{
Name: "1dda1477cace09730bd8ed7a6505607e",
},
CommonServicePlanSpec: scv1beta1.CommonServicePlanSpec{
ExternalName: "dev",
Description: "this is a example description 1",
ExternalMetadata: &runtime.RawExtension{Raw: planExternalMetaDataRaw[0]},
InstanceCreateParameterSchema: &runtime.RawExtension{Raw: planInstanceCreateParameterSchemasRaw[0]},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "7f88be6129622f72554c20af879a8ce0",
},
Spec: scv1beta1.ClusterServicePlanSpec{
ClusterServiceClassRef: scv1beta1.ClusterObjectReference{
Name: "1dda1477cace09730bd8ed7a6505607e",
},
CommonServicePlanSpec: scv1beta1.CommonServicePlanSpec{
ExternalName: "prod",
Description: "this is a example description 2",
ExternalMetadata: &runtime.RawExtension{Raw: planExternalMetaDataRaw[1]},
InstanceCreateParameterSchema: &runtime.RawExtension{Raw: planInstanceCreateParameterSchemasRaw[1]},
},
},
},
},
},
{
name: "test case 2 : no plans found for the service class",
args: args{serviceClassName: "1dda1477cace09730bd8"},
wantErr: false,
want: []scv1beta1.ClusterServicePlan{},
},
}
planList := scv1beta1.ClusterServicePlanList{
Items: []scv1beta1.ClusterServicePlan{
{
ObjectMeta: metav1.ObjectMeta{
Name: "67042296c7c95e84142f21f58da2ebfe",
},
Spec: scv1beta1.ClusterServicePlanSpec{
ClusterServiceClassRef: scv1beta1.ClusterObjectReference{
Name: "1dda1477cace09730bd8ed7a6505607e",
},
CommonServicePlanSpec: scv1beta1.CommonServicePlanSpec{
ExternalName: "dev",
Description: "this is a example description 1",
ExternalMetadata: &runtime.RawExtension{Raw: planExternalMetaDataRaw[0]},
InstanceCreateParameterSchema: &runtime.RawExtension{Raw: planInstanceCreateParameterSchemasRaw[0]},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "7f88be6129622f72554c20af879a8ce0",
},
Spec: scv1beta1.ClusterServicePlanSpec{
ClusterServiceClassRef: scv1beta1.ClusterObjectReference{
Name: "1dda1477cace09730bd8ed7a6505607e",
},
CommonServicePlanSpec: scv1beta1.CommonServicePlanSpec{
ExternalName: "prod",
Description: "this is a example description 2",
ExternalMetadata: &runtime.RawExtension{Raw: planExternalMetaDataRaw[1]},
InstanceCreateParameterSchema: &runtime.RawExtension{Raw: planInstanceCreateParameterSchemasRaw[1]},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client, fakeClientSet := FakeNew()
fakeClientSet.ServiceCatalogClientSet.PrependReactor("list", "clusterserviceplans", func(action ktesting.Action) (bool, runtime.Object, error) {
var pList []scv1beta1.ClusterServicePlan
for _, plan := range planList.Items {
if plan.Spec.ClusterServiceClassRef.Name == strings.Split(action.(ktesting.ListAction).GetListRestrictions().Fields.String(), "=")[1] {
pList = append(pList, plan)
}
}
return true, &scv1beta1.ClusterServicePlanList{Items: pList}, nil
})
gotPlans, err := client.ListClusterServicePlansByServiceName(tt.args.serviceClassName)
if err == nil && !tt.wantErr {
if len(fakeClientSet.ServiceCatalogClientSet.Actions()) != 1 {
t.Errorf("expected 2 actions in GetServiceClassAndPlans got: %v", fakeClientSet.ServiceCatalogClientSet.Actions())
}
for _, wantedServicePlan := range tt.want {
found := false
for _, gotServicePlan := range gotPlans {
if reflect.DeepEqual(wantedServicePlan.Spec.ExternalName, gotServicePlan.Spec.ExternalName) {
found = true
} else {
continue
}
if !reflect.DeepEqual(wantedServicePlan.Name, gotServicePlan.Name) {
t.Errorf("different plan name expected got: %v , expected: %v", wantedServicePlan.Name, gotServicePlan.Name)
}
if !reflect.DeepEqual(wantedServicePlan.Spec, gotServicePlan.Spec) {
t.Errorf("different plan spec value expected: %v", pretty.Compare(wantedServicePlan.Spec, gotServicePlan.Spec))
}
}
if !found {
t.Errorf("service plan %v not found", wantedServicePlan.Spec.ExternalName)
}
}
} else if err == nil && tt.wantErr {
t.Error("test failed, expected: false, got true")
} else if err != nil && !tt.wantErr {
t.Errorf("test failed, expected: no error, got error: %s", err.Error())
}
})
}
}
func TestCreateServiceInstance(t *testing.T) {
type args struct {
serviceName string
serviceType string
labels map[string]string
plan string
parameters map[string]string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Create service instance",
args: args{
serviceName: "jenkins",
serviceType: "jenkins",
labels: map[string]string{
"name": "mongodb",
"namespace": "blog",
},
plan: "dev",
parameters: map[string]string{},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fkclient, fkclientset := FakeNew()
err := fkclient.CreateServiceInstance(tt.args.serviceName, tt.args.serviceType, tt.args.plan, tt.args.parameters, tt.args.labels)
// Checks for error in positive cases
if tt.wantErr == false && (err != nil) {
t.Errorf(" client.CreateServiceInstance(serviceName,serviceType, labels) unexpected error %v, wantErr %v", err, tt.wantErr)
}
// Check for validating actions performed
// creating a service instance also means creating a serviceBinding
// which is why we expect 2 actions
if len(fkclientset.ServiceCatalogClientSet.Actions()) != 2 && !tt.wantErr {
t.Errorf("expected 1 action in CreateServiceInstace got: %v", fkclientset.ServiceCatalogClientSet.Actions())
}
createdServiceInstance := fkclientset.ServiceCatalogClientSet.Actions()[0].(ktesting.CreateAction).GetObject().(*scv1beta1.ServiceInstance)
if !reflect.DeepEqual(createdServiceInstance.Labels, tt.args.labels) {
t.Errorf("labels in created serviceInstance is not matching expected labels, expected: %v, got: %v", tt.args.labels, createdServiceInstance.Labels)
}
if createdServiceInstance.Name != tt.args.serviceName {
t.Errorf("labels in created serviceInstance is not matching expected labels, expected: %v, got: %v", tt.args.serviceName, createdServiceInstance.Name)
}
if !reflect.DeepEqual(createdServiceInstance.Spec.ClusterServiceClassExternalName, tt.args.serviceType) {
t.Errorf("labels in created serviceInstance is not matching expected labels, expected: %v, got: %v", tt.args.serviceType, createdServiceInstance.Spec.ClusterServiceClassExternalName)
}
})
}
}
func TestGetServiceBinding(t *testing.T) {
tests := []struct {
name string
namespace string
serviceName string
wantErr bool
want *scv1beta1.ServiceBinding
}{
{
name: "Case: Valid request for retrieving a service binding",
namespace: "",
serviceName: "foo",
want: &scv1beta1.ServiceBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
},
wantErr: false,
},
{
name: "Case: Invalid request for retrieving a service binding",
namespace: "",
serviceName: "foo2",
want: &scv1beta1.ServiceBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeClient, fakeClientSet := FakeNew()
// Fake getting Secret
fakeClientSet.ServiceCatalogClientSet.PrependReactor("get", "servicebindings", func(action ktesting.Action) (bool, runtime.Object, error) {
if tt.want.Name != tt.serviceName {
return true, nil, fmt.Errorf("'get' called with a different serviebinding name")
}
return true, tt.want, nil
})
returnValue, err := fakeClient.GetServiceBinding(tt.serviceName, tt.namespace)
// Check for validating return value
if err == nil && returnValue != tt.want {
t.Errorf("error in return value got: %v, expected %v", returnValue, tt.want)
}
if !tt.wantErr == (err != nil) {
t.Errorf("\nclient.GetServiceBinding(serviceName, namespace) unexpected error %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestCreateServiceBinding(t *testing.T) {
tests := []struct {
name string
bindingNS string
bindingName string
labels map[string]string
wantErr bool
}{
{
name: "Case: Valid request for creating a secret",
bindingNS: "",
bindingName: "foo",
labels: map[string]string{"app": "app"},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeClient, fakeClientSet := FakeNew()
err := fakeClient.CreateServiceBinding(tt.bindingName, tt.bindingNS, tt.labels)
if err == nil && !tt.wantErr {
if len(fakeClientSet.ServiceCatalogClientSet.Actions()) != 1 {
t.Errorf("expected 1 ServiceCatalogClientSet.Actions() in CreateServiceBinding, got: %v", fakeClientSet.ServiceCatalogClientSet.Actions())
}
createdBinding := fakeClientSet.ServiceCatalogClientSet.Actions()[0].(ktesting.CreateAction).GetObject().(*scv1beta1.ServiceBinding)
if createdBinding.Name != tt.bindingName {
t.Errorf("the name of servicebinding was not correct, expected: %s, got: %s", tt.bindingName, createdBinding.Name)
}
} else if err == nil && tt.wantErr {
t.Error("error was expected, but no error was returned")
} else if err != nil && !tt.wantErr {
t.Errorf("test failed, no error was expected, but got unexpected error: %s", err)
}
})
}
}
func TestListServiceClassesByCategory(t *testing.T) {
t.Run("ListServiceClassesByCategory should work", func(t *testing.T) {
client, fakeClientSet := FakeNew()
foo := testingutil.FakeClusterServiceClass("foo", "footag", "footag2")
bar := testingutil.FakeClusterServiceClass("bar", "")
boo := testingutil.FakeClusterServiceClass("boo")
fakeClientSet.ServiceCatalogClientSet.PrependReactor("list", "clusterserviceclasses", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &scv1beta1.ClusterServiceClassList{
Items: []scv1beta1.ClusterServiceClass{
foo,
bar,
boo,
},
}, nil
})
expected := map[string][]scv1beta1.ClusterServiceClass{"footag": {foo}, "other": {bar, boo}}
categories, err := client.ListServiceClassesByCategory()
if err != nil {
t.Errorf("test failed due to %s", err.Error())
}
if !reflect.DeepEqual(expected, categories) {
t.Errorf("test failed, expected %v, got %v", expected, categories)
}
})
}
func TestListMatchingPlans(t *testing.T) {
t.Run("ListMatchingPlans should work", func(t *testing.T) {
client, fakeClientSet := FakeNew()
foo := testingutil.FakeClusterServiceClass("foo", "footag", "footag2")
dev := testingutil.FakeClusterServicePlan("dev", 1)
classId := foo.Spec.ExternalID
dev.Spec.ClusterServiceClassRef.Name = classId
prod := testingutil.FakeClusterServicePlan("prod", 2)
prod.Spec.ClusterServiceClassRef.Name = classId
fakeClientSet.ServiceCatalogClientSet.PrependReactor("list", "clusterserviceplans", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
value, _ := action.(ktesting.ListAction).GetListRestrictions().Fields.RequiresExactMatch("spec.clusterServiceClassRef.name")
if value != classId {
t.Errorf("cluster service plans list should have been filtered on 'spec.clusterServiceClassRef.name==%s'", classId)
}
return true, &scv1beta1.ClusterServicePlanList{
Items: []scv1beta1.ClusterServicePlan{
dev,
prod,
},
}, nil
})
expected := map[string]scv1beta1.ClusterServicePlan{"dev": dev, "prod": prod}
plans, err := client.ListMatchingPlans(foo)
if err != nil {
t.Errorf("test failed due to %s", err.Error())
}
if !reflect.DeepEqual(expected, plans) {
t.Errorf("test failed, expected %v, got %v", expected, plans)
}
})
}
func TestListServiceInstanceLabelValues(t *testing.T) {
type args struct {
serviceList scv1beta1.ServiceInstanceList
expectedOutput []string
// dcBefore appsv1.DeploymentConfig
}
tests := []struct {
name string
args args
wantErr bool
actions int
}{
{
name: "Case 1 - Retrieve list",
args: args{
expectedOutput: []string{"app", "app2"},
serviceList: scv1beta1.ServiceInstanceList{
Items: []scv1beta1.ServiceInstance{
{
ObjectMeta: metav1.ObjectMeta{
Name: "mysql-persistent",
Finalizers: []string{"kubernetes-incubator/service-catalog"},
Labels: map[string]string{
applabels.ApplicationLabel: "app",
componentlabels.ComponentLabel: "mysql-persistent",
componentlabels.ComponentTypeLabel: "mysql-persistent",
},
Namespace: "myproject",
},
Spec: scv1beta1.ServiceInstanceSpec{
PlanReference: scv1beta1.PlanReference{
ClusterServiceClassExternalName: "mysql-persistent",
ClusterServicePlanExternalName: "default",
},
},
Status: scv1beta1.ServiceInstanceStatus{
Conditions: []scv1beta1.ServiceInstanceCondition{
{
Reason: "ProvisionedSuccessfully",
},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "jenkins-persistent",
Finalizers: []string{"kubernetes-incubator/service-catalog"},
Labels: map[string]string{
applabels.ApplicationLabel: "app2",
componentlabels.ComponentLabel: "jenkins-persistent",
componentlabels.ComponentTypeLabel: "jenkins-persistent",
},
Namespace: "myproject",
},
Spec: scv1beta1.ServiceInstanceSpec{
PlanReference: scv1beta1.PlanReference{
ClusterServiceClassExternalName: "jenkins-persistent",
ClusterServicePlanExternalName: "default",
},
},
Status: scv1beta1.ServiceInstanceStatus{
Conditions: []scv1beta1.ServiceInstanceCondition{
{
Reason: "ProvisionedSuccessfully",
},
},
},
},
},
},
},
wantErr: false,
actions: 1,
},
{
name: "Case 2 - Retrieve list, different order",
args: args{
expectedOutput: []string{"app", "app2"},
serviceList: scv1beta1.ServiceInstanceList{
Items: []scv1beta1.ServiceInstance{
{
ObjectMeta: metav1.ObjectMeta{
Name: "mysql-persistent",
Finalizers: []string{"kubernetes-incubator/service-catalog"},
Labels: map[string]string{
applabels.ApplicationLabel: "app2",
componentlabels.ComponentLabel: "mysql-persistent",
componentlabels.ComponentTypeLabel: "mysql-persistent",
},
Namespace: "myproject",
},
Spec: scv1beta1.ServiceInstanceSpec{
PlanReference: scv1beta1.PlanReference{
ClusterServiceClassExternalName: "mysql-persistent",
ClusterServicePlanExternalName: "default",
},
},
Status: scv1beta1.ServiceInstanceStatus{
Conditions: []scv1beta1.ServiceInstanceCondition{
{
Reason: "ProvisionedSuccessfully",
},
},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "jenkins-persistent",
Finalizers: []string{"kubernetes-incubator/service-catalog"},
Labels: map[string]string{
applabels.ApplicationLabel: "app",
componentlabels.ComponentLabel: "jenkins-persistent",
componentlabels.ComponentTypeLabel: "jenkins-persistent",
},
Namespace: "myproject",
},
Spec: scv1beta1.ServiceInstanceSpec{
PlanReference: scv1beta1.PlanReference{
ClusterServiceClassExternalName: "jenkins-persistent",
ClusterServicePlanExternalName: "default",
},
},
Status: scv1beta1.ServiceInstanceStatus{
Conditions: []scv1beta1.ServiceInstanceCondition{
{
Reason: "ProvisionedSuccessfully",
},
},
},
},
},
},
},
wantErr: false,
actions: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fakeClient, fakeClientSet := FakeNew()
fakeClientSet.ServiceCatalogClientSet.PrependReactor("list", "serviceinstances", func(action ktesting.Action) (bool, runtime.Object, error) {
return true, &tt.args.serviceList, nil
})
// Run function ListServiceInstanceLabelValues
list, err := fakeClient.ListServiceInstanceLabelValues(applabels.ApplicationLabel, applabels.ApplicationLabel)
if err == nil && !tt.wantErr {
// Compare arrays
if !reflect.DeepEqual(list, tt.args.expectedOutput) {
t.Errorf("expected %s output, got %s", tt.args.expectedOutput, list)
}
if (len(fakeClientSet.ServiceCatalogClientSet.Actions()) != tt.actions) && !tt.wantErr {
t.Errorf("expected %v action(s) in ListServiceInstanceLabelValues got %v: %v", tt.actions, len(fakeClientSet.ServiceCatalogClientSet.Actions()), fakeClientSet.ServiceCatalogClientSet.Actions())
}
} else if err == nil && tt.wantErr {
t.Error("test failed, expected: false, got true")
} else if err != nil && !tt.wantErr {
t.Errorf("test failed, expected: no error, got error: %s", err.Error())
}
})
}
}

View File

@@ -22,7 +22,7 @@ func (c *Client) IsDeploymentConfigSupported() (bool, error) {
const Group = "apps.openshift.io"
const Version = "v1"
return c.isResourceSupported(Group, Version, "deploymentconfigs")
return c.GetKubeClient().IsResourceSupported(Group, Version, "deploymentconfigs")
}
// GetDeploymentConfigFromName returns the Deployment Config resource given

View File

@@ -92,7 +92,6 @@ func TestGetDeploymentConfigLabelValues(t *testing.T) {
return true, &tt.args.deploymentConfigList, nil
})
// Run function GetServiceInstanceLabelValues
list, err := fakeClient.GetDeploymentConfigLabelValues(applabels.ApplicationLabel, applabels.ApplicationLabel)
if err == nil && !tt.wantErr {
@@ -103,7 +102,7 @@ func TestGetDeploymentConfigLabelValues(t *testing.T) {
}
if (len(fakeClientSet.AppsClientset.Actions()) != tt.actions) && !tt.wantErr {
t.Errorf("expected %v action(s) in GetServiceInstanceLabelValues got %v: %v", tt.actions, len(fakeClientSet.AppsClientset.Actions()), fakeClientSet.AppsClientset.Actions())
t.Errorf("expected %v action(s) in TestGetDeploymentConfigLabelValues got %v: %v", tt.actions, len(fakeClientSet.AppsClientset.Actions()), fakeClientSet.AppsClientset.Actions())
}
} else if err == nil && tt.wantErr {

View File

@@ -1,6 +1,9 @@
package occlient
import (
"os"
"sync"
fakeServiceCatalogClientSet "github.com/kubernetes-sigs/service-catalog/pkg/client/clientset_generated/clientset/fake"
fakeAppsClientset "github.com/openshift/client-go/apps/clientset/versioned/fake"
fakeBuildClientset "github.com/openshift/client-go/build/clientset/versioned/fake"
@@ -13,8 +16,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery/fake"
fakeKubeClientset "k8s.io/client-go/kubernetes/fake"
"os"
"sync"
)
// FakeClientset holds fake ClientSets
@@ -40,6 +41,7 @@ func FakeNew() (*Client, *FakeClientset) {
kc, fkcs := kclient.FakeNew()
client.kubeClient = kc
fkclientset.Kubernetes = fkcs.Kubernetes
fkclientset.ServiceCatalogClientSet = fkcs.ServiceCatalogClientSet
fkclientset.AppsClientset = fakeAppsClientset.NewSimpleClientset()
client.appsClient = fkclientset.AppsClientset.AppsV1()
@@ -59,14 +61,11 @@ func FakeNew() (*Client, *FakeClientset) {
fkclientset.BuildClientset = fakeBuildClientset.NewSimpleClientset()
client.buildClient = fkclientset.BuildClientset.BuildV1()
fkclientset.ServiceCatalogClientSet = fakeServiceCatalogClientSet.NewSimpleClientset()
client.serviceCatalogClient = fkclientset.ServiceCatalogClientSet.ServicecatalogV1beta1()
if os.Getenv("KUBERNETES") != "true" {
client.SetDiscoveryInterface(fakeDiscoveryWithRoute)
client.SetDiscoveryInterface(fakeDiscoveryWithDeploymentConfig)
client.GetKubeClient().SetDiscoveryInterface(fakeDiscoveryWithRoute)
client.GetKubeClient().SetDiscoveryInterface(fakeDiscoveryWithDeploymentConfig)
} else {
client.SetDiscoveryInterface(&fakeDiscovery{
client.GetKubeClient().SetDiscoveryInterface(&fakeDiscovery{
resourceMap: map[string]*resourceMapEntry{},
})
}

View File

@@ -17,7 +17,7 @@ import (
// IsImageStreamSupported checks if imagestream resource type is present on the cluster
func (c *Client) IsImageStreamSupported() (bool, error) {
return c.isResourceSupported("image.openshift.io", "v1", "imagestreams")
return c.GetKubeClient().IsResourceSupported("image.openshift.io", "v1", "imagestreams")
}
// GetImageStream returns the imagestream using image details like imageNS, imageName and imageTag

View File

@@ -8,7 +8,6 @@ import (
"net"
"path/filepath"
"reflect"
"sort"
"strings"
"time"
@@ -22,7 +21,6 @@ import (
"github.com/openshift/odo/pkg/util"
// api clientsets
servicecatalogclienset "github.com/kubernetes-sigs/service-catalog/pkg/client/clientset_generated/clientset/typed/servicecatalog/v1beta1"
appsclientset "github.com/openshift/client-go/apps/clientset/versioned/typed/apps/v1"
buildclientset "github.com/openshift/client-go/build/clientset/versioned/typed/build/v1"
imageclientset "github.com/openshift/client-go/image/clientset/versioned/typed/image/v1"
@@ -31,19 +29,14 @@ import (
userclientset "github.com/openshift/client-go/user/clientset/versioned/typed/user/v1"
// api resource types
scv1beta1 "github.com/kubernetes-sigs/service-catalog/pkg/apis/servicecatalog/v1beta1"
appsv1 "github.com/openshift/api/apps/v1"
imagev1 "github.com/openshift/api/image/v1"
oauthv1client "github.com/openshift/client-go/oauth/clientset/versioned/typed/oauth/v1"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/version"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/remotecommand"
@@ -171,22 +164,15 @@ odo login https://mycluster.mydomain.com
`
type Client struct {
kubeClient *kclient.Client
imageClient imageclientset.ImageV1Interface
appsClient appsclientset.AppsV1Interface
buildClient buildclientset.BuildV1Interface
projectClient projectclientset.ProjectV1Interface
serviceCatalogClient servicecatalogclienset.ServicecatalogV1beta1Interface
routeClient routeclientset.RouteV1Interface
userClient userclientset.UserV1Interface
KubeConfig clientcmd.ClientConfig
discoveryClient discovery.DiscoveryInterface
Namespace string
supportedResources map[string]bool
}
func (c *Client) SetDiscoveryInterface(client discovery.DiscoveryInterface) {
c.discoveryClient = client
kubeClient *kclient.Client
imageClient imageclientset.ImageV1Interface
appsClient appsclientset.AppsV1Interface
buildClient buildclientset.BuildV1Interface
projectClient projectclientset.ProjectV1Interface
routeClient routeclientset.RouteV1Interface
userClient userclientset.UserV1Interface
KubeConfig clientcmd.ClientConfig
Namespace string
}
// New creates a new client
@@ -223,11 +209,6 @@ func New() (*Client, error) {
return nil, err
}
client.serviceCatalogClient, err = servicecatalogclienset.NewForConfig(config)
if err != nil {
return nil, err
}
client.projectClient, err = projectclientset.NewForConfig(config)
if err != nil {
return nil, err
@@ -243,11 +224,6 @@ func New() (*Client, error) {
return nil, err
}
client.discoveryClient, err = discovery.NewDiscoveryClientForConfig(config)
if err != nil {
return nil, err
}
client.Namespace, _, err = client.KubeConfig.Namespace()
if err != nil {
return nil, err
@@ -1140,195 +1116,6 @@ func (c *Client) WaitForComponentDeletion(selector string) error {
}
}
// DeleteServiceInstance takes labels as a input and based on it, deletes respective service instance
func (c *Client) DeleteServiceInstance(labels map[string]string) error {
klog.V(3).Infof("Deleting Service Instance")
// convert labels to selector
selector := util.ConvertLabelsToSelector(labels)
klog.V(3).Infof("Selectors used for deletion: %s", selector)
// Listing out serviceInstance because `DeleteCollection` method don't work on serviceInstance
serviceInstances, err := c.GetServiceInstanceList(selector)
if err != nil {
return errors.Wrap(err, "unable to list service instance")
}
// Iterating over serviceInstance List and deleting one by one
for _, serviceInstance := range serviceInstances {
// we need to delete the ServiceBinding before deleting the ServiceInstance
err = c.serviceCatalogClient.ServiceBindings(c.Namespace).Delete(serviceInstance.Name, &metav1.DeleteOptions{})
if err != nil {
return errors.Wrap(err, "unable to delete serviceBinding")
}
// now we perform the actual deletion
err = c.serviceCatalogClient.ServiceInstances(c.Namespace).Delete(serviceInstance.Name, &metav1.DeleteOptions{})
if err != nil {
return errors.Wrap(err, "unable to delete serviceInstance")
}
}
return nil
}
// GetServiceInstanceLabelValues get label values of given label from objects in project that match the selector
func (c *Client) GetServiceInstanceLabelValues(label string, selector string) ([]string, error) {
// List ServiceInstance according to given selectors
svcList, err := c.serviceCatalogClient.ServiceInstances(c.Namespace).List(metav1.ListOptions{LabelSelector: selector})
if err != nil {
return nil, errors.Wrap(err, "unable to list ServiceInstances")
}
// Grab all the matched strings
var values []string
for _, elem := range svcList.Items {
val, ok := elem.Labels[label]
if ok {
values = append(values, val)
}
}
// Sort alphabetically
sort.Strings(values)
return values, nil
}
// GetServiceInstanceList returns list service instances
func (c *Client) GetServiceInstanceList(selector string) ([]scv1beta1.ServiceInstance, error) {
// List ServiceInstance according to given selectors
svcList, err := c.serviceCatalogClient.ServiceInstances(c.Namespace).List(metav1.ListOptions{LabelSelector: selector})
if err != nil {
return nil, errors.Wrap(err, "unable to list ServiceInstances")
}
return svcList.Items, nil
}
// GetClusterServiceClasses queries the service service catalog to get available clusterServiceClasses
func (c *Client) GetClusterServiceClasses() ([]scv1beta1.ClusterServiceClass, error) {
classList, err := c.serviceCatalogClient.ClusterServiceClasses().List(metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "unable to list cluster service classes")
}
return classList.Items, nil
}
// GetClusterServiceClass returns the required service class from the service name
// serviceName is the name of the service
// returns the required service class and the error
func (c *Client) GetClusterServiceClass(serviceName string) (*scv1beta1.ClusterServiceClass, error) {
opts := metav1.ListOptions{
FieldSelector: fields.OneTermEqualSelector("spec.externalName", serviceName).String(),
}
searchResults, err := c.serviceCatalogClient.ClusterServiceClasses().List(opts)
if err != nil {
return nil, fmt.Errorf("unable to search classes by name (%s)", err)
}
if len(searchResults.Items) == 0 {
return nil, fmt.Errorf("class '%s' not found", serviceName)
}
if len(searchResults.Items) > 1 {
return nil, fmt.Errorf("more than one matching class found for '%s'", serviceName)
}
return &searchResults.Items[0], nil
}
// GetClusterPlansFromServiceName returns the plans associated with a service class
// serviceName is the name (the actual id, NOT the external name) of the service class whose plans are required
// returns array of ClusterServicePlans or error
func (c *Client) GetClusterPlansFromServiceName(serviceName string) ([]scv1beta1.ClusterServicePlan, error) {
opts := metav1.ListOptions{
FieldSelector: fields.OneTermEqualSelector("spec.clusterServiceClassRef.name", serviceName).String(),
}
searchResults, err := c.serviceCatalogClient.ClusterServicePlans().List(opts)
if err != nil {
return nil, fmt.Errorf("unable to search plans for service name '%s', (%s)", serviceName, err)
}
return searchResults.Items, nil
}
// CreateServiceInstance creates service instance from service catalog
func (c *Client) CreateServiceInstance(serviceName string, serviceType string, servicePlan string, parameters map[string]string, labels map[string]string) error {
serviceInstanceParameters, err := serviceInstanceParameters(parameters)
if err != nil {
return errors.Wrap(err, "unable to create the service instance parameters")
}
_, err = c.serviceCatalogClient.ServiceInstances(c.Namespace).Create(
&scv1beta1.ServiceInstance{
TypeMeta: metav1.TypeMeta{
Kind: "ServiceInstance",
APIVersion: "servicecatalog.k8s.io/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: serviceName,
Namespace: c.Namespace,
Labels: labels,
},
Spec: scv1beta1.ServiceInstanceSpec{
PlanReference: scv1beta1.PlanReference{
ClusterServiceClassExternalName: serviceType,
ClusterServicePlanExternalName: servicePlan,
},
Parameters: serviceInstanceParameters,
},
})
if err != nil {
return errors.Wrapf(err, "unable to create the service instance %s for the service type %s and plan %s", serviceName, serviceType, servicePlan)
}
// Create the secret containing the parameters of the plan selected.
err = c.CreateServiceBinding(serviceName, c.Namespace, labels)
if err != nil {
return errors.Wrapf(err, "unable to create the secret %s for the service instance", serviceName)
}
return nil
}
// CreateServiceBinding creates a ServiceBinding (essentially a secret) within the namespace of the
// service instance created using the service's parameters.
func (c *Client) CreateServiceBinding(bindingName string, namespace string, labels map[string]string) error {
_, err := c.serviceCatalogClient.ServiceBindings(namespace).Create(
&scv1beta1.ServiceBinding{
ObjectMeta: metav1.ObjectMeta{
Name: bindingName,
Namespace: namespace,
Labels: labels,
},
Spec: scv1beta1.ServiceBindingSpec{
InstanceRef: scv1beta1.LocalObjectReference{
Name: bindingName,
},
SecretName: bindingName,
},
})
if err != nil {
return errors.Wrap(err, "Creation of the secret failed")
}
return nil
}
// GetServiceBinding returns the ServiceBinding named serviceName in the namespace namespace
func (c *Client) GetServiceBinding(serviceName string, namespace string) (*scv1beta1.ServiceBinding, error) {
return c.serviceCatalogClient.ServiceBindings(namespace).Get(serviceName, metav1.GetOptions{})
}
// serviceInstanceParameters converts a map of variable assignments to a byte encoded json document,
// which is what the ServiceCatalog API consumes.
func serviceInstanceParameters(params map[string]string) (*runtime.RawExtension, error) {
paramsJSON, err := json.Marshal(params)
if err != nil {
return nil, err
}
return &runtime.RawExtension{Raw: paramsJSON}, nil
}
// LinkSecret links a secret to the DeploymentConfig of a component
func (c *Client) LinkSecret(secretName, componentName, applicationName string) error {
@@ -1377,52 +1164,6 @@ func (c *Client) UnlinkSecret(secretName, componentName, applicationName string)
return c.patchDC(dcName, dcPatchProvider)
}
// GetServiceClassesByCategory retrieves a map associating category name to ClusterServiceClasses matching the category
func (c *Client) GetServiceClassesByCategory() (categories map[string][]scv1beta1.ClusterServiceClass, err error) {
categories = make(map[string][]scv1beta1.ClusterServiceClass)
classes, err := c.GetClusterServiceClasses()
if err != nil {
return nil, err
}
// TODO: Should we replicate the classification performed in
// https://github.com/openshift/console/blob/master/frontend/public/components/catalog/catalog-items.jsx?
for _, class := range classes {
tags := class.Spec.Tags
category := "other"
if len(tags) > 0 && len(tags[0]) > 0 {
category = tags[0]
}
categories[category] = append(categories[category], class)
}
return categories, err
}
// GetMatchingPlans retrieves a map associating service plan name to service plan instance associated with the specified service
// class
func (c *Client) GetMatchingPlans(class scv1beta1.ClusterServiceClass) (plans map[string]scv1beta1.ClusterServicePlan, err error) {
planList, err := c.serviceCatalogClient.ClusterServicePlans().List(metav1.ListOptions{
FieldSelector: "spec.clusterServiceClassRef.name==" + class.Spec.ExternalID,
})
plans = make(map[string]scv1beta1.ClusterServicePlan)
for _, v := range planList.Items {
plans[v.Spec.ExternalName] = v
}
return plans, err
}
// GetAllClusterServicePlans returns list of available plans
func (c *Client) GetAllClusterServicePlans() ([]scv1beta1.ClusterServicePlan, error) {
planList, err := c.serviceCatalogClient.ClusterServicePlans().List(metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "unable to get cluster service plan")
}
return planList.Items, nil
}
// ServerInfo contains the fields that contain the server's information like
// address, OpenShift and Kubernetes versions
type ServerInfo struct {
@@ -1651,16 +1392,6 @@ func isSubDir(baseDir, otherDir string) bool {
return matches
}
// IsSBRSupported checks if resource of type service binding request present on the cluster
func (c *Client) IsSBRSupported() (bool, error) {
return c.isResourceSupported("apps.openshift.io", "v1alpha1", "servicebindingrequests")
}
// IsCSVSupported checks if resource of type service binding request present on the cluster
func (c *Client) IsCSVSupported() (bool, error) {
return c.isResourceSupported("operators.coreos.com", "v1alpha1", "clusterserviceversions")
}
// GenerateOwnerReference generates an ownerReference which can then be set as
// owner for various OpenShift objects and ensure that when the owner object is
// deleted from the cluster, all other objects are automatically removed by
@@ -1676,32 +1407,3 @@ func GenerateOwnerReference(dc *appsv1.DeploymentConfig) metav1.OwnerReference {
return ownerReference
}
func (c *Client) isResourceSupported(apiGroup, apiVersion, resourceName string) (bool, error) {
if c.supportedResources == nil {
c.supportedResources = make(map[string]bool, 7)
}
groupVersion := metav1.GroupVersion{Group: apiGroup, Version: apiVersion}.String()
supported, found := c.supportedResources[groupVersion]
if !found {
list, err := c.discoveryClient.ServerResourcesForGroupVersion(groupVersion)
if err != nil {
if kerrors.IsNotFound(err) {
supported = false
} else {
// don't record, just attempt again next time in case it's a transient error
return false, err
}
} else {
for _, resources := range list.APIResources {
if resources.Name == resourceName {
supported = true
break
}
}
}
c.supportedResources[groupVersion] = supported
}
return supported, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -208,7 +208,7 @@ func (c *Client) CreateNewProject(projectName string, wait bool) error {
// IsProjectSupported checks if Project resource type is present on the cluster
func (c *Client) IsProjectSupported() (bool, error) {
return c.isResourceSupported("project.openshift.io", "v1", "projects")
return c.GetKubeClient().IsResourceSupported("project.openshift.io", "v1", "projects")
}
// GetCurrentProjectName returns the current project name

View File

@@ -12,7 +12,7 @@ import (
// IsRouteSupported checks if route resource type is present on the cluster
func (c *Client) IsRouteSupported() (bool, error) {
return c.isResourceSupported("route.openshift.io", "v1", "routes")
return c.GetKubeClient().IsResourceSupported("route.openshift.io", "v1", "routes")
}
// GetRoute gets the route with the given name

View File

@@ -84,7 +84,7 @@ func (o *commonLinkOptions) complete(name string, cmd *cobra.Command, args []str
}
if o.csvSupport && o.Context.EnvSpecificInfo != nil {
sboSupport, err := o.Client.IsSBRSupported()
sboSupport, err := o.Client.GetKubeClient().IsSBRSupported()
if err != nil {
return err
}
@@ -248,7 +248,7 @@ func (o *commonLinkOptions) validate(wait bool) (err error) {
if o.isTargetAService {
// if there is a ServiceBinding, then that means there is already a secret (or there will be soon)
// which we can link to
_, err = o.Client.GetServiceBinding(o.secretName, o.Project)
_, err = o.Client.GetKubeClient().GetServiceBinding(o.secretName, o.Project)
if err != nil {
return fmt.Errorf("The service was not created via odo. Please delete the service and recreate it using 'odo service create %s'", o.secretName)
}

View File

@@ -102,7 +102,7 @@ func (o *LinkOptions) Complete(name string, cmd *cobra.Command, args []string) (
return err
}
o.csvSupport, err = o.Client.IsCSVSupported()
o.csvSupport, err = o.Client.GetKubeClient().IsCSVSupported()
if err != nil {
return err
}

View File

@@ -60,7 +60,7 @@ func (o *UnlinkOptions) Complete(name string, cmd *cobra.Command, args []string)
return err
}
o.csvSupport, err = o.Client.IsCSVSupported()
o.csvSupport, err = o.Client.GetKubeClient().IsCSVSupported()
if err != nil {
return err
}

View File

@@ -166,7 +166,7 @@ func (o *ServiceCreateOptions) Complete(name string, cmd *cobra.Command, args []
}
if o.interactive {
classesByCategory, err := client.GetServiceClassesByCategory()
classesByCategory, err := client.GetKubeClient().ListServiceClassesByCategory()
if err != nil {
return fmt.Errorf("unable to retrieve service classes: %v", err)
}
@@ -177,7 +177,7 @@ func (o *ServiceCreateOptions) Complete(name string, cmd *cobra.Command, args []
class, o.ServiceType = ui.SelectClassInteractively(classesByCategory)
plans, err := client.GetMatchingPlans(class)
plans, err := client.GetKubeClient().ListMatchingPlans(class)
if err != nil {
return fmt.Errorf("couldn't retrieve plans for class %s: %v", class.GetExternalName(), err)
}
@@ -398,7 +398,7 @@ func (o *ServiceCreateOptions) Validate() (err error) {
}
}
// make sure the service type exists
classPtr, err := o.Client.GetClusterServiceClass(o.ServiceType)
classPtr, err := o.Client.GetKubeClient().GetClusterServiceClass(o.ServiceType)
if err != nil {
return errors.Wrap(err, "unable to create service because Service Catalog is not enabled in your cluster")
}
@@ -407,7 +407,7 @@ func (o *ServiceCreateOptions) Validate() (err error) {
}
// check plan
plans, err := o.Client.GetMatchingPlans(*classPtr)
plans, err := o.Client.GetKubeClient().ListMatchingPlans(*classPtr)
if err != nil {
return err
}

View File

@@ -39,7 +39,7 @@ var ServiceCompletionHandler = func(cmd *cobra.Command, args parsedArgs, context
// ServiceClassCompletionHandler provides catalog service class name completion
var ServiceClassCompletionHandler = func(cmd *cobra.Command, args parsedArgs, context *genericclioptions.Context) (completions []string) {
completions = make([]string, 0)
services, err := context.Client.GetClusterServiceClasses()
services, err := context.Client.GetKubeClient().ListClusterServiceClasses()
if err != nil {
complete.Log("error retrieving services")
return completions
@@ -70,13 +70,13 @@ var ServicePlanCompletionHandler = func(cmd *cobra.Command, args parsedArgs, con
complete.Log(fmt.Sprintf("Using input: serviceName = %s", inputServiceName))
clusterServiceClass, err := context.Client.GetClusterServiceClass(inputServiceName)
clusterServiceClass, err := context.Client.GetKubeClient().GetClusterServiceClass(inputServiceName)
if err != nil {
complete.Log("Error retrieving details of service")
return completions
}
servicePlans, err := context.Client.GetClusterPlansFromServiceName(clusterServiceClass.Name)
servicePlans, err := context.Client.GetKubeClient().ListClusterServicePlansByServiceName(clusterServiceClass.Name)
if err != nil {
complete.Log("Error retrieving details of plans of service")
return completions

View File

@@ -269,7 +269,7 @@ func TestCreate(t *testing.T) {
// The function we are testing
context := genericclioptions.NewFakeContext(tt.projectName, "app", "cmp", client, kubeClient)
context.Client.SetDiscoveryInterface(fakeDiscoveryWithProject)
context.Client.GetKubeClient().SetDiscoveryInterface(fakeDiscoveryWithProject)
err := Create(context, tt.projectName, true)
@@ -294,7 +294,7 @@ func TestCreate(t *testing.T) {
// The function we are testing
context := genericclioptions.NewFakeContext(tt.projectName, "app", "cmp", client, kclient)
context.Client.SetDiscoveryInterface(fakeDiscoveryWithNamespace)
context.Client.GetKubeClient().SetDiscoveryInterface(fakeDiscoveryWithNamespace)
err := Create(context, tt.projectName, true)
@@ -362,7 +362,7 @@ func TestDelete(t *testing.T) {
kclient, _ := fakeDeleteKClient(tt.projectName, tt.deleteLast)
context := genericclioptions.NewFakeContext(tt.projectName, "app", "cmp", client, kclient)
context.Client.SetDiscoveryInterface(fakeDiscoveryWithProject)
context.Client.GetKubeClient().SetDiscoveryInterface(fakeDiscoveryWithProject)
// The function we are testing
err := Delete(context, tt.projectName, tt.wait)
@@ -388,7 +388,7 @@ func TestDelete(t *testing.T) {
kubeClient, fakeKClientSet := fakeDeleteKClient(tt.projectName, tt.deleteLast)
context := genericclioptions.NewFakeContext(tt.projectName, "app", "cmp", client, kubeClient)
context.Client.SetDiscoveryInterface(fakeDiscoveryWithNamespace)
context.Client.GetKubeClient().SetDiscoveryInterface(fakeDiscoveryWithNamespace)
// The function we are testing
err := Delete(context, tt.projectName, tt.wait)
@@ -478,7 +478,7 @@ func TestList(t *testing.T) {
kubeClient, _ := kclient.FakeNew()
client.SetDiscoveryInterface(fakeDiscoveryWithProject)
client.GetKubeClient().SetDiscoveryInterface(fakeDiscoveryWithProject)
context := genericclioptions.NewFakeContext("test", "app", "cmp", client, kubeClient)

View File

@@ -57,7 +57,7 @@ func CreateService(client *occlient.Client, serviceName string, serviceType stri
labels := componentlabels.GetLabels(serviceName, applicationName, true)
// save service type as label
labels[componentlabels.ComponentTypeLabel] = serviceType
err := client.CreateServiceInstance(serviceName, serviceType, servicePlan, parameters, labels)
err := client.GetKubeClient().CreateServiceInstance(serviceName, serviceType, servicePlan, parameters, labels)
if err != nil {
return errors.Wrap(err, "unable to create service instance")
@@ -70,7 +70,7 @@ func CreateService(client *occlient.Client, serviceName string, serviceType stri
// able to find them, an error otherwise.
func GetCSV(client *kclient.Client, crd map[string]interface{}) (string, olm.ClusterServiceVersion, error) {
cr := crd["kind"].(string)
csvs, err := client.GetClusterServiceVersionList()
csvs, err := client.ListClusterServiceVersions()
if err != nil {
return cr, olm.ClusterServiceVersion{}, err
}
@@ -110,7 +110,7 @@ func CreateOperatorService(client *kclient.Client, group, version, resource stri
func DeleteServiceAndUnlinkComponents(client *occlient.Client, serviceName string, applicationName string) error {
// first we attempt to delete the service instance itself
labels := componentlabels.GetLabels(serviceName, applicationName, false)
err := client.DeleteServiceInstance(labels)
err := client.GetKubeClient().DeleteServiceInstance(labels)
if err != nil {
return err
}
@@ -195,7 +195,7 @@ func List(client *occlient.Client, applicationName string) (ServiceList, error)
applicationSelector := util.ConvertLabelsToSelector(labels)
// get service instance list based on given selector
serviceInstanceList, err := client.GetServiceInstanceList(applicationSelector)
serviceInstanceList, err := client.GetKubeClient().ListServiceInstances(applicationSelector)
if err != nil {
return ServiceList{}, errors.Wrapf(err, "unable to list services")
}
@@ -301,7 +301,7 @@ func ListOperatorServices(client *kclient.Client) ([]unstructured.Unstructured,
klog.V(4).Info("Getting list of services")
// First let's get the list of all the operators in the namespace
csvs, err := client.GetClusterServiceVersionList()
csvs, err := client.ListClusterServiceVersions()
if err != nil {
return nil, errors.Wrap(err, "Unable to list operator backed services")
}
@@ -577,7 +577,7 @@ func SplitServiceKindName(serviceName string) (string, string, error) {
// the first parameter returned is the ServiceClass object
// the second parameter returned is the array of ServicePlan associated with the service class
func GetServiceClassAndPlans(client *occlient.Client, serviceName string) (ServiceClass, []ServicePlan, error) {
result, err := client.GetClusterServiceClass(serviceName)
result, err := client.GetKubeClient().GetClusterServiceClass(serviceName)
if err != nil {
return ServiceClass{}, nil, errors.Wrap(err, "unable to get the given service")
}
@@ -608,7 +608,7 @@ func GetServiceClassAndPlans(client *occlient.Client, serviceName string) (Servi
}
// get the plans according to the service name
planResults, err := client.GetClusterPlansFromServiceName(result.Name)
planResults, err := client.GetKubeClient().ListClusterServicePlansByServiceName(result.Name)
if err != nil {
return ServiceClass{}, nil, errors.Wrap(err, "unable to get plans for the given service")
}
@@ -683,10 +683,10 @@ func isRequired(required []string, name string) bool {
// IsCSVSupported checks if the cluster supports resources of type ClusterServiceVersion
func IsCSVSupported() (bool, error) {
oclient, err := occlient.New()
occlient, err := occlient.New()
if err != nil {
return false, err
}
return oclient.IsCSVSupported()
return occlient.GetKubeClient().IsCSVSupported()
}

View File

@@ -3,7 +3,6 @@ package service
import (
"encoding/json"
"fmt"
"github.com/kylelemons/godebug/pretty"
"github.com/onsi/gomega/matchers"
"github.com/openshift/odo/pkg/testingutil"

View File

@@ -163,7 +163,8 @@ func TestGetURLsForKubernetes(t *testing.T) {
// Initializing the fake occlient
fkoclient, fakeoclientSet := occlient.FakeNew()
fkoclient.Namespace = "default"
fkoclient.SetDiscoveryInterface(fakeDiscoveryWithProject)
fkoclient.SetKubeClient(fkclient)
fkoclient.GetKubeClient().SetDiscoveryInterface(fakeDiscoveryWithProject)
fkoclient.SetKubeClient(fkclient)
// Return the test's route list when requested