...

Source file src/github.com/redhat-developer/odo/pkg/binding/binding_test.go

Documentation: github.com/redhat-developer/odo/pkg/binding

     1  package binding
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
     8  	"github.com/devfile/library/v2/pkg/devfile/parser"
     9  	devfileCtx "github.com/devfile/library/v2/pkg/devfile/parser/context"
    10  	"github.com/devfile/library/v2/pkg/testingutil/filesystem"
    11  	"github.com/golang/mock/gomock"
    12  	"github.com/google/go-cmp/cmp"
    13  	"github.com/google/go-cmp/cmp/cmpopts"
    14  	appsv1 "k8s.io/api/apps/v1"
    15  	"k8s.io/apimachinery/pkg/api/meta"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    18  	"k8s.io/apimachinery/pkg/runtime/schema"
    19  
    20  	servicebinding "github.com/redhat-developer/service-binding-operator/apis/binding/v1alpha1"
    21  
    22  	"github.com/redhat-developer/odo/pkg/kclient"
    23  	odoTestingUtil "github.com/redhat-developer/odo/pkg/testingutil"
    24  )
    25  
    26  const deploymentKind = "Deployment"
    27  
    28  var deploymentGVK = appsv1.SchemeGroupVersion.WithKind(deploymentKind)
    29  var deploymentApiVersion, _ = deploymentGVK.ToAPIVersionAndKind()
    30  
    31  const clusterKind = "Cluster"
    32  
    33  var clusterGV = schema.GroupVersion{
    34  	Group:   "postgresql.k8s.enterprisedb.io",
    35  	Version: "v1",
    36  }
    37  var clusterGVK = clusterGV.WithKind(clusterKind)
    38  var clusterGVR = clusterGV.WithResource("clusters")
    39  var ClusterAPIVersion, _ = clusterGVK.ToAPIVersionAndKind()
    40  
    41  func TestBindingClient_GetFlags(t *testing.T) {
    42  	type args struct {
    43  		flags map[string]string
    44  	}
    45  	tests := []struct {
    46  		name string
    47  		args args
    48  		want map[string]string
    49  	}{
    50  		{
    51  			name: "service and name flags are present",
    52  			args: args{flags: map[string]string{"service": "redisService", "name": "mybinding", "v": "9"}},
    53  			want: map[string]string{"service": "redisService", "name": "mybinding"},
    54  		},
    55  		{
    56  			name: "only one flag is present",
    57  			args: args{map[string]string{"service": "redisService", "v": "9"}},
    58  			want: map[string]string{"service": "redisService"},
    59  		},
    60  		{
    61  			name: "no relevant flags are present",
    62  			args: args{map[string]string{"v": "9"}},
    63  			want: map[string]string{},
    64  		},
    65  	}
    66  	for _, tt := range tests {
    67  		t.Run(tt.name, func(t *testing.T) {
    68  			o := &BindingClient{}
    69  			got := o.GetFlags(tt.args.flags)
    70  			if diff := cmp.Diff(tt.want, got); diff != "" {
    71  				t.Errorf("BindingClient.GetFlags() mismatch (-want +got):\n%s", diff)
    72  			}
    73  		})
    74  	}
    75  }
    76  
    77  func TestBindingClient_GetServiceInstances(t *testing.T) {
    78  	ns := "my-ns"
    79  
    80  	var clusterUnstructured unstructured.Unstructured
    81  
    82  	clusterUnstructured.SetGroupVersionKind(clusterGVK)
    83  	clusterUnstructured.SetName("postgres-cluster")
    84  
    85  	serviceBindingInstance := servicebinding.BindableKinds{
    86  		TypeMeta: metav1.TypeMeta{
    87  			Kind:       "BindableKinds",
    88  			APIVersion: "v1alpha1",
    89  		},
    90  		ObjectMeta: metav1.ObjectMeta{
    91  			Name: "bindable-kinds",
    92  		},
    93  		Status: []servicebinding.BindableKindsStatus{
    94  			{
    95  				Group:   "redis.redis.opstreelabs.in",
    96  				Kind:    "Redis",
    97  				Version: "v1beta1",
    98  			},
    99  			{
   100  				Group:   "postgresql.k8s.enterprisedb.io",
   101  				Kind:    "Cluster",
   102  				Version: "v1",
   103  			},
   104  		},
   105  	}
   106  	type fields struct {
   107  		kubernetesClient func(ctrl *gomock.Controller) kclient.ClientInterface
   108  	}
   109  	type args struct {
   110  		namespace string
   111  	}
   112  	tests := []struct {
   113  		name    string
   114  		fields  fields
   115  		args    args
   116  		want    map[string]unstructured.Unstructured
   117  		wantErr bool
   118  	}{
   119  		{
   120  			name: "obtained service instances",
   121  			fields: fields{
   122  				kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
   123  					client := kclient.NewMockClientInterface(ctrl)
   124  					client.EXPECT().IsServiceBindingSupported().Return(true, nil)
   125  					client.EXPECT().GetBindableKinds().Return(serviceBindingInstance, nil)
   126  					client.EXPECT().GetBindableKindStatusRestMapping(serviceBindingInstance.Status).Return([]*meta.RESTMapping{
   127  						{Resource: clusterGVR, GroupVersionKind: clusterGVK},
   128  					}, nil)
   129  
   130  					client.EXPECT().ListDynamicResources("", clusterGVR, "").Return(&unstructured.UnstructuredList{Items: []unstructured.Unstructured{clusterUnstructured}}, nil)
   131  					return client
   132  				},
   133  			},
   134  			want: map[string]unstructured.Unstructured{
   135  				"postgres-cluster (Cluster.postgresql.k8s.enterprisedb.io)": clusterUnstructured,
   136  			},
   137  			wantErr: false,
   138  		},
   139  		{
   140  			name: "obtained service instances from specific namespace",
   141  			fields: fields{
   142  				kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
   143  					client := kclient.NewMockClientInterface(ctrl)
   144  					client.EXPECT().IsServiceBindingSupported().Return(true, nil)
   145  					client.EXPECT().GetBindableKinds().Return(serviceBindingInstance, nil)
   146  					client.EXPECT().GetBindableKindStatusRestMapping(serviceBindingInstance.Status).Return([]*meta.RESTMapping{
   147  						{Resource: clusterGVR, GroupVersionKind: clusterGVK},
   148  					}, nil)
   149  
   150  					client.EXPECT().ListDynamicResources(ns, clusterGVR, "").Return(&unstructured.UnstructuredList{Items: []unstructured.Unstructured{clusterUnstructured}}, nil)
   151  					return client
   152  				},
   153  			},
   154  			args: args{
   155  				namespace: ns,
   156  			},
   157  			want: map[string]unstructured.Unstructured{
   158  				"postgres-cluster (Cluster.postgresql.k8s.enterprisedb.io)": clusterUnstructured,
   159  			},
   160  			wantErr: false,
   161  		},
   162  		{
   163  			name: "do not fail if no bindable kind service was found",
   164  			fields: fields{
   165  				kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
   166  					client := kclient.NewMockClientInterface(ctrl)
   167  					client.EXPECT().IsServiceBindingSupported().Return(true, nil)
   168  					client.EXPECT().GetBindableKinds().Return(serviceBindingInstance, nil)
   169  					client.EXPECT().GetBindableKindStatusRestMapping(serviceBindingInstance.Status).Return(nil, nil)
   170  					return client
   171  				},
   172  			},
   173  			want:    map[string]unstructured.Unstructured{},
   174  			wantErr: false,
   175  		},
   176  		{
   177  			name: "do not fail if no instances of the bindable kind services was found",
   178  			fields: fields{kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
   179  				client := kclient.NewMockClientInterface(ctrl)
   180  				client.EXPECT().IsServiceBindingSupported().Return(true, nil)
   181  				client.EXPECT().GetBindableKinds().Return(serviceBindingInstance, nil)
   182  				client.EXPECT().GetBindableKindStatusRestMapping(serviceBindingInstance.Status).Return([]*meta.RESTMapping{
   183  					{Resource: clusterGVR, GroupVersionKind: clusterGVK},
   184  				}, nil)
   185  
   186  				client.EXPECT().ListDynamicResources("", clusterGVR, "").Return(&unstructured.UnstructuredList{Items: nil}, nil)
   187  				return client
   188  			}},
   189  			want:    map[string]unstructured.Unstructured{},
   190  			wantErr: false,
   191  		},
   192  		{
   193  			name: "error out if the servicebinding CRD is not found",
   194  			fields: fields{kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
   195  				client := kclient.NewMockClientInterface(ctrl)
   196  				client.EXPECT().IsServiceBindingSupported().Return(false, nil)
   197  				return client
   198  			}},
   199  			want:    nil,
   200  			wantErr: true,
   201  		},
   202  	}
   203  	for _, tt := range tests {
   204  		t.Run(tt.name, func(t *testing.T) {
   205  			ctrl := gomock.NewController(t)
   206  			o := &BindingClient{
   207  				kubernetesClient: tt.fields.kubernetesClient(ctrl),
   208  			}
   209  			got, err := o.GetServiceInstances(tt.args.namespace)
   210  			if (err != nil) != tt.wantErr {
   211  				t.Errorf("GetServiceInstances() error = %v, wantErr %v", err, tt.wantErr)
   212  				return
   213  			}
   214  			if diff := cmp.Diff(tt.want, got); diff != "" {
   215  				t.Errorf("BindingClient.GetServiceInstances() mismatch (-want +got):\n%s", diff)
   216  			}
   217  		})
   218  	}
   219  }
   220  
   221  func TestBindingClient_AddBindingToDevfile(t *testing.T) {
   222  	bindingName := "my-nodejs-app-cluster-sample"
   223  	ns := "my-ns"
   224  
   225  	var clusterUnstructured unstructured.Unstructured
   226  	clusterUnstructured.SetGroupVersionKind(clusterGVK)
   227  	clusterUnstructured.SetName("cluster-sample")
   228  
   229  	serviceBindingRef := servicebinding.Service{
   230  		Id: &bindingName,
   231  		NamespacedRef: servicebinding.NamespacedRef{
   232  			Ref: servicebinding.Ref{
   233  				Group:    clusterGVK.Group,
   234  				Version:  clusterGVK.Version,
   235  				Kind:     clusterGVK.Kind,
   236  				Name:     clusterUnstructured.GetName(),
   237  				Resource: "clusters",
   238  			},
   239  		},
   240  	}
   241  
   242  	serviceBindingRefWithNs := servicebinding.Service{
   243  		Id: &bindingName,
   244  		NamespacedRef: servicebinding.NamespacedRef{
   245  			Ref: servicebinding.Ref{
   246  				Group:    clusterGVK.Group,
   247  				Version:  clusterGVK.Version,
   248  				Kind:     clusterGVK.Kind,
   249  				Name:     clusterUnstructured.GetName(),
   250  				Resource: "clusters",
   251  			},
   252  			Namespace: &ns,
   253  		},
   254  	}
   255  
   256  	type fields struct {
   257  		kubernetesClient func(ctrl *gomock.Controller) kclient.ClientInterface
   258  	}
   259  	type args struct {
   260  		bindingName         string
   261  		bindAsFiles         bool
   262  		namingStrategy      string
   263  		namespace           string
   264  		unstructuredService unstructured.Unstructured
   265  		obj                 parser.DevfileObj
   266  	}
   267  	tests := []struct {
   268  		name    string
   269  		fields  fields
   270  		args    args
   271  		want    *parser.DevfileObj
   272  		wantErr bool
   273  	}{
   274  		{
   275  			name: "successfully add binding",
   276  			fields: fields{
   277  				kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
   278  					client := kclient.NewMockClientInterface(ctrl)
   279  					client.EXPECT().NewServiceBindingServiceObject("", clusterUnstructured, bindingName).Return(serviceBindingRef, nil)
   280  					client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
   281  					return client
   282  				},
   283  			},
   284  			args: args{
   285  				bindingName:         bindingName,
   286  				bindAsFiles:         false,
   287  				unstructuredService: clusterUnstructured,
   288  				obj:                 odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
   289  			},
   290  			want:    getDevfileObjWithServiceBinding(bindingName, "", false, ""),
   291  			wantErr: false,
   292  		},
   293  		{
   294  			name: "successfully add binding with namespace",
   295  			fields: fields{
   296  				kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
   297  					client := kclient.NewMockClientInterface(ctrl)
   298  					client.EXPECT().NewServiceBindingServiceObject(ns, clusterUnstructured, bindingName).Return(serviceBindingRefWithNs, nil)
   299  					client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
   300  					return client
   301  				},
   302  			},
   303  			args: args{
   304  				bindingName:         bindingName,
   305  				bindAsFiles:         false,
   306  				namespace:           ns,
   307  				unstructuredService: clusterUnstructured,
   308  				obj:                 odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
   309  			},
   310  			want:    getDevfileObjWithServiceBinding(bindingName, ns, false, ""),
   311  			wantErr: false,
   312  		},
   313  		{
   314  			name: "successfully added binding for a Service Binding bound as files",
   315  			fields: fields{
   316  				kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
   317  					client := kclient.NewMockClientInterface(ctrl)
   318  					client.EXPECT().NewServiceBindingServiceObject("", clusterUnstructured, bindingName).Return(serviceBindingRef, nil)
   319  					client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
   320  					return client
   321  				},
   322  			},
   323  			args: args{
   324  				bindingName:         bindingName,
   325  				bindAsFiles:         true,
   326  				unstructuredService: clusterUnstructured,
   327  				obj:                 odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
   328  			},
   329  			want:    getDevfileObjWithServiceBinding(bindingName, "", true, ""),
   330  			wantErr: false,
   331  		},
   332  		{
   333  			name: "successfully added binding for a Service Binding bound as files and namespace",
   334  			fields: fields{
   335  				kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
   336  					client := kclient.NewMockClientInterface(ctrl)
   337  					client.EXPECT().NewServiceBindingServiceObject(ns, clusterUnstructured, bindingName).Return(serviceBindingRefWithNs, nil)
   338  					client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
   339  					return client
   340  				},
   341  			},
   342  			args: args{
   343  				bindingName:         bindingName,
   344  				bindAsFiles:         true,
   345  				namespace:           ns,
   346  				unstructuredService: clusterUnstructured,
   347  				obj:                 odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
   348  			},
   349  			want:    getDevfileObjWithServiceBinding(bindingName, ns, true, ""),
   350  			wantErr: false,
   351  		},
   352  		{
   353  			name: "successfully added binding for a Service Binding with naming strategy",
   354  			fields: fields{
   355  				kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
   356  					client := kclient.NewMockClientInterface(ctrl)
   357  					client.EXPECT().NewServiceBindingServiceObject("", clusterUnstructured, bindingName).Return(serviceBindingRef, nil)
   358  					client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
   359  					return client
   360  				},
   361  			},
   362  			args: args{
   363  				bindingName:         bindingName,
   364  				bindAsFiles:         true,
   365  				namingStrategy:      "uppercase",
   366  				unstructuredService: clusterUnstructured,
   367  				obj:                 odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
   368  			},
   369  			want:    getDevfileObjWithServiceBinding(bindingName, "", true, "uppercase"),
   370  			wantErr: false,
   371  		},
   372  		{
   373  			name: "successfully added binding for a Service Binding with naming strategy and namespace",
   374  			fields: fields{
   375  				kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
   376  					client := kclient.NewMockClientInterface(ctrl)
   377  					client.EXPECT().NewServiceBindingServiceObject(ns, clusterUnstructured, bindingName).Return(serviceBindingRefWithNs, nil)
   378  					client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
   379  					return client
   380  				},
   381  			},
   382  			args: args{
   383  				bindingName:         bindingName,
   384  				bindAsFiles:         true,
   385  				namespace:           ns,
   386  				namingStrategy:      "uppercase",
   387  				unstructuredService: clusterUnstructured,
   388  				obj:                 odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
   389  			},
   390  			want:    getDevfileObjWithServiceBinding(bindingName, ns, true, "uppercase"),
   391  			wantErr: false,
   392  		},
   393  	}
   394  	for _, tt := range tests {
   395  		t.Run(tt.name, func(t *testing.T) {
   396  			ctrl := gomock.NewController(t)
   397  			o := &BindingClient{
   398  				kubernetesClient: tt.fields.kubernetesClient(ctrl),
   399  			}
   400  			got, err := o.AddBindingToDevfile(
   401  				tt.args.obj.GetMetadataName(),
   402  				tt.args.bindingName,
   403  				tt.args.bindAsFiles,
   404  				tt.args.namespace,
   405  				tt.args.namingStrategy,
   406  				tt.args.unstructuredService,
   407  				tt.args.obj,
   408  			)
   409  			if (err != nil) != tt.wantErr {
   410  				t.Errorf("AddBindingToDevfile() error = %v, wantErr %v", err, tt.wantErr)
   411  				return
   412  			}
   413  			if diff := cmp.Diff(*tt.want, got,
   414  				cmp.AllowUnexported(devfileCtx.DevfileCtx{}),
   415  				cmpopts.IgnoreInterfaces(struct{ filesystem.Filesystem }{})); diff != "" {
   416  				t.Errorf("BindingClient.AddBindingToDevfile() mismatch (-want +got):\n%s", diff)
   417  			}
   418  		})
   419  	}
   420  }
   421  
   422  func getDevfileObjWithServiceBinding(bindingName string, ns string, bindAsFiles bool, namingStrategy string) *parser.DevfileObj {
   423  	obj := odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs())
   424  	_ = obj.Data.AddComponents([]v1alpha2.Component{{
   425  		Name: bindingName,
   426  		ComponentUnion: v1alpha2.ComponentUnion{
   427  			Kubernetes: &v1alpha2.KubernetesComponent{
   428  				K8sLikeComponent: v1alpha2.K8sLikeComponent{
   429  					BaseComponent: v1alpha2.BaseComponent{},
   430  					K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
   431  						Inlined: getServiceBindingInlinedContent(ns, bindAsFiles, namingStrategy),
   432  					},
   433  				},
   434  			},
   435  		},
   436  	}})
   437  	return &obj
   438  }
   439  
   440  func getServiceBindingInlinedContent(ns string, bindAsFiles bool, namingStrategy string) string {
   441  	h := fmt.Sprintf(`apiVersion: binding.operators.coreos.com/v1alpha1
   442  kind: ServiceBinding
   443  metadata:
   444    creationTimestamp: null
   445    name: my-nodejs-app-cluster-sample
   446  spec:
   447    application:
   448      group: apps
   449      kind: Deployment
   450      name: my-nodejs-app-app
   451      version: v1
   452    bindAsFiles: %v
   453    detectBindingResources: true`, bindAsFiles)
   454  
   455  	fNoNamespace := `
   456    services:
   457    - group: postgresql.k8s.enterprisedb.io
   458      id: my-nodejs-app-cluster-sample
   459      kind: Cluster
   460      name: cluster-sample
   461      resource: clusters
   462      version: v1
   463  status:
   464    secret: ""
   465  `
   466  
   467  	fNamespace := fmt.Sprintf(`
   468    services:
   469    - group: postgresql.k8s.enterprisedb.io
   470      id: my-nodejs-app-cluster-sample
   471      kind: Cluster
   472      name: cluster-sample
   473      namespace: %s
   474      resource: clusters
   475      version: v1
   476  status:
   477    secret: ""
   478  `, ns)
   479  
   480  	if namingStrategy != "" {
   481  		if ns != "" {
   482  			return fmt.Sprintf(`%s
   483    namingStrategy: %s%s`, h, namingStrategy, fNamespace)
   484  		}
   485  		return fmt.Sprintf(`%s
   486    namingStrategy: %s%s`, h, namingStrategy, fNoNamespace)
   487  	}
   488  
   489  	if ns != "" {
   490  		return h + fNamespace
   491  	}
   492  
   493  	return h + fNoNamespace
   494  }
   495  

View as plain text