Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include port names in the 'minikube service' cmd's output #5290

Merged
merged 1 commit into from
Sep 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 31 additions & 29 deletions pkg/minikube/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,20 @@ func (*K8sClientGetter) GetClientset(timeout time.Duration) (*kubernetes.Clients
return client, nil
}

// URL represents service URL
type URL struct {
// SvcURL represents a service URL. Each item in the URLs field combines the service URL with one of the configured
// node ports. The PortNames field contains the configured names of the ports in the URLs field (sorted correspondingly -
// first item in PortNames belongs to the first item in URLs).
type SvcURL struct {
Namespace string
Name string
URLs []string
PortNames []string
}

// URLs represents a list of URL
type URLs []URL
type URLs []SvcURL

// GetServiceURLs returns all the node port URLs for every service in a particular namespace
// GetServiceURLs returns a SvcURL object for every service in a particular namespace.
// Accepts a template for formatting
func GetServiceURLs(api libmachine.API, namespace string, t *template.Template) (URLs, error) {
host, err := cluster.CheckIfHostExistsAndLoad(api, config.GetMachineName())
Expand All @@ -132,52 +135,49 @@ func GetServiceURLs(api libmachine.API, namespace string, t *template.Template)
return nil, err
}

var serviceURLs []URL
var serviceURLs []SvcURL
for _, svc := range svcs.Items {
urls, err := printURLsForService(client, ip, svc.Name, svc.Namespace, t)
svcURL, err := printURLsForService(client, ip, svc.Name, svc.Namespace, t)
if err != nil {
return nil, err
}
serviceURLs = append(serviceURLs, URL{Namespace: svc.Namespace, Name: svc.Name, URLs: urls})
serviceURLs = append(serviceURLs, svcURL)
}

return serviceURLs, nil
}

// GetServiceURLsForService 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) {
// GetServiceURLsForService returns a SvcUrl object for a service in a namespace. Supports optional formatting.
func GetServiceURLsForService(api libmachine.API, namespace, service string, t *template.Template) (SvcURL, error) {
host, err := cluster.CheckIfHostExistsAndLoad(api, config.GetMachineName())
if err != nil {
return nil, errors.Wrap(err, "Error checking if api exist and loading it")
return SvcURL{}, errors.Wrap(err, "Error checking if api exist and loading it")
}

ip, err := host.Driver.GetIP()
if err != nil {
return nil, errors.Wrap(err, "Error getting ip from host")
return SvcURL{}, errors.Wrap(err, "Error getting ip from host")
}

client, err := K8s.GetCoreClient()
if err != nil {
return nil, err
return SvcURL{}, err
}

return printURLsForService(client, ip, service, namespace, t)
}

