From 12c5973fbf102d983f18344974b4d2d29810b0d0 Mon Sep 17 00:00:00 2001 From: Matt Rickard Date: Mon, 21 Nov 2016 15:01:20 -0800 Subject: [PATCH] Manual changes for k8s v1.5.0-beta.1 The internal k8s client has now been taken out of the kubernetes repo and is fully at k8s.io/client-go. Minikube should only reference the client-go library, while Localkube can continue to reference both internal k8s components as well as client-go. make gendocs --- cmd/minikube/cmd/service.go | 36 +++++++++++++ cmd/minikube/cmd/service_test.go | 55 +++++++++++++++++++ docs/minikube_start.md | 5 -- pkg/localkube/apiserver.go | 40 +++++++------- pkg/minikube/cluster/cluster.go | 40 +++++++------- pkg/minikube/cluster/cluster_test.go | 79 ++++++++++++++++------------ 6 files changed, 176 insertions(+), 79 deletions(-) create mode 100644 cmd/minikube/cmd/service_test.go diff --git a/cmd/minikube/cmd/service.go b/cmd/minikube/cmd/service.go index 103536f549..0c7bea6553 100644 --- a/cmd/minikube/cmd/service.go +++ b/cmd/minikube/cmd/service.go @@ -24,8 +24,10 @@ import ( "github.com/docker/machine/libmachine" "github.com/pkg/errors" "github.com/spf13/cobra" + "k8s.io/client-go/1.5/pkg/api/v1" "k8s.io/minikube/pkg/minikube/cluster" "k8s.io/minikube/pkg/minikube/constants" + "k8s.io/minikube/pkg/util" ) var ( @@ -93,3 +95,37 @@ func validateService(namespace string, service string) error { } return 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) +} + +const notReadyMsg = "Waiting, endpoint for service is not ready yet...\n" + +func CheckEndpointReady(endpoint *v1.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")} + } + 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 +} diff --git a/cmd/minikube/cmd/service_test.go b/cmd/minikube/cmd/service_test.go new file mode 100644 index 0000000000..7e2d395c78 --- /dev/null +++ b/cmd/minikube/cmd/service_test.go @@ -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" + + "k8s.io/client-go/1.5/pkg/api/v1" +) + +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") + } + + endpointNotReady := &v1.Endpoints{ + Subsets: []v1.EndpointSubset{ + {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") + } + + 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") + } +} diff --git a/docs/minikube_start.md b/docs/minikube_start.md index 20afa0cd0e..fa06b96054 100644 --- a/docs/minikube_start.md +++ b/docs/minikube_start.md @@ -26,13 +26,8 @@ minikube start --hyperv-virtual-switch string The hyperv virtual switch name. Defaults to first found. (only supported with HyperV driver) --insecure-registry stringSlice Insecure Docker registries to pass to the Docker daemon --iso-url string Location of the minikube iso (default "https://storage.googleapis.com/minikube/minikube-0.7.iso") -<<<<<<< HEAD --kubernetes-version string The kubernetes version that the minikube VM will use (ex: v1.2.3) - OR a URI which contains a localkube binary (ex: https://storage.googleapis.com/minikube/k8sReleases/v1.3.0/localkube-linux-amd64) (default "v1.5.0-alpha.2") -======= - --kubernetes-version string The kubernetes version that the minikube VM will (ex: v1.2.3) OR a URI which contains a localkube binary (ex: https://storage.googleapis.com/minikube/k8sReleases/v1.3.0/localkube-linux-amd64) (default "v1.5.0-beta.1") ->>>>>>> 54df7ea... Update to k8s v1.5.0-beta.1 --kvm-network string The KVM network name. (only supported with KVM driver) (default "default") --memory int Amount of RAM allocated to the minikube VM (default 2048) --network-plugin string The name of the network plugin diff --git a/pkg/localkube/apiserver.go b/pkg/localkube/apiserver.go index 25e5c0d65c..472731003b 100644 --- a/pkg/localkube/apiserver.go +++ b/pkg/localkube/apiserver.go @@ -19,11 +19,11 @@ package localkube import ( "strings" + "k8s.io/client-go/1.5/kubernetes" + "k8s.io/client-go/1.5/rest" apiserver "k8s.io/kubernetes/cmd/kube-apiserver/app" "k8s.io/kubernetes/cmd/kube-apiserver/app/options" - kuberest "k8s.io/kubernetes/pkg/client/restclient" - kubeclient "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/storage/storagebackend" ) @@ -32,32 +32,32 @@ func (lk LocalkubeServer) NewAPIServer() Server { } func StartAPIServer(lk LocalkubeServer) func() error { - config := options.NewAPIServer() + config := options.NewServerRunOptions() - config.BindAddress = lk.APIServerAddress - config.SecurePort = lk.APIServerPort - config.InsecureBindAddress = lk.APIServerInsecureAddress - config.InsecurePort = lk.APIServerInsecurePort + config.GenericServerRunOptions.BindAddress = lk.APIServerAddress + config.GenericServerRunOptions.SecurePort = lk.APIServerPort + config.GenericServerRunOptions.InsecureBindAddress = lk.APIServerInsecureAddress + config.GenericServerRunOptions.InsecurePort = lk.APIServerInsecurePort - config.ClientCAFile = lk.GetCAPublicKeyCertPath() - config.TLSCertFile = lk.GetPublicKeyCertPath() - config.TLSPrivateKeyFile = lk.GetPrivateKeyCertPath() - config.AdmissionControl = "NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota" + config.GenericServerRunOptions.ClientCAFile = lk.GetCAPublicKeyCertPath() + config.GenericServerRunOptions.TLSCertFile = lk.GetPublicKeyCertPath() + config.GenericServerRunOptions.TLSPrivateKeyFile = lk.GetPrivateKeyCertPath() + config.GenericServerRunOptions.AdmissionControl = "NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota" // use localkube etcd - config.StorageConfig = storagebackend.Config{ServerList: KubeEtcdClientURLs} + config.GenericServerRunOptions.StorageConfig = storagebackend.Config{ServerList: KubeEtcdClientURLs} // set Service IP range - config.ServiceClusterIPRange = lk.ServiceClusterIPRange + config.GenericServerRunOptions.ServiceClusterIPRange = lk.ServiceClusterIPRange // defaults from apiserver command - config.EnableProfiling = true - config.EnableWatchCache = true - config.MinRequestTimeout = 1800 + config.GenericServerRunOptions.EnableProfiling = true + config.GenericServerRunOptions.EnableWatchCache = true + config.GenericServerRunOptions.MinRequestTimeout = 1800 config.AllowPrivileged = true - config.RuntimeConfig = lk.RuntimeConfig + config.GenericServerRunOptions.RuntimeConfig = lk.RuntimeConfig lk.SetExtraConfigForComponent("apiserver", &config) @@ -74,11 +74,11 @@ func notFoundErr(err error) bool { return strings.HasSuffix(err.Error(), "not found") } -func kubeClient() *kubeclient.Client { - config := &kuberest.Config{ +func kubeClient() *kubernetes.Clientset { + config := &rest.Config{ Host: "http://localhost:8080", // TODO: Make configurable } - client, err := kubeclient.New(config) + client, err := kubernetes.NewForConfig(config) if err != nil { panic(err) } diff --git a/pkg/minikube/cluster/cluster.go b/pkg/minikube/cluster/cluster.go index afbf7b5a7d..11e46bfa04 100644 --- a/pkg/minikube/cluster/cluster.go +++ b/pkg/minikube/cluster/cluster.go @@ -41,10 +41,12 @@ import ( download "github.com/jimmidyson/go-download" "github.com/pkg/browser" "github.com/pkg/errors" - kubeapi "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" - "k8s.io/kubernetes/pkg/labels" + "k8s.io/client-go/1.5/kubernetes" + 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/pkg/labels" + "k8s.io/client-go/1.5/tools/clientcmd" "k8s.io/minikube/pkg/minikube/assets" "k8s.io/minikube/pkg/minikube/constants" @@ -536,7 +538,7 @@ func GetServiceURLsForService(api libmachine.API, namespace, service string, t * return getServiceURLsWithClient(client, ip, namespace, service, t) } -func getServiceURLsWithClient(client *unversioned.Client, ip, namespace, service string, t *template.Template) ([]string, error) { +func getServiceURLsWithClient(client *kubernetes.Clientset, ip, namespace, service string, t *template.Template) ([]string, error) { if t == nil { return nil, errors.New("Error, attempted to generate service url with nil --format template") } @@ -565,24 +567,24 @@ func getServiceURLsWithClient(client *unversioned.Client, ip, namespace, service } type serviceGetter interface { - Get(name string) (*kubeapi.Service, error) - List(kubeapi.ListOptions) (*kubeapi.ServiceList, error) + Get(name string) (*v1.Service, error) + List(kubeapi.ListOptions) (*v1.ServiceList, error) } -func getServicePorts(client *unversioned.Client, namespace, service string) ([]int32, error) { +func getServicePorts(client *kubernetes.Clientset, namespace, service string) ([]int32, error) { services := client.Services(namespace) return getServicePortsFromServiceGetter(services, service) } type MissingNodePortError struct { - service *kubeapi.Service + 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) } -func getServiceFromServiceGetter(services serviceGetter, service string) (*kubeapi.Service, error) { +func getServiceFromServiceGetter(services serviceGetter, service string) (*v1.Service, error) { svc, err := services.Get(service) if err != nil { return nil, fmt.Errorf("Error getting %s service: %s", service, err) @@ -609,7 +611,7 @@ func getServicePortsFromServiceGetter(services serviceGetter, service string) ([ return nodePorts, nil } -func GetKubernetesClient() (*unversioned.Client, error) { +func GetKubernetesClient() (*kubernetes.Clientset, error) { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() configOverrides := &clientcmd.ConfigOverrides{} kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) @@ -617,7 +619,7 @@ func GetKubernetesClient() (*unversioned.Client, error) { if err != nil { return nil, fmt.Errorf("Error creating kubeConfig: %s", err) } - client, err := unversioned.New(config) + client, err := kubernetes.NewForConfig(config) if err != nil { return nil, errors.Wrap(err, "Error creating new client from kubeConfig.ClientConfig()") } @@ -704,7 +706,7 @@ func CheckService(namespace string, service string) error { return checkEndpointReady(endpoint) } -func checkEndpointReady(endpoint *kubeapi.Endpoints) error { +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) @@ -744,26 +746,26 @@ func WaitAndMaybeOpenService(api libmachine.API, namespace string, service strin } } -func GetServiceListByLabel(namespace string, key string, value string) (*kubeapi.ServiceList, error) { +func GetServiceListByLabel(namespace string, key string, value string) (*v1.ServiceList, error) { client, err := GetKubernetesClient() if err != nil { - return &kubeapi.ServiceList{}, &util.RetriableError{Err: err} + return &v1.ServiceList{}, &util.RetriableError{Err: err} } services := client.Services(namespace) if err != nil { - return &kubeapi.ServiceList{}, &util.RetriableError{Err: err} + return &v1.ServiceList{}, &util.RetriableError{Err: err} } return getServiceListFromServicesByLabel(services, key, value) } -func getServiceListFromServicesByLabel(services unversioned.ServiceInterface, key string, value string) (*kubeapi.ServiceList, error) { +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 &kubeapi.ServiceList{}, &util.RetriableError{Err: err} + return &v1.ServiceList{}, &util.RetriableError{Err: err} } if len(serviceList.Items) == 0 { - return &kubeapi.ServiceList{}, &util.RetriableError{Err: err} + return &v1.ServiceList{}, &util.RetriableError{Err: err} } return serviceList, nil } diff --git a/pkg/minikube/cluster/cluster_test.go b/pkg/minikube/cluster/cluster_test.go index 9b445d3d5d..0bc14c1d16 100644 --- a/pkg/minikube/cluster/cluster_test.go +++ b/pkg/minikube/cluster/cluster_test.go @@ -32,10 +32,11 @@ import ( "github.com/docker/machine/libmachine/provision" "github.com/docker/machine/libmachine/state" "github.com/pkg/errors" - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/client/restclient" - "k8s.io/kubernetes/pkg/watch" + "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/client-go/1.5/pkg/watch" + "k8s.io/client-go/1.5/rest" "k8s.io/minikube/pkg/minikube/assets" "k8s.io/minikube/pkg/minikube/constants" "k8s.io/minikube/pkg/minikube/tests" @@ -524,16 +525,16 @@ func TestCreateSSHShell(t *testing.T) { } type MockServiceGetter struct { - services map[string]api.Service + services map[string]v1.Service } func NewMockServiceGetter() *MockServiceGetter { return &MockServiceGetter{ - services: make(map[string]api.Service), + services: make(map[string]v1.Service), } } -func (mockServiceGetter *MockServiceGetter) Get(name string) (*api.Service, error) { +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) @@ -541,8 +542,8 @@ func (mockServiceGetter *MockServiceGetter) Get(name string) (*api.Service, erro return &service, nil } -func (mockServiceGetter *MockServiceGetter) List(options api.ListOptions) (*api.ServiceList, error) { - services := api.ServiceList{ +func (mockServiceGetter *MockServiceGetter) List(options api.ListOptions) (*v1.ServiceList, error) { + services := v1.ServiceList{ TypeMeta: unversioned.TypeMeta{Kind: "ServiceList", APIVersion: "v1"}, ListMeta: unversioned.ListMeta{}, } @@ -556,9 +557,9 @@ func (mockServiceGetter *MockServiceGetter) List(options api.ListOptions) (*api. func TestGetServiceURLs(t *testing.T) { mockServiceGetter := NewMockServiceGetter() expected := []int32{1111, 2222} - mockDashboardService := api.Service{ - Spec: api.ServiceSpec{ - Ports: []api.ServicePort{ + mockDashboardService := v1.Service{ + Spec: v1.ServiceSpec{ + Ports: []v1.ServicePort{ { NodePort: expected[0], }, { @@ -581,7 +582,7 @@ func TestGetServiceURLs(t *testing.T) { func TestGetServiceURLWithoutNodePort(t *testing.T) { mockServiceGetter := NewMockServiceGetter() - mockDashboardService := api.Service{} + mockDashboardService := v1.Service{} mockServiceGetter.services["mock-service"] = mockDashboardService _, err := getServicePortsFromServiceGetter(mockServiceGetter, "mock-service") @@ -783,15 +784,15 @@ func TestUpdateCustomAddons(t *testing.T) { } func TestCheckEndpointReady(t *testing.T) { - endpointNoSubsets := &api.Endpoints{} + endpointNoSubsets := &v1.Endpoints{} if err := checkEndpointReady(endpointNoSubsets); err == nil { t.Fatalf("Endpoint had no subsets but checkEndpointReady did not return an error") } - endpointNotReady := &api.Endpoints{ - Subsets: []api.EndpointSubset{ - {Addresses: []api.EndpointAddress{}, - NotReadyAddresses: []api.EndpointAddress{ + endpointNotReady := &v1.Endpoints{ + Subsets: []v1.EndpointSubset{ + {Addresses: []v1.EndpointAddress{}, + NotReadyAddresses: []v1.EndpointAddress{ {IP: "1.1.1.1"}, {IP: "2.2.2.2"}, {IP: "3.3.3.3"}, @@ -800,13 +801,13 @@ func TestCheckEndpointReady(t *testing.T) { t.Fatalf("Endpoint had no Addresses but checkEndpointReady did not return an error") } - endpointReady := &api.Endpoints{ - Subsets: []api.EndpointSubset{ - {Addresses: []api.EndpointAddress{ + endpointReady := &v1.Endpoints{ + Subsets: []v1.EndpointSubset{ + {Addresses: []v1.EndpointAddress{ {IP: "1.1.1.1"}, {IP: "2.2.2.2"}, }, - NotReadyAddresses: []api.EndpointAddress{}, + NotReadyAddresses: []v1.EndpointAddress{}, }}, } if err := checkEndpointReady(endpointReady); err != nil { @@ -815,12 +816,12 @@ func TestCheckEndpointReady(t *testing.T) { } type ServiceInterfaceMock struct { - ServiceList *api.ServiceList + ServiceList *v1.ServiceList } -func (s ServiceInterfaceMock) List(opts api.ListOptions) (*api.ServiceList, error) { - serviceList := &api.ServiceList{ - Items: []api.Service{}, +func (s ServiceInterfaceMock) 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 { @@ -830,33 +831,41 @@ func (s ServiceInterfaceMock) List(opts api.ListOptions) (*api.ServiceList, erro } return serviceList, nil } -func (s ServiceInterfaceMock) Get(name string) (*api.Service, error) { +func (s ServiceInterfaceMock) Get(name string) (*v1.Service, error) { return nil, nil } -func (s ServiceInterfaceMock) Create(*api.Service) (*api.Service, error) { +func (s ServiceInterfaceMock) Create(*v1.Service) (*v1.Service, error) { return nil, nil } -func (s ServiceInterfaceMock) Update(*api.Service) (*api.Service, error) { +func (s ServiceInterfaceMock) Update(*v1.Service) (*v1.Service, error) { return nil, nil } -func (s ServiceInterfaceMock) UpdateStatus(*api.Service) (*api.Service, error) { +func (s ServiceInterfaceMock) UpdateStatus(*v1.Service) (*v1.Service, error) { return nil, nil } -func (s ServiceInterfaceMock) Delete(name string) error { +func (s ServiceInterfaceMock) Delete(string, *api.DeleteOptions) error { return nil } func (s ServiceInterfaceMock) Watch(opts api.ListOptions) (watch.Interface, error) { return nil, nil } -func (s ServiceInterfaceMock) ProxyGet(scheme, name, port, path string, params map[string]string) restclient.ResponseWrapper { +func (s ServiceInterfaceMock) ProxyGet(scheme, name, port, path string, params map[string]string) rest.ResponseWrapper { return nil } +func (s ServiceInterfaceMock) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error { + return nil +} + +func (s ServiceInterfaceMock) Patch(name string, pt api.PatchType, data []byte, subresources ...string) (result *v1.Service, err error) { + return nil, nil +} + func TestGetServiceListFromServicesByLabel(t *testing.T) { - serviceList := &api.ServiceList{ - Items: []api.Service{ + serviceList := &v1.ServiceList{ + Items: []v1.Service{ { - Spec: api.ServiceSpec{ + Spec: v1.ServiceSpec{ Selector: map[string]string{ "foo": "bar", },