Merge pull request #720 from jimmidyson/service-format

Add format flag to service command
pull/723/head
dlorenc 2016-10-19 17:24:39 -07:00 committed by GitHub
commit f16658c5ba
4 changed files with 96 additions and 42 deletions

View File

@ -24,7 +24,7 @@ import (
"github.com/docker/machine/libmachine"
"github.com/pkg/browser"
"github.com/spf13/cobra"
cmdUtil "k8s.io/minikube/cmd/util"
cmdutil "k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/constants"
@ -49,15 +49,15 @@ var dashboardCmd = &cobra.Command{
service := "kubernetes-dashboard"
if err := commonutil.RetryAfter(20, func() error { return CheckService(namespace, service) }, 6*time.Second); err != nil {
fmt.Fprintln(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s", service, err)
cmdUtil.MaybeReportErrorAndExit(err)
fmt.Fprintf(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s\n", service, err)
cmdutil.MaybeReportErrorAndExit(err)
}
url, err := cluster.GetServiceURL(api, namespace, service)
url, err := cluster.GetServiceURL(api, namespace, service, nil)
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, "Check that minikube is running.")
cmdUtil.MaybeReportErrorAndExit(err)
cmdutil.MaybeReportErrorAndExit(err)
}
if dashboardURLMode {
fmt.Fprintln(os.Stdout, url)

View File

@ -20,23 +20,26 @@ import (
"fmt"
"os"
"strings"
"text/template"
"time"
"github.com/docker/machine/libmachine"
"github.com/pkg/browser"
"github.com/pkg/errors"
"github.com/spf13/cobra"
kubeApi "k8s.io/kubernetes/pkg/api"
cmdUtil "k8s.io/minikube/cmd/util"
kubeapi "k8s.io/kubernetes/pkg/api"
cmdutil "k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/util"
)
var (
namespace string
https bool
serviceURLMode bool
namespace string
https bool
serviceURLMode bool
serviceURLFormat string
serviceURLTemplate *template.Template
)
// serviceCmd represents the service command
@ -44,11 +47,19 @@ var serviceCmd = &cobra.Command{
Use: "service [flags] SERVICE",
Short: "Gets the kubernetes URL for the specified service in your local cluster",
Long: `Gets the kubernetes URL for the specified service in your local cluster`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
t, err := template.New("serviceURL").Parse(serviceURLFormat)
if err != nil {
fmt.Fprintln(os.Stderr, "The value passed to --format is invalid:\n\n", err)
os.Exit(1)
}
serviceURLTemplate = t
},
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 || len(args) > 1 {
errText := "Please specify a service name."
fmt.Fprintln(os.Stderr, errText)
cmdUtil.MaybeReportErrorAndExit(errors.New(errText))
cmdutil.MaybeReportErrorAndExit(errors.New(errText))
}
service := args[0]
@ -57,15 +68,15 @@ var serviceCmd = &cobra.Command{
cluster.EnsureMinikubeRunningOrExit(api, 1)
if err := util.RetryAfter(20, func() error { return CheckService(namespace, service) }, 6*time.Second); err != nil {
fmt.Fprintln(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s", service, err)
cmdUtil.MaybeReportErrorAndExit(err)
fmt.Fprintf(os.Stderr, "Could not find finalized endpoint being pointed to by %s: %s\n", service, err)
cmdutil.MaybeReportErrorAndExit(err)
}
url, err := cluster.GetServiceURL(api, namespace, service)
url, err := cluster.GetServiceURL(api, namespace, service, serviceURLTemplate)
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).")
cmdUtil.MaybeReportErrorAndExit(err)
cmdutil.MaybeReportErrorAndExit(err)
}
if https {
url = strings.Replace(url, "http", "https", 1)
@ -83,6 +94,9 @@ func init() {
serviceCmd.Flags().StringVarP(&namespace, "namespace", "n", "default", "The service namespace")
serviceCmd.Flags().BoolVar(&serviceURLMode, "url", false, "Display the kubernetes service URL in the CLI instead of opening it in the default browser")
serviceCmd.Flags().BoolVar(&https, "https", false, "Open the service URL with https instead of http")
serviceCmd.PersistentFlags().StringVar(&serviceURLFormat, "format", "http://{{.IP}}:{{.Port}}", "Format to output service URL in")
RootCmd.AddCommand(serviceCmd)
}
@ -102,7 +116,7 @@ func CheckService(namespace string, service string) error {
const notReadyMsg = "Waiting, endpoint for service is not ready yet...\n"
func CheckEndpointReady(endpoint *kubeApi.Endpoints) error {
func CheckEndpointReady(endpoint *kubeapi.Endpoints) error {
if len(endpoint.Subsets) == 0 {
fmt.Fprintf(os.Stderr, notReadyMsg)
return &util.RetriableError{Err: errors.New("Endpoint for service is not ready yet")}

View File

@ -14,6 +14,7 @@ minikube service [flags] SERVICE
### Options
```
--format string Format to output service URL in (default "http://{{.IP}}:{{.Port}}")
--https Open the service URL with https instead of http
-n, --namespace string The service namespace (default "default")
--url Display the kubernetes service URL in the CLI instead of opening it in the default browser

View File

@ -30,7 +30,9 @@ import (
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"text/template"
"time"
"github.com/docker/machine/drivers/virtualbox"
@ -41,7 +43,7 @@ import (
"github.com/docker/machine/libmachine/state"
"github.com/golang/glog"
"github.com/pkg/errors"
kubeApi "k8s.io/kubernetes/pkg/api"
kubeapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
@ -545,7 +547,12 @@ func CreateSSHShell(api libmachine.API, args []string) error {
return client.Shell(strings.Join(args, " "))
}
func GetServiceURL(api libmachine.API, namespace, service string) (string, error) {
type ipPort struct {
IP string
Port int
}
func GetServiceURL(api libmachine.API, namespace, service string, t *template.Template) (string, error) {
host, err := CheckIfApiExistsAndLoad(api)
if err != nil {
return "", errors.Wrap(err, "Error checking if api exist and loading it")
@ -556,52 +563,89 @@ func GetServiceURL(api libmachine.API, namespace, service string) (string, error
return "", errors.Wrap(err, "Error getting ip from host")
}
port, err := getServicePort(namespace, service)
client, err := getKubernetesClient()
if err != nil {
return "", errors.Wrapf(err, "Error getting service port from %s, %s", namespace, service)
return "", err
}
return fmt.Sprintf("http://%s:%d", ip, port), nil
return getServiceURLWithClient(client, ip, namespace, service, t)
}
func getServiceURLWithClient(client *unversioned.Client, ip, namespace, service string, t *template.Template) (string, error) {
port, err := getServicePort(client, namespace, service)
if err != nil {
return "", err
}
if t == nil {
return fmt.Sprintf("http://%s", net.JoinHostPort(ip, strconv.Itoa(port))), nil
}
var doc bytes.Buffer
err = t.Execute(&doc, ipPort{ip, port})
if err != nil {
return "", err
}
u, err := url.Parse(doc.String())
if err != nil {
return "", err
}
return u.String(), nil
}
type serviceGetter interface {
Get(name string) (*kubeApi.Service, error)
Get(name string) (*kubeapi.Service, error)
}
type endpointGetter interface {
Get(name string) (*kubeApi.Endpoints, error)
Get(name string) (*kubeapi.Endpoints, error)
}
func getServicePort(namespace, service string) (int, error) {
services, err := GetKubernetesServicesWithNamespace(namespace)
if err != nil {
return 0, errors.Wrapf(err, "Error getting kubernetes service with namespace", namespace)
}
func getServicePort(client *unversioned.Client, namespace, service string) (int, error) {
services := getKubernetesServicesWithNamespace(client, namespace)
return getServicePortFromServiceGetter(services, service)
}
func getServicePortFromServiceGetter(services serviceGetter, service string) (int, error) {
type MissingNodePortError struct {
service *kubeapi.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)
}
func getServiceFromServiceGetter(services serviceGetter, service string) (*kubeapi.Service, error) {
svc, err := services.Get(service)
if err != nil {
return 0, errors.Wrapf(err, "Error getting %s service: %s", service)
return nil, fmt.Errorf("Error getting %s service: %s", service, err)
}
return svc, nil
}
func getServicePortFromServiceGetter(services serviceGetter, service string) (int, error) {
svc, err := getServiceFromServiceGetter(services, service)
if err != nil {
return 0, err
}
nodePort := 0
if len(svc.Spec.Ports) > 0 {
nodePort = int(svc.Spec.Ports[0].NodePort)
}
if nodePort == 0 {
return 0, errors.Errorf("Service %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.", service, svc.Spec.Type)
return 0, MissingNodePortError{svc}
}
return nodePort, nil
}
func GetKubernetesClient() (*unversioned.Client, error) {
func getKubernetesClient() (*unversioned.Client, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
configOverrides := &clientcmd.ConfigOverrides{CurrentContext: constants.MinikubeContext}
configOverrides := &clientcmd.ConfigOverrides{}
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
config, err := kubeConfig.ClientConfig()
if err != nil {
return nil, errors.Wrap(err, "Error creating kubeConfig: %s")
return nil, fmt.Errorf("Error creating kubeConfig: %s", err)
}
client, err := unversioned.New(config)
if err != nil {
@ -610,17 +654,12 @@ func GetKubernetesClient() (*unversioned.Client, error) {
return client, nil
}
func GetKubernetesServicesWithNamespace(namespace string) (serviceGetter, error) {
client, err := GetKubernetesClient()
if err != nil {
return nil, errors.Wrap(err, "Error getting kubernetes client")
}
services := client.Services(namespace)
return services, nil
func getKubernetesServicesWithNamespace(client *unversioned.Client, namespace string) serviceGetter {
return client.Services(namespace)
}
func GetKubernetesEndpointsWithNamespace(namespace string) (endpointGetter, error) {
client, err := GetKubernetesClient()
client, err := getKubernetesClient()
if err != nil {
return nil, errors.Wrap(err, "Error getting kubernetes client")
}