Merge pull request #1154 from r2d4/svc-tests

Refactor service package and add test coverage
pull/1180/head
Matt Rickard 2017-02-24 13:19:07 -08:00 committed by GitHub
commit b26f0aa66b
4 changed files with 455 additions and 240 deletions

View File

@ -68,12 +68,11 @@ var serviceCmd = &cobra.Command{
defer api.Close()
cluster.EnsureMinikubeRunningOrExit(api, 1)
if err := service.ValidateService(namespace, svc); err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("service '%s' could not be found running in namespace '%s' within kubernetes",
svc, namespace))
err = service.WaitAndMaybeOpenService(api, namespace, svc, serviceURLTemplate, serviceURLMode, https)
if err != nil {
fmt.Fprintf(os.Stderr, "Error opening service: %s\n", err)
os.Exit(1)
}
service.WaitAndMaybeOpenService(api, namespace, svc, serviceURLTemplate, serviceURLMode, https)
},
}

View File

@ -34,8 +34,6 @@ import (
"github.com/docker/machine/libmachine/state"
"github.com/golang/glog"
"github.com/pkg/errors"
"k8s.io/client-go/1.5/kubernetes"
"k8s.io/client-go/1.5/tools/clientcmd"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/constants"
@ -421,21 +419,6 @@ func CreateSSHShell(api libmachine.API, args []string) error {
return client.Shell(strings.Join(args, " "))
}
func GetKubernetesClient() (*kubernetes.Clientset, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
configOverrides := &clientcmd.ConfigOverrides{}
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
config, err := kubeConfig.ClientConfig()
if err != nil {
return nil, fmt.Errorf("Error creating kubeConfig: %s", err)
}
client, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, errors.Wrap(err, "Error creating new client from kubeConfig.ClientConfig()")
}
return client, nil
}
// EnsureMinikubeRunningOrExit checks that minikube has a status available and that
// that the status is `Running`, otherwise it will exit
func EnsureMinikubeRunningOrExit(api libmachine.API, exitStatus int) {

View File

@ -31,6 +31,7 @@ import (
corev1 "k8s.io/client-go/1.5/kubernetes/typed/core/v1"
kubeapi "k8s.io/client-go/1.5/pkg/api"
"k8s.io/client-go/1.5/pkg/api/v1"
"k8s.io/client-go/1.5/tools/clientcmd"
"text/template"
@ -39,6 +40,33 @@ import (
"k8s.io/minikube/pkg/util"
)
type K8sClient interface {
GetCoreClient() (corev1.CoreInterface, error)
}
type K8sClientGetter struct{}
var k8s K8sClient
func init() {
k8s = &K8sClientGetter{}
}
func (*K8sClientGetter) GetCoreClient() (corev1.CoreInterface, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
configOverrides := &clientcmd.ConfigOverrides{}
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
config, err := kubeConfig.ClientConfig()
if err != nil {
return nil, fmt.Errorf("Error creating kubeConfig: %s", err)
}
client, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, errors.Wrap(err, "Error creating new client from kubeConfig.ClientConfig()")
}
return client.Core(), nil
}
type ServiceURL struct {
Namespace string
Name string
@ -47,6 +75,8 @@ type ServiceURL struct {
type ServiceURLs []ServiceURL
// Returns all the node port URLs for every service in a particular namespace
// Accepts a template for formating
func GetServiceURLs(api libmachine.API, namespace string, t *template.Template) (ServiceURLs, error) {
host, err := cluster.CheckIfApiExistsAndLoad(api)
if err != nil {
@ -58,27 +88,22 @@ func GetServiceURLs(api libmachine.API, namespace string, t *template.Template)
return nil, err
}
client, err := cluster.GetKubernetesClient()
client, err := k8s.GetCoreClient()
if err != nil {
return nil, err
}
getter := client.Services(namespace)
serviceInterface := client.Services(namespace)
svcs, err := getter.List(kubeapi.ListOptions{})
svcs, err := serviceInterface.List(kubeapi.ListOptions{})
if err != nil {
return nil, err
}
var serviceURLs []ServiceURL
for _, svc := range svcs.Items {
urls, err := getServiceURLsWithClient(client, ip, svc.Namespace, svc.Name, t)
urls, err := printURLsForService(client, ip, svc.Name, svc.Namespace, t)
if err != nil {
if _, ok := err.(MissingNodePortError); ok {
serviceURLs = append(serviceURLs, ServiceURL{Namespace: svc.Namespace, Name: svc.Name})
continue
}
return nil, err
}
serviceURLs = append(serviceURLs, ServiceURL{Namespace: svc.Namespace, Name: svc.Name, URLs: urls})
@ -87,123 +112,8 @@ func GetServiceURLs(api libmachine.API, namespace string, t *template.Template)
return serviceURLs, nil
}
// CheckService waits for the specified service to be ready by returning an error until the service is up
// The check is done by polling the endpoint associated with the service and when the endpoint exists, returning no error->service-online
func CheckService(namespace string, service string) error {
client, err := cluster.GetKubernetesClient()
if err != nil {
return &util.RetriableError{Err: err}
}
endpoints := client.Endpoints(namespace)
if err != nil {
return &util.RetriableError{Err: err}
}
endpoint, err := endpoints.Get(service)
if err != nil {
return &util.RetriableError{Err: err}
}
return checkEndpointReady(endpoint)
}
func checkEndpointReady(endpoint *v1.Endpoints) error {
const notReadyMsg = "Waiting, endpoint for service is not ready yet...\n"
if len(endpoint.Subsets) == 0 {
fmt.Fprintf(os.Stderr, notReadyMsg)
return &util.RetriableError{Err: errors.New("Endpoint for service is not ready yet")}
}
for _, subset := range endpoint.Subsets {
if len(subset.Addresses) == 0 {
fmt.Fprintf(os.Stderr, notReadyMsg)
return &util.RetriableError{Err: errors.New("No endpoints for service are ready yet")}
}
}
return nil
}
func WaitAndMaybeOpenService(api libmachine.API, namespace string, service string, urlTemplate *template.Template, urlMode bool, https bool) {
if err := util.RetryAfter(20, func() error { return CheckService(namespace, service) }, 6*time.Second); err != nil {
fmt.Fprintf(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s\n", service, err)
os.Exit(1)
}
urls, err := GetServiceURLsForService(api, namespace, service, urlTemplate)
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, "Check that minikube is running and that you have specified the correct namespace (-n flag).")
os.Exit(1)
}
for _, url := range urls {
if https {
url = strings.Replace(url, "http", "https", 1)
}
if urlMode || !strings.HasPrefix(url, "http") {
fmt.Fprintln(os.Stdout, url)
} else {
fmt.Fprintln(os.Stdout, "Opening kubernetes service "+namespace+"/"+service+" in default browser...")
browser.OpenURL(url)
}
}
}
func GetServiceListByLabel(namespace string, key string, value string) (*v1.ServiceList, error) {
client, err := cluster.GetKubernetesClient()
if err != nil {
return &v1.ServiceList{}, &util.RetriableError{Err: err}
}
services := client.Services(namespace)
if err != nil {
return &v1.ServiceList{}, &util.RetriableError{Err: err}
}
return getServiceListFromServicesByLabel(services, key, value)
}
func getServiceListFromServicesByLabel(services corev1.ServiceInterface, key string, value string) (*v1.ServiceList, error) {
selector := labels.SelectorFromSet(labels.Set(map[string]string{key: value}))
serviceList, err := services.List(kubeapi.ListOptions{LabelSelector: selector})
if err != nil {
return &v1.ServiceList{}, &util.RetriableError{Err: err}
}
return serviceList, nil
}
func getServicePortsFromServiceGetter(services serviceGetter, service string) ([]int32, error) {
svc, err := services.Get(service)
if err != nil {
return nil, fmt.Errorf("Error getting %s service: %s", service, err)
}
var nodePorts []int32
if len(svc.Spec.Ports) > 0 {
for _, port := range svc.Spec.Ports {
if port.NodePort > 0 {
nodePorts = append(nodePorts, port.NodePort)
}
}
}
if len(nodePorts) == 0 {
return nil, MissingNodePortError{svc}
}
return nodePorts, nil
}
type serviceGetter interface {
Get(name string) (*v1.Service, error)
List(kubeapi.ListOptions) (*v1.ServiceList, error)
}
func getServicePorts(client *kubernetes.Clientset, namespace, service string) ([]int32, error) {
services := client.Services(namespace)
return getServicePortsFromServiceGetter(services, service)
}
type MissingNodePortError struct {
service *v1.Service
}
func (e MissingNodePortError) Error() string {
return fmt.Sprintf("Service %s/%s does not have a node port. To have one assigned automatically, the service type must be NodePort or LoadBalancer, but this service is of type %s.", e.service.Namespace, e.service.Name, e.service.Spec.Type)
}
// Returns all the node ports for a service in a namespace
// with optional formatting
func GetServiceURLsForService(api libmachine.API, namespace, service string, t *template.Template) ([]string, error) {
host, err := cluster.CheckIfApiExistsAndLoad(api)
if err != nil {
@ -215,26 +125,34 @@ func GetServiceURLsForService(api libmachine.API, namespace, service string, t *
return nil, errors.Wrap(err, "Error getting ip from host")
}
client, err := cluster.GetKubernetesClient()
client, err := k8s.GetCoreClient()
if err != nil {
return nil, err
}
return getServiceURLsWithClient(client, ip, namespace, service, t)
return printURLsForService(client, ip, service, namespace, t)
}
func getServiceURLsWithClient(client *kubernetes.Clientset, ip, namespace, service string, t *template.Template) ([]string, error) {
func printURLsForService(c corev1.CoreInterface, ip, service, namespace string, t *template.Template) ([]string, error) {
if t == nil {
return nil, errors.New("Error, attempted to generate service url with nil --format template")
}
ports, err := getServicePorts(client, namespace, service)
s := c.Services(namespace)
svc, err := s.Get(service)
if err != nil {
return nil, err
return nil, errors.Wrapf(err, "service '%s' could not be found running", service)
}
var nodePorts []int32
if len(svc.Spec.Ports) > 0 {
for _, port := range svc.Spec.Ports {
if port.NodePort > 0 {
nodePorts = append(nodePorts, port.NodePort)
}
}
}
urls := []string{}
for _, port := range ports {
for _, port := range nodePorts {
var doc bytes.Buffer
err = t.Execute(&doc, struct {
IP string
@ -257,14 +175,89 @@ func getServiceURLsWithClient(client *kubernetes.Clientset, ip, namespace, servi
return urls, nil
}
func ValidateService(namespace string, service string) error {
client, err := cluster.GetKubernetesClient()
// CheckService waits for the specified service to be ready by returning an error until the service is up
// The check is done by polling the endpoint associated with the service and when the endpoint exists, returning no error->service-online
func CheckService(namespace string, service string) error {
client, err := k8s.GetCoreClient()
if err != nil {
return errors.Wrap(err, "error validating input service name")
return errors.Wrap(err, "Error getting kubernetes client")
}
services := client.Services(namespace)
if _, err = services.Get(service); err != nil {
return errors.Wrapf(err, "service '%s' could not be found running in namespace '%s' within kubernetes", service, namespace)
err = validateService(services, service)
if err != nil {
return errors.Wrap(err, "Error validating service")
}
endpoints := client.Endpoints(namespace)
return checkEndpointReady(endpoints, service)
}
func validateService(s corev1.ServiceInterface, service string) error {
if _, err := s.Get(service); err != nil {
return errors.Wrapf(err, "Error getting service %s", service)
}
return nil
}
func checkEndpointReady(endpoints corev1.EndpointsInterface, service string) error {
endpoint, err := endpoints.Get(service)
if err != nil {
return errors.Wrapf(err, "Error getting endpoints for service %s", service)
}
const notReadyMsg = "Waiting, endpoint for service is not ready yet...\n"
if len(endpoint.Subsets) == 0 {
fmt.Fprintf(os.Stderr, notReadyMsg)
return &util.RetriableError{Err: errors.New("Endpoint for service is not ready yet")}
}
for _, subset := range endpoint.Subsets {
if len(subset.Addresses) == 0 {
fmt.Fprintf(os.Stderr, notReadyMsg)
return &util.RetriableError{Err: errors.New("No endpoints for service are ready yet")}
}
}
return nil
}
func WaitAndMaybeOpenService(api libmachine.API, namespace string, service string, urlTemplate *template.Template, urlMode bool, https bool) error {
if err := util.RetryAfter(20, func() error { return CheckService(namespace, service) }, 6*time.Second); err != nil {
return errors.Wrapf(err, "Could not find finalized endpoint being pointed to by %s", service)
}
urls, err := GetServiceURLsForService(api, namespace, service, urlTemplate)
if err != nil {
return errors.Wrap(err, "Check that minikube is running and that you have specified the correct namespace")
}
for _, url := range urls {
if https {
url = strings.Replace(url, "http", "https", 1)
}
if urlMode || !strings.HasPrefix(url, "http") {
fmt.Fprintln(os.Stdout, url)
} else {
fmt.Fprintln(os.Stdout, "Opening kubernetes service "+namespace+"/"+service+" in default browser...")
browser.OpenURL(url)
}
}
return nil
}
func GetServiceListByLabel(namespace string, key string, value string) (*v1.ServiceList, error) {
client, err := k8s.GetCoreClient()
if err != nil {
return &v1.ServiceList{}, &util.RetriableError{Err: err}
}
services := client.Services(namespace)
if err != nil {
return &v1.ServiceList{}, &util.RetriableError{Err: err}
}
return getServiceListFromServicesByLabel(services, key, value)
}
func getServiceListFromServicesByLabel(services corev1.ServiceInterface, key string, value string) (*v1.ServiceList, error) {
selector := labels.SelectorFromSet(labels.Set(map[string]string{key: value}))
serviceList, err := services.List(kubeapi.ListOptions{LabelSelector: selector})
if err != nil {
return &v1.ServiceList{}, &util.RetriableError{Err: err}
}
return serviceList, nil
}

View File

@ -17,64 +17,187 @@ limitations under the License.
package service
import (
"reflect"
"strings"
"testing"
"text/template"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/host"
"github.com/pkg/errors"
corev1 "k8s.io/client-go/1.5/kubernetes/typed/core/v1"
"k8s.io/client-go/1.5/kubernetes/typed/core/v1/fake"
"k8s.io/client-go/1.5/pkg/api"
"k8s.io/client-go/1.5/pkg/api/unversioned"
"k8s.io/client-go/1.5/pkg/api/v1"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/tests"
)
func TestCheckEndpointReady(t *testing.T) {
endpointNoSubsets := &v1.Endpoints{}
if err := checkEndpointReady(endpointNoSubsets); err == nil {
t.Fatalf("Endpoint had no subsets but checkEndpointReady did not return an error")
}
type MockClientGetter struct {
servicesMap map[string]corev1.ServiceInterface
}
endpointNotReady := &v1.Endpoints{
func (m *MockClientGetter) GetCoreClient() (corev1.CoreInterface, error) {
return &MockCoreClient{
servicesMap: m.servicesMap,
}, nil
}
type MockCoreClient struct {
fake.FakeCore
servicesMap map[string]corev1.ServiceInterface
}
var serviceNamespaces = map[string]corev1.ServiceInterface{
"default": defaultNamespaceServiceInterface,
}
var defaultNamespaceServiceInterface = &MockServiceInterface{
ServiceList: &v1.ServiceList{
Items: []v1.Service{
{
ObjectMeta: v1.ObjectMeta{
Name: "mock-dashboard",
Namespace: "default",
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{
{NodePort: int32(1111)},
{NodePort: int32(2222)},
},
},
},
{
ObjectMeta: v1.ObjectMeta{
Name: "mock-dashboard-no-ports",
Namespace: "default",
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{},
},
},
},
},
}
func (m *MockCoreClient) Endpoints(namespace string) corev1.EndpointsInterface {
return &MockEndpointsInterface{}
}
func (m *MockCoreClient) Services(namespace string) corev1.ServiceInterface {
return m.servicesMap[namespace]
}
type MockEndpointsInterface struct {
fake.FakeEndpoints
Endpoints *v1.Endpoints
}
var endpointMap = map[string]*v1.Endpoints{
"no-subsets": {},
"not-ready": {
Subsets: []v1.EndpointSubset{
{Addresses: []v1.EndpointAddress{},
{
Addresses: []v1.EndpointAddress{},
NotReadyAddresses: []v1.EndpointAddress{
{IP: "1.1.1.1"},
{IP: "2.2.2.2"},
{IP: "3.3.3.3"},
}}}}
if err := checkEndpointReady(endpointNotReady); err == nil {
t.Fatalf("Endpoint had no Addresses but checkEndpointReady did not return an error")
},
},
},
},
"one-ready": {
Subsets: []v1.EndpointSubset{
{
Addresses: []v1.EndpointAddress{
{IP: "1.1.1.1"},
},
NotReadyAddresses: []v1.EndpointAddress{
{IP: "2.2.2.2"},
},
},
},
},
}
func (e MockEndpointsInterface) Get(name string) (*v1.Endpoints, error) {
endpoint, ok := endpointMap[name]
if !ok {
return nil, errors.New("Endpoint not found")
}
return endpoint, nil
}
func TestCheckEndpointReady(t *testing.T) {
var tests = []struct {
description string
service string
err bool
}{
{
description: "Endpoint with no subsets should return an error",
service: "no-subsets",
err: true,
},
{
description: "Endpoint with no ready endpoints should return an error",
service: "not-ready",
err: true,
},
{
description: "Endpoint with at least one ready endpoint should not return an error",
service: "one-ready",
err: false,
},
}
endpointReady := &v1.Endpoints{
Subsets: []v1.EndpointSubset{
{Addresses: []v1.EndpointAddress{
{IP: "1.1.1.1"},
{IP: "2.2.2.2"},
},
NotReadyAddresses: []v1.EndpointAddress{},
}},
}
if err := checkEndpointReady(endpointReady); err != nil {
t.Fatalf("Endpoint was ready with at least one Address, but checkEndpointReady returned an error")
for _, test := range tests {
test := test
t.Run(test.description, func(t *testing.T) {
t.Parallel()
err := checkEndpointReady(&MockEndpointsInterface{}, test.service)
if err != nil && !test.err {
t.Errorf("Check endpoints returned an error: %+v", err)
}
if err == nil && test.err {
t.Errorf("Check endpoints should have returned an error but returned nil")
}
})
}
}
type ServiceInterfaceMock struct {
type MockServiceInterface struct {
fake.FakeServices
ServiceList *v1.ServiceList
}
func (s ServiceInterfaceMock) List(opts api.ListOptions) (*v1.ServiceList, error) {
func (s MockServiceInterface) List(opts api.ListOptions) (*v1.ServiceList, error) {
serviceList := &v1.ServiceList{
Items: []v1.Service{},
}
keyValArr := strings.Split(opts.LabelSelector.String(), "=")
for _, service := range s.ServiceList.Items {
if service.Spec.Selector[keyValArr[0]] == keyValArr[1] {
serviceList.Items = append(serviceList.Items, service)
if opts.LabelSelector != nil {
keyValArr := strings.Split(opts.LabelSelector.String(), "=")
for _, service := range s.ServiceList.Items {
if service.Spec.Selector[keyValArr[0]] == keyValArr[1] {
serviceList.Items = append(serviceList.Items, service)
}
}
return serviceList, nil
}
return s.ServiceList, nil
}
func (s MockServiceInterface) Get(name string) (*v1.Service, error) {
for _, svc := range s.ServiceList.Items {
if svc.ObjectMeta.Name == name {
return &svc, nil
}
}
return serviceList, nil
return nil, nil
}
func TestGetServiceListFromServicesByLabel(t *testing.T) {
@ -89,7 +212,7 @@ func TestGetServiceListFromServicesByLabel(t *testing.T) {
},
},
}
serviceIface := ServiceInterfaceMock{
serviceIface := MockServiceInterface{
ServiceList: serviceList,
}
if _, err := getServiceListFromServicesByLabel(&serviceIface, "nothing", "nothing"); err != nil {
@ -101,69 +224,186 @@ func TestGetServiceListFromServicesByLabel(t *testing.T) {
}
}
type MockServiceGetter struct {
services map[string]v1.Service
}
func NewMockServiceGetter() *MockServiceGetter {
return &MockServiceGetter{
services: make(map[string]v1.Service),
func TestPrintURLsForService(t *testing.T) {
defaultTemplate := template.Must(template.New("svc-template").Parse("{{.IP}}:{{.Port}}"))
client := &MockCoreClient{
servicesMap: serviceNamespaces,
}
}
func (mockServiceGetter *MockServiceGetter) Get(name string) (*v1.Service, error) {
service, ok := mockServiceGetter.services[name]
if !ok {
return nil, errors.Errorf("Error getting %s service from mockServiceGetter", name)
var tests = []struct {
description string
serviceName string
namespace string
tmpl *template.Template
expectedOutput []string
err bool
}{
{
description: "should get all node ports",
serviceName: "mock-dashboard",
namespace: "default",
tmpl: defaultTemplate,
expectedOutput: []string{"127.0.0.1:1111", "127.0.0.1:2222"},
},
{
description: "empty slice for no node ports",
serviceName: "mock-dashboard-no-ports",
namespace: "default",
tmpl: defaultTemplate,
expectedOutput: []string{},
},
{
description: "throw error without template",
err: true,
},
}
return &service, nil
}
func (mockServiceGetter *MockServiceGetter) List(options api.ListOptions) (*v1.ServiceList, error) {
services := v1.ServiceList{
TypeMeta: unversioned.TypeMeta{Kind: "ServiceList", APIVersion: "v1"},
ListMeta: unversioned.ListMeta{},
for _, test := range tests {
test := test
t.Run(test.description, func(t *testing.T) {
t.Parallel()
urls, err := printURLsForService(client, "127.0.0.1", test.serviceName, test.namespace, test.tmpl)
if err != nil && !test.err {
t.Errorf("Error: %s", err)
}
if err == nil && test.err {
t.Errorf("Expected error but got none")
}
if !reflect.DeepEqual(urls, test.expectedOutput) {
t.Errorf("Expected %+v \n\n Actual: %+v \n\n")
}
})
}
for _, svc := range mockServiceGetter.services {
services.Items = append(services.Items, svc)
}
return &services, nil
}
func TestGetServiceURLs(t *testing.T) {
mockServiceGetter := NewMockServiceGetter()
expected := []int32{1111, 2222}
mockDashboardService := v1.Service{
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{
{
NodePort: expected[0],
}, {
NodePort: expected[1],
}},
defaultAPI := &tests.MockAPI{
Hosts: map[string]*host.Host{
constants.MachineName: {
Name: constants.MachineName,
Driver: &tests.MockDriver{},
},
},
}
mockServiceGetter.services["mock-service"] = mockDashboardService
defaultTemplate := template.Must(template.New("svc-template").Parse("{{.IP}}:{{.Port}}"))
ports, err := getServicePortsFromServiceGetter(mockServiceGetter, "mock-service")
if err != nil {
t.Fatalf("Error getting mock-service ports from api: Error: %s", err)
var tests = []struct {
description string
api libmachine.API
namespace string
expected ServiceURLs
err bool
}{
{
description: "no host",
api: &tests.MockAPI{
Hosts: make(map[string]*host.Host),
},
err: true,
},
{
description: "correctly return serviceURLs",
namespace: "default",
api: defaultAPI,
expected: []ServiceURL{
{
Namespace: "default",
Name: "mock-dashboard",
URLs: []string{"127.0.0.1:1111", "127.0.0.1:2222"},
},
{
Namespace: "default",
Name: "mock-dashboard-no-ports",
URLs: []string{},
},
},
},
}
for i := range ports {
if ports[i] != expected[i] {
t.Fatalf("Error getting mock-service port from api: Expected: %d, Got: %d", ports[0], expected)
}
defer revertK8sClient(k8s)
for _, test := range tests {
test := test
t.Run(test.description, func(t *testing.T) {
t.Parallel()
k8s = &MockClientGetter{
servicesMap: serviceNamespaces,
}
urls, err := GetServiceURLs(test.api, test.namespace, defaultTemplate)
if err != nil && !test.err {
t.Errorf("Error GetServiceURLs %s", err)
}
if err == nil && test.err {
t.Errorf("Test should have failed, but didn't")
}
if !reflect.DeepEqual(urls, test.expected) {
t.Errorf("URLs did not match, expected %+v \n\n got %+v", test.expected, urls)
}
})
}
}
func TestGetServiceURLWithoutNodePort(t *testing.T) {
mockServiceGetter := NewMockServiceGetter()
mockDashboardService := v1.Service{}
mockServiceGetter.services["mock-service"] = mockDashboardService
func TestGetServiceURLsForService(t *testing.T) {
defaultAPI := &tests.MockAPI{
Hosts: map[string]*host.Host{
constants.MachineName: {
Name: constants.MachineName,
Driver: &tests.MockDriver{},
},
},
}
defaultTemplate := template.Must(template.New("svc-template").Parse("{{.IP}}:{{.Port}}"))
_, err := getServicePortsFromServiceGetter(mockServiceGetter, "mock-service")
if err == nil {
t.Fatalf("Expected error getting service with no node port")
var tests = []struct {
description string
api libmachine.API
namespace string
service string
expected []string
err bool
}{
{
description: "no host",
api: &tests.MockAPI{
Hosts: make(map[string]*host.Host),
},
err: true,
},
{
description: "correctly return serviceURLs",
namespace: "default",
service: "mock-dashboard",
api: defaultAPI,
expected: []string{"127.0.0.1:1111", "127.0.0.1:2222"},
},
{
description: "correctly return empty serviceURLs",
namespace: "default",
service: "mock-dashboard-no-ports",
api: defaultAPI,
expected: []string{},
},
}
defer revertK8sClient(k8s)
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
t.Parallel()
k8s = &MockClientGetter{
servicesMap: serviceNamespaces,
}
urls, err := GetServiceURLsForService(test.api, test.namespace, test.service, defaultTemplate)
if err != nil && !test.err {
t.Errorf("Error GetServiceURLsForService %s", err)
}
if err == nil && test.err {
t.Errorf("Test should have failed, but didn't")
}
if !reflect.DeepEqual(urls, test.expected) {
t.Errorf("URLs did not match, expected %+v \n\n got %+v", test.expected, urls)
}
})
}
}
func revertK8sClient(k K8sClient) {
k8s = k
}