Merge pull request #428 from aaron-prindle/dashboard-wait

Have working minikube service & minikube dashboard wait until ready functionality.
pull/375/merge
dlorenc 2016-08-11 15:48:45 -07:00 committed by GitHub
commit d799222e35
4 changed files with 148 additions and 6 deletions

View File

@ -19,13 +19,15 @@ package cmd
import (
"fmt"
"os"
"time"
"github.com/docker/machine/libmachine"
"github.com/golang/glog"
"github.com/pkg/browser"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/constants"
commonutil "k8s.io/minikube/pkg/util"
)
var (
@ -40,9 +42,20 @@ var dashboardCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
api := libmachine.NewClient(constants.Minipath, constants.MakeMiniPath("certs"))
defer api.Close()
url, err := cluster.GetServiceURL(api, "kube-system", "kubernetes-dashboard")
cluster.EnsureMinikubeRunningOrExit(api)
namespace := "kube-system"
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)
os.Exit(1)
}
url, err := cluster.GetServiceURL(api, namespace, service)
if err != nil {
glog.Errorln("Error accessing the kubernetes dashboard (is minikube running?): Error: ", err)
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, "Check that minikube is running.")
os.Exit(1)
}
if dashboardURLMode {
@ -51,6 +64,7 @@ var dashboardCmd = &cobra.Command{
fmt.Fprintln(os.Stdout, "Opening kubernetes dashboard in default browser...")
browser.OpenURL(url)
}
},
}

View File

@ -19,12 +19,16 @@ package cmd
import (
"fmt"
"os"
"time"
"github.com/docker/machine/libmachine"
"github.com/pkg/browser"
"github.com/spf13/cobra"
kubeApi "k8s.io/kubernetes/pkg/api"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/constants"
commonutil "k8s.io/minikube/pkg/util"
)
var (
@ -44,9 +48,15 @@ var serviceCmd = &cobra.Command{
}
service := args[0]
api := libmachine.NewClient(constants.Minipath, constants.MakeMiniPath("certs"))
defer api.Close()
cluster.EnsureMinikubeRunningOrExit(api)
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)
os.Exit(1)
}
url, err := cluster.GetServiceURL(api, namespace, service)
if err != nil {
fmt.Fprintln(os.Stderr, err)
@ -67,3 +77,31 @@ func init() {
serviceCmd.Flags().BoolVar(&serviceURLMode, "url", false, "Display the kubernetes service URL in the CLI instead of opening it in the default browser")
RootCmd.AddCommand(serviceCmd)
}
// 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 {
endpoints, err := cluster.GetKubernetesEndpointsWithNamespace(namespace)
if err != nil {
return err
}
endpoint, err := endpoints.Get(service)
if err != nil {
return err
}
return CheckEndpointReady(endpoint)
}
func CheckEndpointReady(endpoint *kubeApi.Endpoints) error {
if len(endpoint.Subsets) == 0 {
fmt.Fprintf(os.Stderr, "Waiting, endpoint for service is not ready yet...\n")
return fmt.Errorf("Endpoint for service is not ready yet\n")
}
for _, subset := range endpoint.Subsets {
if len(subset.NotReadyAddresses) != 0 {
fmt.Fprintf(os.Stderr, "Waiting, endpoint for service is not ready yet...\n")
return fmt.Errorf("Endpoint for service is not ready yet\n")
}
}
return nil
}

View File

@ -0,0 +1,55 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"testing"
kubeApi "k8s.io/kubernetes/pkg/api"
)
func TestCheckEndpointReady(t *testing.T) {
endpointNoSubsets := &kubeApi.Endpoints{}
if err := CheckEndpointReady(endpointNoSubsets); err == nil {
t.Fatalf("Endpoint had no subsets but CheckEndpointReady did not return an error")
}
endpointNotReady := &kubeApi.Endpoints{
Subsets: []kubeApi.EndpointSubset{
{Addresses: []kubeApi.EndpointAddress{
{IP: "1.1.1.1"},
{IP: "2.2.2.2"}},
NotReadyAddresses: []kubeApi.EndpointAddress{
{IP: "3.3.3.3"},
}}}}
if err := CheckEndpointReady(endpointNotReady); err == nil {
t.Fatalf("Endpoint had NotReadyAddresses but CheckEndpointReady did not return an error")
}
endpointReady := &kubeApi.Endpoints{
Subsets: []kubeApi.EndpointSubset{
{Addresses: []kubeApi.EndpointAddress{
{IP: "1.1.1.1"},
{IP: "2.2.2.2"},
},
NotReadyAddresses: []kubeApi.EndpointAddress{},
}},
}
if err := CheckEndpointReady(endpointReady); err != nil {
t.Fatalf("Endpoint was ready with no NotReadyAddresses but CheckEndpointReady returned an error")
}
}

View File

@ -507,8 +507,12 @@ type serviceGetter interface {
Get(name string) (*kubeApi.Service, error)
}
type endpointGetter interface {
Get(name string) (*kubeApi.Endpoints, error)
}
func getServicePort(namespace, service string) (int, error) {
services, err := getKubernetesServicesWithNamespace(namespace)
services, err := GetKubernetesServicesWithNamespace(namespace)
if err != nil {
return 0, err
}
@ -530,7 +534,7 @@ func getServicePortFromServiceGetter(services serviceGetter, service string) (in
return nodePort, nil
}
func getKubernetesServicesWithNamespace(namespace string) (serviceGetter, error) {
func GetKubernetesClient() (*unversioned.Client, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
configOverrides := &clientcmd.ConfigOverrides{}
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
@ -542,6 +546,37 @@ func getKubernetesServicesWithNamespace(namespace string) (serviceGetter, error)
if err != nil {
return nil, err
}
return client, nil
}
func GetKubernetesServicesWithNamespace(namespace string) (serviceGetter, error) {
client, err := GetKubernetesClient()
if err != nil {
return nil, err
}
services := client.Services(namespace)
return services, nil
}
func GetKubernetesEndpointsWithNamespace(namespace string) (endpointGetter, error) {
client, err := GetKubernetesClient()
if err != nil {
return nil, err
}
endpoints := client.Endpoints(namespace)
return endpoints, 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) {
s, err := GetHostStatus(api)
if err != nil {
glog.Errorln("Error getting machine status:", err)
os.Exit(1)
}
if s != state.Running.String() {
fmt.Fprintln(os.Stdout, "minikube is not currently running so the service cannot be accessed")
os.Exit(1)
}
}