func printURLsForService(c typed_core.CoreV1Interface, ip, service, namespace string, t *template.Template) ([]string, error) {
func printURLsForService(c typed_core.CoreV1Interface, ip, service, namespace string, t *template.Template) (SvcURL, error) {
if t == nil {
return nil, errors.New("Error, attempted to generate service url with nil --format template")
return SvcURL{}, errors.New("Error, attempted to generate service url with nil --format template")
}

s := c.Services(namespace)
svc, err := s.Get(service, meta.GetOptions{})
svc, err := c.Services(namespace).Get(service, meta.GetOptions{})
if err != nil {
return nil, errors.Wrapf(err, "service '%s' could not be found running", service)
return SvcURL{}, errors.Wrapf(err, "service '%s' could not be found running", service)
}

e := c.Endpoints(namespace)
endpoints, err := e.Get(service, meta.GetOptions{})
endpoints, err := c.Endpoints(namespace).Get(service, meta.GetOptions{})
m := make(map[int32]string)
if err == nil && endpoints != nil && len(endpoints.Subsets) > 0 {
for _, ept := range endpoints.Subsets {
Expand All @@ -188,6 +188,7 @@ func printURLsForService(c typed_core.CoreV1Interface, ip, service, namespace st
}

urls := []string{}
portNames := []string{}
for _, port := range svc.Spec.Ports {
if port.NodePort > 0 {
var doc bytes.Buffer
Expand All @@ -201,12 +202,13 @@ func printURLsForService(c typed_core.CoreV1Interface, ip, service, namespace st
m[port.TargetPort.IntVal],
})
if err != nil {
return nil, err
return SvcURL{}, err
}
urls = append(urls, doc.String())
portNames = append(portNames, m[port.TargetPort.IntVal])
}
}
return urls, nil
return SvcURL{Namespace: svc.Namespace, Name: svc.Name, URLs: urls, PortNames: portNames}, nil
}

// CheckService checks if a service is listening on a port.
Expand Down Expand Up @@ -249,7 +251,7 @@ func OptionallyHTTPSFormattedURLString(bareURLString string, https bool) (string
// "Namespace", "Name" and "URL" columns to a writer
func PrintServiceList(writer io.Writer, data [][]string) {
table := tablewriter.NewWriter(writer)
table.SetHeader([]string{"Namespace", "Name", "URL"})
table.SetHeader([]string{"Namespace", "Name", "Target Port", "URL"})
table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true})
table.SetCenterSeparator("|")
table.AppendBulk(data)
Expand All @@ -269,27 +271,27 @@ func WaitAndMaybeOpenService(api libmachine.API, namespace string, service strin
return errors.Wrapf(err, "Could not find finalized endpoint being pointed to by %s", service)
}

urls, err := GetServiceURLsForService(api, namespace, service, urlTemplate)
serviceURL, 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")
}

if !urlMode {
var data [][]string
if len(urls) == 0 {
data = append(data, []string{namespace, service, "No node port"})
if len(serviceURL.URLs) == 0 {
data = append(data, []string{namespace, service, "", "No node port"})
} else {
data = append(data, []string{namespace, service, strings.Join(urls, "\n")})
data = append(data, []string{namespace, service, strings.Join(serviceURL.PortNames, "\n"), strings.Join(serviceURL.URLs, "\n")})
}
PrintServiceList(os.Stdout, data)
}

if len(urls) == 0 {
if len(serviceURL.URLs) == 0 {
out.T(out.Sad, "service {{.namespace_name}}/{{.service_name}} has no node port", out.V{"namespace_name": namespace, "service_name": service})
return nil
}

for _, bareURLString := range urls {
for _, bareURLString := range serviceURL.URLs {
urlString, isHTTPSchemedURL := OptionallyHTTPSFormattedURLString(bareURLString, https)

if urlMode || !isHTTPSchemedURL {
Expand Down
16 changes: 9 additions & 7 deletions pkg/minikube/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,15 +280,15 @@ func TestPrintURLsForService(t *testing.T) {
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)
svcURL, err := printURLsForService(client, "127.0.0.1", test.serviceName, test.namespace, test.tmpl)
if err != nil && !test.err {
t.Errorf("Error: %v", err)
}
if err == nil && test.err {
t.Errorf("Expected error but got none")
}
if !reflect.DeepEqual(urls, test.expectedOutput) {
t.Errorf("\nExpected %v \nActual: %v \n\n", test.expectedOutput, urls)
if !reflect.DeepEqual(svcURL.URLs, test.expectedOutput) {
t.Errorf("\nExpected %v \nActual: %v \n\n", test.expectedOutput, svcURL.URLs)
}
})
}
Expand Down Expand Up @@ -384,16 +384,18 @@ func TestGetServiceURLs(t *testing.T) {
description: "correctly return serviceURLs",
namespace: "default",
api: defaultAPI,
expected: []URL{
expected: []SvcURL{
{
Namespace: "default",
Name: "mock-dashboard",
URLs: []string{"http://127.0.0.1:1111", "http://127.0.0.1:2222"},
PortNames: []string{"port1", "port2"},
},
{
Namespace: "default",
Name: "mock-dashboard-no-ports",
URLs: []string{},
PortNames: []string{},
},
},
},
Expand Down Expand Up @@ -477,15 +479,15 @@ func TestGetServiceURLsForService(t *testing.T) {
servicesMap: serviceNamespaces,
endpointsMap: endpointNamespaces,
}
urls, err := GetServiceURLsForService(test.api, test.namespace, test.service, defaultTemplate)
svcURL, err := GetServiceURLsForService(test.api, test.namespace, test.service, defaultTemplate)
if err != nil && !test.err {
t.Errorf("Error GetServiceURLsForService %v", 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)
if !reflect.DeepEqual(svcURL.URLs, test.expected) {
t.Errorf("URLs did not match, expected %+v \n\n got %+v", test.expected, svcURL.URLs)
}
})
}
Expand Down