Files
odo/pkg/binding/binding_test.go
Parthvi Vala 19f8e0ebdb Update devfile/library to support pod-overrides and container-overrides attributes and add integration test for it (#6512)
* Update the Devfile library

* Add test for container-overrides and pod-overrides attributes

Signed-off-by: Parthvi Vala <pvala@redhat.com>

Attempt at fixing CI failures

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Fix CI failure

* Attempt at fixing OC integration test failures

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Use random name in integration test

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Rebase and fix integration test failure

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Make integration test work for podman

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Temp attempt at fixing podman test for GH

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* Another attempt at fixing CI test for podman

Signed-off-by: Parthvi Vala <pvala@redhat.com>

* One more attempt at fixing integration test for podman

Signed-off-by: Parthvi Vala <pvala@redhat.com>

Signed-off-by: Parthvi Vala <pvala@redhat.com>
2023-01-23 15:21:18 -05:00

495 lines
16 KiB
Go

package binding
import (
"fmt"
"testing"
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/devfile/library/v2/pkg/devfile/parser"
devfileCtx "github.com/devfile/library/v2/pkg/devfile/parser/context"
"github.com/devfile/library/v2/pkg/testingutil/filesystem"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
servicebinding "github.com/redhat-developer/service-binding-operator/apis/binding/v1alpha1"
"github.com/redhat-developer/odo/pkg/kclient"
odoTestingUtil "github.com/redhat-developer/odo/pkg/testingutil"
)
const deploymentKind = "Deployment"
var deploymentGVK = appsv1.SchemeGroupVersion.WithKind(deploymentKind)
var deploymentApiVersion, _ = deploymentGVK.ToAPIVersionAndKind()
const clusterKind = "Cluster"
var clusterGV = schema.GroupVersion{
Group: "postgresql.k8s.enterprisedb.io",
Version: "v1",
}
var clusterGVK = clusterGV.WithKind(clusterKind)
var clusterGVR = clusterGV.WithResource("clusters")
var ClusterAPIVersion, _ = clusterGVK.ToAPIVersionAndKind()
func TestBindingClient_GetFlags(t *testing.T) {
type args struct {
flags map[string]string
}
tests := []struct {
name string
args args
want map[string]string
}{
{
name: "service and name flags are present",
args: args{flags: map[string]string{"service": "redisService", "name": "mybinding", "v": "9"}},
want: map[string]string{"service": "redisService", "name": "mybinding"},
},
{
name: "only one flag is present",
args: args{map[string]string{"service": "redisService", "v": "9"}},
want: map[string]string{"service": "redisService"},
},
{
name: "no relevant flags are present",
args: args{map[string]string{"v": "9"}},
want: map[string]string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := &BindingClient{}
got := o.GetFlags(tt.args.flags)
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Errorf("BindingClient.GetFlags() mismatch (-want +got):\n%s", diff)
}
})
}
}
func TestBindingClient_GetServiceInstances(t *testing.T) {
ns := "my-ns"
var clusterUnstructured unstructured.Unstructured
clusterUnstructured.SetGroupVersionKind(clusterGVK)
clusterUnstructured.SetName("postgres-cluster")
serviceBindingInstance := servicebinding.BindableKinds{
TypeMeta: metav1.TypeMeta{
Kind: "BindableKinds",
APIVersion: "v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "bindable-kinds",
},
Status: []servicebinding.BindableKindsStatus{
{
Group: "redis.redis.opstreelabs.in",
Kind: "Redis",
Version: "v1beta1",
},
{
Group: "postgresql.k8s.enterprisedb.io",
Kind: "Cluster",
Version: "v1",
},
},
}
type fields struct {
kubernetesClient func(ctrl *gomock.Controller) kclient.ClientInterface
}
type args struct {
namespace string
}
tests := []struct {
name string
fields fields
args args
want map[string]unstructured.Unstructured
wantErr bool
}{
{
name: "obtained service instances",
fields: fields{
kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
client := kclient.NewMockClientInterface(ctrl)
client.EXPECT().IsServiceBindingSupported().Return(true, nil)
client.EXPECT().GetBindableKinds().Return(serviceBindingInstance, nil)
client.EXPECT().GetBindableKindStatusRestMapping(serviceBindingInstance.Status).Return([]*meta.RESTMapping{
{Resource: clusterGVR, GroupVersionKind: clusterGVK},
}, nil)
client.EXPECT().ListDynamicResources("", clusterGVR, "").Return(&unstructured.UnstructuredList{Items: []unstructured.Unstructured{clusterUnstructured}}, nil)
return client
},
},
want: map[string]unstructured.Unstructured{
"postgres-cluster (Cluster.postgresql.k8s.enterprisedb.io)": clusterUnstructured,
},
wantErr: false,
},
{
name: "obtained service instances from specific namespace",
fields: fields{
kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
client := kclient.NewMockClientInterface(ctrl)
client.EXPECT().IsServiceBindingSupported().Return(true, nil)
client.EXPECT().GetBindableKinds().Return(serviceBindingInstance, nil)
client.EXPECT().GetBindableKindStatusRestMapping(serviceBindingInstance.Status).Return([]*meta.RESTMapping{
{Resource: clusterGVR, GroupVersionKind: clusterGVK},
}, nil)
client.EXPECT().ListDynamicResources(ns, clusterGVR, "").Return(&unstructured.UnstructuredList{Items: []unstructured.Unstructured{clusterUnstructured}}, nil)
return client
},
},
args: args{
namespace: ns,
},
want: map[string]unstructured.Unstructured{
"postgres-cluster (Cluster.postgresql.k8s.enterprisedb.io)": clusterUnstructured,
},
wantErr: false,
},
{
name: "do not fail if no bindable kind service was found",
fields: fields{
kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
client := kclient.NewMockClientInterface(ctrl)
client.EXPECT().IsServiceBindingSupported().Return(true, nil)
client.EXPECT().GetBindableKinds().Return(serviceBindingInstance, nil)
client.EXPECT().GetBindableKindStatusRestMapping(serviceBindingInstance.Status).Return(nil, nil)
return client
},
},
want: map[string]unstructured.Unstructured{},
wantErr: false,
},
{
name: "do not fail if no instances of the bindable kind services was found",
fields: fields{kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
client := kclient.NewMockClientInterface(ctrl)
client.EXPECT().IsServiceBindingSupported().Return(true, nil)
client.EXPECT().GetBindableKinds().Return(serviceBindingInstance, nil)
client.EXPECT().GetBindableKindStatusRestMapping(serviceBindingInstance.Status).Return([]*meta.RESTMapping{
{Resource: clusterGVR, GroupVersionKind: clusterGVK},
}, nil)
client.EXPECT().ListDynamicResources("", clusterGVR, "").Return(&unstructured.UnstructuredList{Items: nil}, nil)
return client
}},
want: map[string]unstructured.Unstructured{},
wantErr: false,
},
{
name: "error out if the servicebinding CRD is not found",
fields: fields{kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
client := kclient.NewMockClientInterface(ctrl)
client.EXPECT().IsServiceBindingSupported().Return(false, nil)
return client
}},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
o := &BindingClient{
kubernetesClient: tt.fields.kubernetesClient(ctrl),
}
got, err := o.GetServiceInstances(tt.args.namespace)
if (err != nil) != tt.wantErr {
t.Errorf("GetServiceInstances() error = %v, wantErr %v", err, tt.wantErr)
return
}
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Errorf("BindingClient.GetServiceInstances() mismatch (-want +got):\n%s", diff)
}
})
}
}
func TestBindingClient_AddBindingToDevfile(t *testing.T) {
bindingName := "my-nodejs-app-cluster-sample"
ns := "my-ns"
var clusterUnstructured unstructured.Unstructured
clusterUnstructured.SetGroupVersionKind(clusterGVK)
clusterUnstructured.SetName("cluster-sample")
serviceBindingRef := servicebinding.Service{
Id: &bindingName,
NamespacedRef: servicebinding.NamespacedRef{
Ref: servicebinding.Ref{
Group: clusterGVK.Group,
Version: clusterGVK.Version,
Kind: clusterGVK.Kind,
Name: clusterUnstructured.GetName(),
Resource: "clusters",
},
},
}
serviceBindingRefWithNs := servicebinding.Service{
Id: &bindingName,
NamespacedRef: servicebinding.NamespacedRef{
Ref: servicebinding.Ref{
Group: clusterGVK.Group,
Version: clusterGVK.Version,
Kind: clusterGVK.Kind,
Name: clusterUnstructured.GetName(),
Resource: "clusters",
},
Namespace: &ns,
},
}
type fields struct {
kubernetesClient func(ctrl *gomock.Controller) kclient.ClientInterface
}
type args struct {
bindingName string
bindAsFiles bool
namingStrategy string
namespace string
unstructuredService unstructured.Unstructured
obj parser.DevfileObj
}
tests := []struct {
name string
fields fields
args args
want *parser.DevfileObj
wantErr bool
}{
{
name: "successfully add binding",
fields: fields{
kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
client := kclient.NewMockClientInterface(ctrl)
client.EXPECT().NewServiceBindingServiceObject("", clusterUnstructured, bindingName).Return(serviceBindingRef, nil)
client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
return client
},
},
args: args{
bindingName: bindingName,
bindAsFiles: false,
unstructuredService: clusterUnstructured,
obj: odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
},
want: getDevfileObjWithServiceBinding(bindingName, "", false, ""),
wantErr: false,
},
{
name: "successfully add binding with namespace",
fields: fields{
kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
client := kclient.NewMockClientInterface(ctrl)
client.EXPECT().NewServiceBindingServiceObject(ns, clusterUnstructured, bindingName).Return(serviceBindingRefWithNs, nil)
client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
return client
},
},
args: args{
bindingName: bindingName,
bindAsFiles: false,
namespace: ns,
unstructuredService: clusterUnstructured,
obj: odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
},
want: getDevfileObjWithServiceBinding(bindingName, ns, false, ""),
wantErr: false,
},
{
name: "successfully added binding for a Service Binding bound as files",
fields: fields{
kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
client := kclient.NewMockClientInterface(ctrl)
client.EXPECT().NewServiceBindingServiceObject("", clusterUnstructured, bindingName).Return(serviceBindingRef, nil)
client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
return client
},
},
args: args{
bindingName: bindingName,
bindAsFiles: true,
unstructuredService: clusterUnstructured,
obj: odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
},
want: getDevfileObjWithServiceBinding(bindingName, "", true, ""),
wantErr: false,
},
{
name: "successfully added binding for a Service Binding bound as files and namespace",
fields: fields{
kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
client := kclient.NewMockClientInterface(ctrl)
client.EXPECT().NewServiceBindingServiceObject(ns, clusterUnstructured, bindingName).Return(serviceBindingRefWithNs, nil)
client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
return client
},
},
args: args{
bindingName: bindingName,
bindAsFiles: true,
namespace: ns,
unstructuredService: clusterUnstructured,
obj: odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
},
want: getDevfileObjWithServiceBinding(bindingName, ns, true, ""),
wantErr: false,
},
{
name: "successfully added binding for a Service Binding with naming strategy",
fields: fields{
kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
client := kclient.NewMockClientInterface(ctrl)
client.EXPECT().NewServiceBindingServiceObject("", clusterUnstructured, bindingName).Return(serviceBindingRef, nil)
client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
return client
},
},
args: args{
bindingName: bindingName,
bindAsFiles: true,
namingStrategy: "uppercase",
unstructuredService: clusterUnstructured,
obj: odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
},
want: getDevfileObjWithServiceBinding(bindingName, "", true, "uppercase"),
wantErr: false,
},
{
name: "successfully added binding for a Service Binding with naming strategy and namespace",
fields: fields{
kubernetesClient: func(ctrl *gomock.Controller) kclient.ClientInterface {
client := kclient.NewMockClientInterface(ctrl)
client.EXPECT().NewServiceBindingServiceObject(ns, clusterUnstructured, bindingName).Return(serviceBindingRefWithNs, nil)
client.EXPECT().GetDeploymentAPIVersion().Return(deploymentGVK, nil)
return client
},
},
args: args{
bindingName: bindingName,
bindAsFiles: true,
namespace: ns,
namingStrategy: "uppercase",
unstructuredService: clusterUnstructured,
obj: odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs()),
},
want: getDevfileObjWithServiceBinding(bindingName, ns, true, "uppercase"),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
o := &BindingClient{
kubernetesClient: tt.fields.kubernetesClient(ctrl),
}
got, err := o.AddBindingToDevfile(
tt.args.obj.GetMetadataName(),
tt.args.bindingName,
tt.args.bindAsFiles,
tt.args.namespace,
tt.args.namingStrategy,
tt.args.unstructuredService,
tt.args.obj,
)
if (err != nil) != tt.wantErr {
t.Errorf("AddBindingToDevfile() error = %v, wantErr %v", err, tt.wantErr)
return
}
if diff := cmp.Diff(*tt.want, got,
cmp.AllowUnexported(devfileCtx.DevfileCtx{}),
cmpopts.IgnoreInterfaces(struct{ filesystem.Filesystem }{})); diff != "" {
t.Errorf("BindingClient.AddBindingToDevfile() mismatch (-want +got):\n%s", diff)
}
})
}
}
func getDevfileObjWithServiceBinding(bindingName string, ns string, bindAsFiles bool, namingStrategy string) *parser.DevfileObj {
obj := odoTestingUtil.GetTestDevfileObj(filesystem.NewFakeFs())
_ = obj.Data.AddComponents([]v1alpha2.Component{{
Name: bindingName,
ComponentUnion: v1alpha2.ComponentUnion{
Kubernetes: &v1alpha2.KubernetesComponent{
K8sLikeComponent: v1alpha2.K8sLikeComponent{
BaseComponent: v1alpha2.BaseComponent{},
K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
Inlined: getServiceBindingInlinedContent(ns, bindAsFiles, namingStrategy),
},
},
},
},
}})
return &obj
}
func getServiceBindingInlinedContent(ns string, bindAsFiles bool, namingStrategy string) string {
h := fmt.Sprintf(`apiVersion: binding.operators.coreos.com/v1alpha1
kind: ServiceBinding
metadata:
creationTimestamp: null
name: my-nodejs-app-cluster-sample
spec:
application:
group: apps
kind: Deployment
name: my-nodejs-app-app
version: v1
bindAsFiles: %v
detectBindingResources: true`, bindAsFiles)
fNoNamespace := `
services:
- group: postgresql.k8s.enterprisedb.io
id: my-nodejs-app-cluster-sample
kind: Cluster
name: cluster-sample
resource: clusters
version: v1
status:
secret: ""
`
fNamespace := fmt.Sprintf(`
services:
- group: postgresql.k8s.enterprisedb.io
id: my-nodejs-app-cluster-sample
kind: Cluster
name: cluster-sample
namespace: %s
resource: clusters
version: v1
status:
secret: ""
`, ns)
if namingStrategy != "" {
if ns != "" {
return fmt.Sprintf(`%s
namingStrategy: %s%s`, h, namingStrategy, fNamespace)
}
return fmt.Sprintf(`%s
namingStrategy: %s%s`, h, namingStrategy, fNoNamespace)
}
if ns != "" {
return h + fNamespace
}
return h + fNoNamespace
}