Merge pull request #720 from jimmidyson/service-format
Add format flag to service commandpull/723/head
commit
f16658c5ba
|
@ -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)
|
||||
|
|
|
@ -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")}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue