From 5483333e29e0b881beea76b94bcee11c4bf169bf Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Thu, 11 Sep 2014 19:01:29 -0400 Subject: [PATCH] Allow server and client to take api version as argument * Defaults to v1beta1 * apiserver takes -storage_version which controls etcd storage version and the version of the client used to connect to other apiservers * Changed signature of client.New to add version parameter * All controller code and component code prefers the oldest (most common) server version --- cmd/apiserver/apiserver.go | 16 ++- cmd/controller-manager/controller-manager.go | 3 +- cmd/integration/integration.go | 18 ++- cmd/kubecfg/kubecfg.go | 19 +-- cmd/proxy/proxy.go | 3 +- pkg/api/latest/doc.go | 3 +- pkg/api/latest/latest.go | 33 ++++- pkg/api/latest/latest_test.go | 11 ++ pkg/client/client.go | 26 +++- pkg/client/client_test.go | 46 ++++++- pkg/client/request_test.go | 49 +++---- pkg/controller/replication_controller_test.go | 12 +- pkg/kubecfg/parse.go | 6 +- pkg/kubecfg/parse_test.go | 20 +-- pkg/master/master.go | 31 +++-- pkg/registry/etcd/etcd.go | 9 +- pkg/registry/etcd/etcd_test.go | 2 +- pkg/service/endpoints_controller_test.go | 8 +- plugin/cmd/scheduler/scheduler.go | 3 +- plugin/pkg/scheduler/factory/factory_test.go | 12 +- test/integration/client_test.go | 129 ++++++++++-------- test/integration/utils.go | 14 ++ 22 files changed, 309 insertions(+), 164 deletions(-) diff --git a/cmd/apiserver/apiserver.go b/cmd/apiserver/apiserver.go index 21142befa3..9db30ed931 100644 --- a/cmd/apiserver/apiserver.go +++ b/cmd/apiserver/apiserver.go @@ -38,15 +38,16 @@ import ( ) var ( - port = flag.Uint("port", 8080, "The port to listen on. Default 8080.") + port = flag.Uint("port", 8080, "The port to listen on. Default 8080") address = flag.String("address", "127.0.0.1", "The address on the local server to listen to. Default 127.0.0.1") apiPrefix = flag.String("api_prefix", "/api", "The prefix for API requests on the server. Default '/api'") + storageVersion = flag.String("storage_version", "", "The version to store resources with. Defaults to server preferred") cloudProvider = flag.String("cloud_provider", "", "The provider for cloud services. Empty string for no provider.") cloudConfigFile = flag.String("cloud_config", "", "The path to the cloud provider configuration file. Empty string for no configuration file.") minionRegexp = flag.String("minion_regexp", "", "If non empty, and -cloud_provider is specified, a regular expression for matching minion VMs") minionPort = flag.Uint("minion_port", 10250, "The port at which kubelet will be listening on the minions.") - healthCheckMinions = flag.Bool("health_check_minions", true, "If true, health check minions and filter unhealthy ones. [default true]") - minionCacheTTL = flag.Duration("minion_cache_ttl", 30*time.Second, "Duration of time to cache minion information. [default 30 seconds]") + healthCheckMinions = flag.Bool("health_check_minions", true, "If true, health check minions and filter unhealthy ones. Default true") + minionCacheTTL = flag.Duration("minion_cache_ttl", 30*time.Second, "Duration of time to cache minion information. Default 30 seconds") etcdServerList util.StringList machineList util.StringList corsAllowedOriginList util.StringList @@ -125,15 +126,20 @@ func main() { Port: *minionPort, } - client, err := client.New(net.JoinHostPort(*address, strconv.Itoa(int(*port))), nil) + client, err := client.New(net.JoinHostPort(*address, strconv.Itoa(int(*port))), *storageVersion, nil) if err != nil { glog.Fatalf("Invalid server address: %v", err) } + helper, err := master.NewEtcdHelper(etcdServerList, *storageVersion) + if err != nil { + glog.Fatalf("Invalid storage version: %v", err) + } + m := master.New(&master.Config{ Client: client, Cloud: cloud, - EtcdServers: etcdServerList, + EtcdHelper: helper, HealthCheckMinions: *healthCheckMinions, Minions: machineList, MinionCacheTTL: *minionCacheTTL, diff --git a/cmd/controller-manager/controller-manager.go b/cmd/controller-manager/controller-manager.go index 5364f65821..835def8314 100644 --- a/cmd/controller-manager/controller-manager.go +++ b/cmd/controller-manager/controller-manager.go @@ -27,6 +27,7 @@ import ( "strconv" "time" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/controller" _ "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz" @@ -53,7 +54,7 @@ func main() { glog.Fatal("usage: controller-manager -master ") } - kubeClient, err := client.New(*master, nil) + kubeClient, err := client.New(*master, latest.OldestVersion, nil) if err != nil { glog.Fatalf("Invalid -master: %v", err) } diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index 18c2fcdb86..d66727156e 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -106,19 +106,27 @@ func startComponents(manifestURL string) (apiServerURL string) { } } - cl := client.NewOrDie(apiServer.URL, nil) + cl := client.NewOrDie(apiServer.URL, "", nil) cl.PollPeriod = time.Second * 1 cl.Sync = true + helper, err := master.NewEtcdHelper(servers, "") + if err != nil { + glog.Fatalf("Unable to get etcd helper: %v", err) + } + // Master m := master.New(&master.Config{ Client: cl, - EtcdServers: servers, + EtcdHelper: helper, Minions: machineList, PodInfoGetter: fakePodInfoGetter{}, }) - storage, codec := m.API_v1beta1() - handler.delegate = apiserver.Handle(storage, codec, "/api/v1beta1") + mux := http.NewServeMux() + apiserver.NewAPIGroup(m.API_v1beta1()).InstallREST(mux, "/api/v1beta1") + apiserver.NewAPIGroup(m.API_v1beta2()).InstallREST(mux, "/api/v1beta2") + apiserver.InstallSupport(mux) + handler.delegate = mux // Scheduler scheduler.New((&factory.ConfigFactory{cl}).Create()).Run() @@ -303,7 +311,7 @@ func main() { // Wait for the synchronization threads to come up. time.Sleep(time.Second * 10) - kubeClient := client.NewOrDie(apiServerURL, nil) + kubeClient := client.NewOrDie(apiServerURL, "", nil) // Run tests in parallel testFuncs := []testFunc{ diff --git a/cmd/kubecfg/kubecfg.go b/cmd/kubecfg/kubecfg.go index 83b45c99d6..efe3c7a732 100644 --- a/cmd/kubecfg/kubecfg.go +++ b/cmd/kubecfg/kubecfg.go @@ -58,6 +58,7 @@ var ( templateFile = flag.String("template_file", "", "If present, load this file as a golang template and use it for output printing") templateStr = flag.String("template", "", "If present, parse this string as a golang template and use it for output printing") imageName = flag.String("image", "", "Image used when updating a replicationController. Will apply to the first container in the pod template.") + apiVersion = flag.String("api_version", latest.Version, "The version of the API to use against this server.") ) var parser = kubecfg.NewParser(map[string]runtime.Object{ @@ -125,12 +126,12 @@ func readConfigData() []byte { // readConfig reads and parses pod, replicationController, and service // configuration files. If any errors log and exit non-zero. -func readConfig(storage string) []byte { +func readConfig(storage string, serverCodec runtime.Codec) []byte { if len(*config) == 0 { glog.Fatal("Need config file (-c)") } - data, err := parser.ToWireFormat(readConfigData(), storage, latest.Codec) + data, err := parser.ToWireFormat(readConfigData(), storage, latest.Codec, serverCodec) if err != nil { glog.Fatalf("Error parsing %v as an object for %v: %v\n", *config, storage, err) @@ -160,9 +161,9 @@ func main() { } else { masterServer = "http://localhost:8080" } - kubeClient, err := client.New(masterServer, nil) + kubeClient, err := client.New(masterServer, *apiVersion, nil) if err != nil { - glog.Fatalf("Unable to parse %s as a URL: %v", masterServer, err) + glog.Fatalf("Can't configure client: %v", err) } // TODO: this won't work if TLS is enabled with client cert auth, but no @@ -172,9 +173,9 @@ func main() { if err != nil { glog.Fatalf("Error loading auth: %v", err) } - kubeClient, err = client.New(masterServer, auth) + kubeClient, err = client.New(masterServer, *apiVersion, auth) if err != nil { - glog.Fatalf("Unable to parse %s as a URL: %v", masterServer, err) + glog.Fatalf("Can't configure client: %v", err) } } @@ -296,7 +297,7 @@ func executeAPIRequest(method string, c *client.Client) bool { ParseSelectorParam("labels", *selector) if setBody { if version != 0 { - data := readConfig(storage) + data := readConfig(storage, c.RESTClient.Codec) obj, err := latest.Codec.Decode(data) if err != nil { glog.Fatalf("error setting resource version: %v", err) @@ -306,13 +307,13 @@ func executeAPIRequest(method string, c *client.Client) bool { glog.Fatalf("error setting resource version: %v", err) } jsonBase.SetResourceVersion(version) - data, err = latest.Codec.Encode(obj) + data, err = c.RESTClient.Codec.Encode(obj) if err != nil { glog.Fatalf("error setting resource version: %v", err) } r.Body(data) } else { - r.Body(readConfig(storage)) + r.Body(readConfig(storage, c.RESTClient.Codec)) } } result := r.Do() diff --git a/cmd/proxy/proxy.go b/cmd/proxy/proxy.go index 2a17601ba9..1a4e6a4100 100644 --- a/cmd/proxy/proxy.go +++ b/cmd/proxy/proxy.go @@ -20,6 +20,7 @@ import ( "flag" "time" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy" "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy/config" @@ -54,7 +55,7 @@ func main() { if *master != "" { glog.Infof("Using api calls to get config %v", *master) //TODO: add auth info - client, err := client.New(*master, nil) + client, err := client.New(*master, latest.OldestVersion, nil) if err != nil { glog.Fatalf("Invalid -master: %v", err) } diff --git a/pkg/api/latest/doc.go b/pkg/api/latest/doc.go index bb76f61a03..e7a6c2b43c 100644 --- a/pkg/api/latest/doc.go +++ b/pkg/api/latest/doc.go @@ -16,5 +16,6 @@ limitations under the License. // Package latest defines the default output serializations that code should // use and imports the required schemas. It also ensures all previously known -// and supported API versions are available for conversion. +// and supported API versions are available for conversion. Consumers may +// import this package in lieu of importing individual versions. package latest diff --git a/pkg/api/latest/latest.go b/pkg/api/latest/latest.go index 2c835101b7..cdd0704d6a 100644 --- a/pkg/api/latest/latest.go +++ b/pkg/api/latest/latest.go @@ -17,13 +17,26 @@ limitations under the License. package latest import ( + "fmt" + "strings" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" - _ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) -// Version is the string that represents the current external default version -var Version = "v1beta1" +// Version is the string that represents the current external default version. +const Version = "v1beta1" + +// OldestVersion is the string that represents the oldest server version supported, +// for client code that wants to hardcode the lowest common denominator. +const OldestVersion = "v1beta1" + +// Versions is the list of versions that are recognized in code. The order provided +// may be assumed to be least feature rich to most feature rich, and clients may +// choose to prefer the latter items in the list over the former items when presented +// with a set of versions to choose. +var Versions = []string{"v1beta1", "v1beta2"} // Codec is the default codec for serializing output that should use // the latest supported version. Use this Codec when writing to @@ -35,3 +48,17 @@ var Codec = v1beta1.Codec // of versioning. // TODO: when versioning changes, make this part of each API definition. var ResourceVersioner = runtime.NewJSONBaseResourceVersioner() + +// InterfacesFor returns the default Codec and ResourceVersioner for a given version +// string, or an error if the version is not known. +func InterfacesFor(version string) (codec runtime.Codec, versioner runtime.ResourceVersioner, err error) { + switch version { + case "v1beta1": + codec, versioner = v1beta1.Codec, ResourceVersioner + case "v1beta2": + codec, versioner = v1beta2.Codec, ResourceVersioner + default: + err = fmt.Errorf("unsupported storage version: %s (valid: %s)", version, strings.Join(Versions, ", ")) + } + return +} diff --git a/pkg/api/latest/latest_test.go b/pkg/api/latest/latest_test.go index 997d536bc3..61b698e943 100644 --- a/pkg/api/latest/latest_test.go +++ b/pkg/api/latest/latest_test.go @@ -144,3 +144,14 @@ func TestCodec(t *testing.T) { t.Errorf("unexpected unmarshalled object %#v", other) } } + +func TestInterfacesFor(t *testing.T) { + if _, _, err := InterfacesFor(""); err == nil { + t.Fatalf("unexpected non-error: %v", err) + } + for i, version := range append([]string{Version, OldestVersion}, Versions...) { + if codec, versioner, err := InterfacesFor(version); err != nil || codec == nil || versioner == nil { + t.Fatalf("%d: unexpected result: %v", i, err) + } + } +} diff --git a/pkg/client/client.go b/pkg/client/client.go index 5147dce1d6..b79cd29a9f 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -23,6 +23,7 @@ import ( "io/ioutil" "net/http" "net/url" + "strings" "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" @@ -96,19 +97,30 @@ type Client struct { // New creates a Kubernetes client. This client works with pods, replication controllers // and services. It allows operations such as list, get, update and delete on these objects. // host must be a host string, a host:port combo, or an http or https URL. Passing a prefix -// to a URL will prepend the server path. Returns an error if host cannot be converted to a -// valid URL. -func New(host string, auth *AuthInfo) (*Client, error) { - restClient, err := NewRESTClient(host, auth, "/api/v1beta1/", latest.Codec) +// to a URL will prepend the server path. The API version to use may be specified or left +// empty to use the client preferred version. Returns an error if host cannot be converted to +// a valid URL. +func New(host, version string, auth *AuthInfo) (*Client, error) { + if version == "" { + // Clients default to the preferred code API version + // TODO: implement version negotation (highest version supported by server) + version = latest.Version + } + serverCodec, _, err := latest.InterfacesFor(version) if err != nil { - return nil, err + return nil, fmt.Errorf("API version '%s' is not recognized (valid values: %s)", version, strings.Join(latest.Versions, ", ")) + } + prefix := fmt.Sprintf("/api/%s/", version) + restClient, err := NewRESTClient(host, auth, prefix, serverCodec) + if err != nil { + return nil, fmt.Errorf("API URL '%s' is not valid: %v", host, err) } return &Client{restClient}, nil } // NewOrDie creates a Kubernetes client and panics if the provided host is invalid. -func NewOrDie(host string, auth *AuthInfo) *Client { - client, err := New(host, auth) +func NewOrDie(host, version string, auth *AuthInfo) *Client { + client, err := New(host, version, auth) if err != nil { panic(err) } diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 1f0b2018fb..3ac7415677 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -27,6 +27,8 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" @@ -36,6 +38,38 @@ import ( // TODO: Move this to a common place, it's needed in multiple tests. const apiPath = "/api/v1beta1" +func TestChecksCodec(t *testing.T) { + testCases := map[string]struct { + Err bool + Prefix string + Codec runtime.Codec + }{ + "v1beta1": {false, "/api/v1beta1/", v1beta1.Codec}, + "": {false, "/api/v1beta1/", v1beta1.Codec}, + "v1beta2": {false, "/api/v1beta2/", v1beta2.Codec}, + "v1beta3": {true, "", nil}, + } + for version, expected := range testCases { + client, err := New("127.0.0.1", version, nil) + switch { + case err == nil && expected.Err: + t.Errorf("expected error but was nil") + continue + case err != nil && !expected.Err: + t.Errorf("unexpected error %v", err) + continue + case err != nil: + continue + } + if e, a := expected.Prefix, client.prefix; e != a { + t.Errorf("expected %#v, got %#v", e, a) + } + if e, a := expected.Codec, client.Codec; e != a { + t.Errorf("expected %#v, got %#v", e, a) + } + } +} + func TestValidatesHostParameter(t *testing.T) { testCases := map[string]struct { Host string @@ -49,7 +83,7 @@ func TestValidatesHostParameter(t *testing.T) { "host/server": {"", "", true}, } for k, expected := range testCases { - c, err := NewRESTClient(k, nil, "/api/v1beta1/", latest.Codec) + c, err := NewRESTClient(k, nil, "/api/v1beta1/", v1beta1.Codec) switch { case err == nil && expected.Err: t.Errorf("expected error but was nil") @@ -355,7 +389,7 @@ func (c *testClient) Setup() *testClient { } c.server = httptest.NewServer(c.handler) if c.Client == nil { - c.Client = NewOrDie("localhost", nil) + c.Client = NewOrDie("localhost", "v1beta1", nil) } c.Client.host = c.server.URL c.Client.prefix = "/api/v1beta1/" @@ -512,7 +546,7 @@ func TestDoRequest(t *testing.T) { testClients := []testClient{ {Request: testRequest{Method: "GET", Path: "good"}, Response: Response{StatusCode: 200}}, {Request: testRequest{Method: "GET", Path: "bad%ZZ"}, Error: true}, - {Client: NewOrDie("localhost", &AuthInfo{"foo", "bar"}), Request: testRequest{Method: "GET", Path: "auth", Header: "Authorization"}, Response: Response{StatusCode: 200}}, + {Client: NewOrDie("localhost", "v1beta1", &AuthInfo{"foo", "bar"}), Request: testRequest{Method: "GET", Path: "auth", Header: "Authorization"}, Response: Response{StatusCode: 200}}, {Client: &Client{&RESTClient{httpClient: http.DefaultClient}}, Request: testRequest{Method: "GET", Path: "nocertificate"}, Error: true}, {Request: testRequest{Method: "GET", Path: "error"}, Response: Response{StatusCode: 500}, Error: true}, {Request: testRequest{Method: "POST", Path: "faildecode"}, Response: Response{StatusCode: 200, RawBody: &invalid}}, @@ -543,7 +577,7 @@ func TestDoRequestAccepted(t *testing.T) { testServer := httptest.NewServer(&fakeHandler) request, _ := http.NewRequest("GET", testServer.URL+"/foo/bar", nil) auth := AuthInfo{User: "user", Password: "pass"} - c, err := New(testServer.URL, &auth) + c, err := New(testServer.URL, "", &auth) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -580,7 +614,7 @@ func TestDoRequestAcceptedSuccess(t *testing.T) { testServer := httptest.NewServer(&fakeHandler) request, _ := http.NewRequest("GET", testServer.URL+"/foo/bar", nil) auth := AuthInfo{User: "user", Password: "pass"} - c, err := New(testServer.URL, &auth) + c, err := New(testServer.URL, "", &auth) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -617,7 +651,7 @@ func TestGetServerVersion(t *testing.T) { w.WriteHeader(http.StatusOK) w.Write(output) })) - client := NewOrDie(server.URL, nil) + client := NewOrDie(server.URL, "", nil) got, err := client.ServerVersion() if err != nil { diff --git a/pkg/client/request_test.go b/pkg/client/request_test.go index 24bebadceb..cf7229e73a 100644 --- a/pkg/client/request_test.go +++ b/pkg/client/request_test.go @@ -29,7 +29,8 @@ import ( "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" @@ -39,7 +40,7 @@ import ( func TestDoRequestNewWay(t *testing.T) { reqBody := "request body" expectedObj := &api.Service{Port: 12345} - expectedBody, _ := latest.Codec.Encode(expectedObj) + expectedBody, _ := v1beta2.Codec.Encode(expectedObj) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -47,7 +48,7 @@ func TestDoRequestNewWay(t *testing.T) { } testServer := httptest.NewServer(&fakeHandler) auth := AuthInfo{User: "user", Password: "pass"} - c := NewOrDie(testServer.URL, &auth) + c := NewOrDie(testServer.URL, "v1beta2", &auth) obj, err := c.Verb("POST"). Path("foo/bar"). Path("baz"). @@ -64,7 +65,7 @@ func TestDoRequestNewWay(t *testing.T) { } else if !reflect.DeepEqual(obj, expectedObj) { t.Errorf("Expected: %#v, got %#v", expectedObj, obj) } - fakeHandler.ValidateRequest(t, "/api/v1beta1/foo/bar/baz?labels=name%3Dfoo", "POST", &reqBody) + fakeHandler.ValidateRequest(t, "/api/v1beta2/foo/bar/baz?labels=name%3Dfoo", "POST", &reqBody) if fakeHandler.RequestReceived.Header["Authorization"] == nil { t.Errorf("Request is missing authorization header: %#v", *fakeHandler.RequestReceived) } @@ -72,9 +73,9 @@ func TestDoRequestNewWay(t *testing.T) { func TestDoRequestNewWayReader(t *testing.T) { reqObj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} - reqBodyExpected, _ := latest.Codec.Encode(reqObj) + reqBodyExpected, _ := v1beta1.Codec.Encode(reqObj) expectedObj := &api.Service{Port: 12345} - expectedBody, _ := latest.Codec.Encode(expectedObj) + expectedBody, _ := v1beta1.Codec.Encode(expectedObj) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -82,7 +83,7 @@ func TestDoRequestNewWayReader(t *testing.T) { } testServer := httptest.NewServer(&fakeHandler) auth := AuthInfo{User: "user", Password: "pass"} - c := NewOrDie(testServer.URL, &auth) + c := NewOrDie(testServer.URL, "v1beta1", &auth) obj, err := c.Verb("POST"). Path("foo/bar"). Path("baz"). @@ -109,9 +110,9 @@ func TestDoRequestNewWayReader(t *testing.T) { func TestDoRequestNewWayObj(t *testing.T) { reqObj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} - reqBodyExpected, _ := latest.Codec.Encode(reqObj) + reqBodyExpected, _ := v1beta2.Codec.Encode(reqObj) expectedObj := &api.Service{Port: 12345} - expectedBody, _ := latest.Codec.Encode(expectedObj) + expectedBody, _ := v1beta2.Codec.Encode(expectedObj) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -119,7 +120,7 @@ func TestDoRequestNewWayObj(t *testing.T) { } testServer := httptest.NewServer(&fakeHandler) auth := AuthInfo{User: "user", Password: "pass"} - c := NewOrDie(testServer.URL, &auth) + c := NewOrDie(testServer.URL, "v1beta2", &auth) obj, err := c.Verb("POST"). Path("foo/bar"). Path("baz"). @@ -137,7 +138,7 @@ func TestDoRequestNewWayObj(t *testing.T) { t.Errorf("Expected: %#v, got %#v", expectedObj, obj) } tmpStr := string(reqBodyExpected) - fakeHandler.ValidateRequest(t, "/api/v1beta1/foo/bar/baz?labels=name%3Dfoo", "POST", &tmpStr) + fakeHandler.ValidateRequest(t, "/api/v1beta2/foo/bar/baz?labels=name%3Dfoo", "POST", &tmpStr) if fakeHandler.RequestReceived.Header["Authorization"] == nil { t.Errorf("Request is missing authorization header: %#v", *fakeHandler.RequestReceived) } @@ -145,7 +146,7 @@ func TestDoRequestNewWayObj(t *testing.T) { func TestDoRequestNewWayFile(t *testing.T) { reqObj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} - reqBodyExpected, err := latest.Codec.Encode(reqObj) + reqBodyExpected, err := v1beta1.Codec.Encode(reqObj) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -161,7 +162,7 @@ func TestDoRequestNewWayFile(t *testing.T) { } expectedObj := &api.Service{Port: 12345} - expectedBody, _ := latest.Codec.Encode(expectedObj) + expectedBody, _ := v1beta1.Codec.Encode(expectedObj) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -169,7 +170,7 @@ func TestDoRequestNewWayFile(t *testing.T) { } testServer := httptest.NewServer(&fakeHandler) auth := AuthInfo{User: "user", Password: "pass"} - c := NewOrDie(testServer.URL, &auth) + c := NewOrDie(testServer.URL, "v1beta1", &auth) obj, err := c.Verb("POST"). Path("foo/bar"). Path("baz"). @@ -194,7 +195,7 @@ func TestDoRequestNewWayFile(t *testing.T) { } func TestVerbs(t *testing.T) { - c := NewOrDie("localhost", nil) + c := NewOrDie("localhost", "", nil) if r := c.Post(); r.verb != "POST" { t.Errorf("Post verb is wrong") } @@ -211,7 +212,7 @@ func TestVerbs(t *testing.T) { func TestAbsPath(t *testing.T) { expectedPath := "/bar/foo" - c := NewOrDie("localhost", nil) + c := NewOrDie("localhost", "", nil) r := c.Post().Path("/foo").AbsPath(expectedPath) if r.path != expectedPath { t.Errorf("unexpected path: %s, expected %s", r.path, expectedPath) @@ -219,7 +220,7 @@ func TestAbsPath(t *testing.T) { } func TestSync(t *testing.T) { - c := NewOrDie("localhost", nil) + c := NewOrDie("localhost", "", nil) r := c.Get() if r.sync { t.Errorf("sync has wrong default") @@ -246,7 +247,7 @@ func TestUintParam(t *testing.T) { } for _, item := range table { - c := NewOrDie("localhost", nil) + c := NewOrDie("localhost", "", nil) r := c.Get().AbsPath("").UintParam(item.name, item.testVal) if e, a := item.expectStr, r.finalURL(); e != a { t.Errorf("expected %v, got %v", e, a) @@ -265,7 +266,7 @@ func TestUnacceptableParamNames(t *testing.T) { } for _, item := range table { - c := NewOrDie("localhost", nil) + c := NewOrDie("localhost", "", nil) r := c.Get().setParam(item.name, item.testVal) if e, a := item.expectSuccess, r.err == nil; e != a { t.Errorf("expected %v, got %v (%v)", e, a, r.err) @@ -274,7 +275,7 @@ func TestUnacceptableParamNames(t *testing.T) { } func TestSetPollPeriod(t *testing.T) { - c := NewOrDie("localhost", nil) + c := NewOrDie("localhost", "", nil) r := c.Get() if r.pollPeriod == 0 { t.Errorf("polling should be on by default") @@ -296,7 +297,7 @@ func TestPolling(t *testing.T) { callNumber := 0 testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - data, err := latest.Codec.Encode(objects[callNumber]) + data, err := v1beta1.Codec.Encode(objects[callNumber]) if err != nil { t.Errorf("Unexpected encode error") } @@ -305,7 +306,7 @@ func TestPolling(t *testing.T) { })) auth := AuthInfo{User: "user", Password: "pass"} - c := NewOrDie(testServer.URL, &auth) + c := NewOrDie(testServer.URL, "v1beta1", &auth) trials := []func(){ func() { @@ -402,7 +403,7 @@ func TestWatch(t *testing.T) { encoder := json.NewEncoder(w) for _, item := range table { - data, err := api.NewJSONWatchEvent(latest.Codec, watch.Event{item.t, item.obj}) + data, err := api.NewJSONWatchEvent(v1beta1.Codec, watch.Event{item.t, item.obj}) if err != nil { panic(err) } @@ -413,7 +414,7 @@ func TestWatch(t *testing.T) { } })) - s, err := New(testServer.URL, &auth) + s, err := New(testServer.URL, "v1beta1", &auth) if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/pkg/controller/replication_controller_test.go b/pkg/controller/replication_controller_test.go index 1012ed01e0..be8d840359 100644 --- a/pkg/controller/replication_controller_test.go +++ b/pkg/controller/replication_controller_test.go @@ -117,7 +117,7 @@ func TestSyncReplicationControllerDoesNothing(t *testing.T) { ResponseBody: string(body), } testServer := httptest.NewTLSServer(&fakeHandler) - client := client.NewOrDie(testServer.URL, nil) + client := client.NewOrDie(testServer.URL, "v1beta1", nil) fakePodControl := FakePodControl{} @@ -137,7 +137,7 @@ func TestSyncReplicationControllerDeletes(t *testing.T) { ResponseBody: string(body), } testServer := httptest.NewTLSServer(&fakeHandler) - client := client.NewOrDie(testServer.URL, nil) + client := client.NewOrDie(testServer.URL, "v1beta1", nil) fakePodControl := FakePodControl{} @@ -157,7 +157,7 @@ func TestSyncReplicationControllerCreates(t *testing.T) { ResponseBody: string(body), } testServer := httptest.NewTLSServer(&fakeHandler) - client := client.NewOrDie(testServer.URL, nil) + client := client.NewOrDie(testServer.URL, "v1beta1", nil) fakePodControl := FakePodControl{} @@ -177,7 +177,7 @@ func TestCreateReplica(t *testing.T) { ResponseBody: string(body), } testServer := httptest.NewTLSServer(&fakeHandler) - client := client.NewOrDie(testServer.URL, nil) + client := client.NewOrDie(testServer.URL, "v1beta1", nil) podControl := RealPodControl{ kubeClient: client, @@ -227,7 +227,7 @@ func TestCreateReplica(t *testing.T) { } } -func TestSyncronize(t *testing.T) { +func TestSynchonize(t *testing.T) { controllerSpec1 := api.ReplicationController{ JSONBase: api.JSONBase{APIVersion: "v1beta1"}, DesiredState: api.ReplicationControllerState{ @@ -310,7 +310,7 @@ func TestSyncronize(t *testing.T) { t.Errorf("Unexpected request for %v", req.RequestURI) }) testServer := httptest.NewServer(mux) - client := client.NewOrDie(testServer.URL, nil) + client := client.NewOrDie(testServer.URL, "v1beta1", nil) manager := NewReplicationManager(client) fakePodControl := FakePodControl{} manager.podControl = &fakePodControl diff --git a/pkg/kubecfg/parse.go b/pkg/kubecfg/parse.go index ae6bbc877f..b05399d118 100644 --- a/pkg/kubecfg/parse.go +++ b/pkg/kubecfg/parse.go @@ -38,18 +38,18 @@ func NewParser(objectMap map[string]runtime.Object) *Parser { // ToWireFormat takes input 'data' as either json or yaml, checks that it parses as the // appropriate object type, and returns json for sending to the API or an error. -func (p *Parser) ToWireFormat(data []byte, storage string, c runtime.Codec) ([]byte, error) { +func (p *Parser) ToWireFormat(data []byte, storage string, decode runtime.Codec, encode runtime.Codec) ([]byte, error) { prototypeType, found := p.storageToType[storage] if !found { return nil, fmt.Errorf("unknown storage type: %v", storage) } obj := reflect.New(prototypeType).Interface().(runtime.Object) - err := c.DecodeInto(data, obj) + err := decode.DecodeInto(data, obj) if err != nil { return nil, err } - return c.Encode(obj) + return encode.Encode(obj) } func (p *Parser) SupportedWireStorage() []string { diff --git a/pkg/kubecfg/parse_test.go b/pkg/kubecfg/parse_test.go index 25db54f26f..6aa82efa44 100644 --- a/pkg/kubecfg/parse_test.go +++ b/pkg/kubecfg/parse_test.go @@ -22,28 +22,28 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" - _ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "gopkg.in/v1/yaml" ) func TestParseBadStorage(t *testing.T) { p := NewParser(map[string]runtime.Object{}) - _, err := p.ToWireFormat([]byte("{}"), "badstorage", latest.Codec) + _, err := p.ToWireFormat([]byte("{}"), "badstorage", latest.Codec, latest.Codec) if err == nil { t.Errorf("Expected error, received none") } } -func DoParseTest(t *testing.T, storage string, obj runtime.Object, p *Parser) { - jsonData, _ := latest.Codec.Encode(obj) +func DoParseTest(t *testing.T, storage string, obj runtime.Object, codec runtime.Codec, p *Parser) { + jsonData, _ := codec.Encode(obj) var tmp map[string]interface{} json.Unmarshal(jsonData, &tmp) yamlData, _ := yaml.Marshal(tmp) t.Logf("Intermediate yaml:\n%v\n", string(yamlData)) t.Logf("Intermediate json:\n%v\n", string(jsonData)) - jsonGot, jsonErr := p.ToWireFormat(jsonData, storage, latest.Codec) - yamlGot, yamlErr := p.ToWireFormat(yamlData, storage, latest.Codec) + jsonGot, jsonErr := p.ToWireFormat(jsonData, storage, latest.Codec, codec) + yamlGot, yamlErr := p.ToWireFormat(yamlData, storage, latest.Codec, codec) if jsonErr != nil { t.Errorf("json err: %#v", jsonErr) @@ -81,7 +81,7 @@ func TestParsePod(t *testing.T) { }, }, }, - }, testParser) + }, v1beta1.Codec, testParser) } func TestParseService(t *testing.T) { @@ -94,7 +94,7 @@ func TestParseService(t *testing.T) { Selector: map[string]string{ "area": "staging", }, - }, testParser) + }, v1beta1.Codec, testParser) } func TestParseController(t *testing.T) { @@ -116,7 +116,7 @@ func TestParseController(t *testing.T) { }, }, }, - }, testParser) + }, v1beta1.Codec, testParser) } type TestParseType struct { @@ -136,5 +136,5 @@ func TestParseCustomType(t *testing.T) { DoParseTest(t, "custom", &TestParseType{ JSONBase: api.JSONBase{APIVersion: "", ID: "my custom object", Kind: "TestParseType"}, Data: "test data", - }, parser) + }, v1beta1.Codec, parser) } diff --git a/pkg/master/master.go b/pkg/master/master.go index 997288598e..321572683d 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -20,6 +20,7 @@ import ( "net/http" "time" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" @@ -34,6 +35,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/service" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" servicecontroller "github.com/GoogleCloudPlatform/kubernetes/pkg/service" + "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" goetcd "github.com/coreos/go-etcd/etcd" @@ -44,7 +46,7 @@ import ( type Config struct { Client *client.Client Cloud cloudprovider.Interface - EtcdServers []string + EtcdHelper tools.EtcdHelper HealthCheckMinions bool Minions []string MinionCacheTTL time.Duration @@ -64,16 +66,29 @@ type Master struct { client *client.Client } -// New returns a new instance of Master connected to the given etcdServer. +// NewEtcdHelper returns an EtcdHelper for the provided arguments or an error if the version +// is incorrect. +func NewEtcdHelper(etcdServers []string, version string) (helper tools.EtcdHelper, err error) { + client := goetcd.NewClient(etcdServers) + if version == "" { + version = latest.Version + } + codec, versioner, err := latest.InterfacesFor(version) + if err != nil { + return helper, err + } + return tools.EtcdHelper{client, codec, versioner}, nil +} + +// New returns a new instance of Master connected to the given etcd server. func New(c *Config) *Master { - etcdClient := goetcd.NewClient(c.EtcdServers) minionRegistry := makeMinionRegistry(c) m := &Master{ - podRegistry: etcd.NewRegistry(etcdClient), - controllerRegistry: etcd.NewRegistry(etcdClient), - serviceRegistry: etcd.NewRegistry(etcdClient), - endpointRegistry: etcd.NewRegistry(etcdClient), - bindingRegistry: etcd.NewRegistry(etcdClient), + podRegistry: etcd.NewRegistry(c.EtcdHelper), + controllerRegistry: etcd.NewRegistry(c.EtcdHelper), + serviceRegistry: etcd.NewRegistry(c.EtcdHelper), + endpointRegistry: etcd.NewRegistry(c.EtcdHelper), + bindingRegistry: etcd.NewRegistry(c.EtcdHelper), minionRegistry: minionRegistry, client: c.Client, } diff --git a/pkg/registry/etcd/etcd.go b/pkg/registry/etcd/etcd.go index e95c0e11ab..0b9f29081e 100644 --- a/pkg/registry/etcd/etcd.go +++ b/pkg/registry/etcd/etcd.go @@ -21,7 +21,6 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd" - "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/constraint" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" @@ -42,13 +41,9 @@ type Registry struct { } // NewRegistry creates an etcd registry. -func NewRegistry(client tools.EtcdClient) *Registry { +func NewRegistry(helper tools.EtcdHelper) *Registry { registry := &Registry{ - EtcdHelper: tools.EtcdHelper{ - client, - latest.Codec, - latest.ResourceVersioner, - }, + EtcdHelper: helper, } registry.manifestFactory = &BasicManifestFactory{ serviceRegistry: registry, diff --git a/pkg/registry/etcd/etcd_test.go b/pkg/registry/etcd/etcd_test.go index 322c58826b..b9e5986cf7 100644 --- a/pkg/registry/etcd/etcd_test.go +++ b/pkg/registry/etcd/etcd_test.go @@ -32,7 +32,7 @@ import ( ) func NewTestEtcdRegistry(client tools.EtcdClient) *Registry { - registry := NewRegistry(client) + registry := NewRegistry(tools.EtcdHelper{client, latest.Codec, latest.ResourceVersioner}) registry.manifestFactory = &BasicManifestFactory{ serviceRegistry: ®istrytest.ServiceRegistry{}, } diff --git a/pkg/service/endpoints_controller_test.go b/pkg/service/endpoints_controller_test.go index c1a3b73c65..55c71a0905 100644 --- a/pkg/service/endpoints_controller_test.go +++ b/pkg/service/endpoints_controller_test.go @@ -150,7 +150,7 @@ func TestSyncEndpointsEmpty(t *testing.T) { testServer := makeTestServer(t, serverResponse{http.StatusOK, newPodList(0)}, serverResponse{http.StatusOK, api.ServiceList{}}) - client := client.NewOrDie(testServer.URL, nil) + client := client.NewOrDie(testServer.URL, "v1beta1", nil) serviceRegistry := registrytest.ServiceRegistry{} endpoints := NewEndpointController(&serviceRegistry, client) if err := endpoints.SyncServiceEndpoints(); err != nil { @@ -162,7 +162,7 @@ func TestSyncEndpointsError(t *testing.T) { testServer := makeTestServer(t, serverResponse{http.StatusOK, newPodList(0)}, serverResponse{http.StatusInternalServerError, api.ServiceList{}}) - client := client.NewOrDie(testServer.URL, nil) + client := client.NewOrDie(testServer.URL, "v1beta1", nil) serviceRegistry := registrytest.ServiceRegistry{ Err: fmt.Errorf("test error"), } @@ -185,7 +185,7 @@ func TestSyncEndpointsItems(t *testing.T) { testServer := makeTestServer(t, serverResponse{http.StatusOK, newPodList(1)}, serverResponse{http.StatusOK, serviceList}) - client := client.NewOrDie(testServer.URL, nil) + client := client.NewOrDie(testServer.URL, "v1beta1", nil) serviceRegistry := registrytest.ServiceRegistry{} endpoints := NewEndpointController(&serviceRegistry, client) if err := endpoints.SyncServiceEndpoints(); err != nil { @@ -210,7 +210,7 @@ func TestSyncEndpointsPodError(t *testing.T) { testServer := makeTestServer(t, serverResponse{http.StatusInternalServerError, api.PodList{}}, serverResponse{http.StatusOK, serviceList}) - client := client.NewOrDie(testServer.URL, nil) + client := client.NewOrDie(testServer.URL, "v1beta1", nil) serviceRegistry := registrytest.ServiceRegistry{ List: api.ServiceList{ Items: []api.Service{ diff --git a/plugin/cmd/scheduler/scheduler.go b/plugin/cmd/scheduler/scheduler.go index a0f100c5ec..68b35a9d73 100644 --- a/plugin/cmd/scheduler/scheduler.go +++ b/plugin/cmd/scheduler/scheduler.go @@ -22,6 +22,7 @@ import ( "net/http" "strconv" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" _ "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz" masterPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/master" @@ -46,7 +47,7 @@ func main() { verflag.PrintAndExitIfRequested() // TODO: security story for plugins! - kubeClient, err := client.New(*master, nil) + kubeClient, err := client.New(*master, latest.OldestVersion, nil) if err != nil { glog.Fatalf("Invalid -master: %v", err) } diff --git a/plugin/pkg/scheduler/factory/factory_test.go b/plugin/pkg/scheduler/factory/factory_test.go index 1845d41c36..a5172c3e65 100644 --- a/plugin/pkg/scheduler/factory/factory_test.go +++ b/plugin/pkg/scheduler/factory/factory_test.go @@ -39,7 +39,7 @@ func TestCreate(t *testing.T) { T: t, } server := httptest.NewServer(&handler) - client := client.NewOrDie(server.URL, nil) + client := client.NewOrDie(server.URL, "", nil) factory := ConfigFactory{client} factory.Create() } @@ -74,7 +74,7 @@ func TestCreateLists(t *testing.T) { T: t, } server := httptest.NewServer(&handler) - factory.Client = client.NewOrDie(server.URL, nil) + factory.Client = client.NewOrDie(server.URL, latest.OldestVersion, nil) // This test merely tests that the correct request is made. item.factory().List() handler.ValidateRequest(t, item.location, "GET", nil) @@ -127,7 +127,7 @@ func TestCreateWatches(t *testing.T) { T: t, } server := httptest.NewServer(&handler) - factory.Client = client.NewOrDie(server.URL, nil) + factory.Client = client.NewOrDie(server.URL, "v1beta1", nil) // This test merely tests that the correct request is made. item.factory().Watch(item.rv) handler.ValidateRequest(t, item.location, "GET", nil) @@ -157,7 +157,7 @@ func TestPollMinions(t *testing.T) { // FakeHandler musn't be sent requests other than the one you want to test. mux.Handle("/api/v1beta1/minions", &handler) server := httptest.NewServer(mux) - client := client.NewOrDie(server.URL, nil) + client := client.NewOrDie(server.URL, "v1beta1", nil) cf := ConfigFactory{client} ce, err := cf.pollMinions() @@ -184,7 +184,7 @@ func TestDefaultErrorFunc(t *testing.T) { // FakeHandler musn't be sent requests other than the one you want to test. mux.Handle("/api/v1beta1/pods/foo", &handler) server := httptest.NewServer(mux) - factory := ConfigFactory{client.NewOrDie(server.URL, nil)} + factory := ConfigFactory{client.NewOrDie(server.URL, "", nil)} queue := cache.NewFIFO() errFunc := factory.makeDefaultErrorFunc(queue) @@ -289,7 +289,7 @@ func TestBind(t *testing.T) { T: t, } server := httptest.NewServer(&handler) - client := client.NewOrDie(server.URL, nil) + client := client.NewOrDie(server.URL, "", nil) b := binder{client} if err := b.Bind(item.binding); err != nil { diff --git a/test/integration/client_test.go b/test/integration/client_test.go index 5d539d11e2..e3627118dd 100644 --- a/test/integration/client_test.go +++ b/test/integration/client_test.go @@ -19,6 +19,7 @@ limitations under the License. package integration import ( + "fmt" "net/http/httptest" "reflect" "testing" @@ -28,6 +29,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/master" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/version" ) @@ -36,72 +38,87 @@ func init() { } func TestClient(t *testing.T) { + helper, err := master.NewEtcdHelper(newEtcdClient().GetCluster(), "v1beta1") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } m := master.New(&master.Config{ - EtcdServers: newEtcdClient().GetCluster(), + EtcdHelper: helper, }) + s1, c1 := m.API_v1beta1() + s2, c2 := m.API_v1beta2() - storage, codec := m.API_v1beta1() - s := httptest.NewServer(apiserver.Handle(storage, codec, "/api/v1beta1/")) - - client := client.NewOrDie(s.URL, nil) - - info, err := client.ServerVersion() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if e, a := version.Get(), *info; !reflect.DeepEqual(e, a) { - t.Errorf("expected %#v, got %#v", e, a) + testCases := map[string]struct { + Storage map[string]apiserver.RESTStorage + Codec runtime.Codec + }{ + "v1beta1": {s1, c1}, + "v1beta2": {s2, c2}, } - pods, err := client.ListPods(labels.Everything()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if len(pods.Items) != 0 { - t.Errorf("expected no pods, got %#v", pods) - } + for apiVersion, values := range testCases { + deleteAllEtcdKeys() + s := httptest.NewServer(apiserver.Handle(values.Storage, values.Codec, fmt.Sprintf("/api/%s/", apiVersion))) + client := client.NewOrDie(s.URL, apiVersion, nil) - // get a validation error - pod := &api.Pod{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Version: "v1beta2", - Containers: []api.Container{ - { - Name: "test", + info, err := client.ServerVersion() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if e, a := version.Get(), *info; !reflect.DeepEqual(e, a) { + t.Errorf("expected %#v, got %#v", e, a) + } + + pods, err := client.ListPods(labels.Everything()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(pods.Items) != 0 { + t.Errorf("expected no pods, got %#v", pods) + } + + // get a validation error + pod := &api.Pod{ + DesiredState: api.PodState{ + Manifest: api.ContainerManifest{ + Version: "v1beta2", + Containers: []api.Container{ + { + Name: "test", + }, }, }, }, - }, - } - got, err := client.CreatePod(pod) - if err == nil { - t.Fatalf("unexpected non-error: %v", err) - } + } + got, err := client.CreatePod(pod) + if err == nil { + t.Fatalf("unexpected non-error: %v", err) + } - // get a created pod - pod.DesiredState.Manifest.Containers[0].Image = "an-image" - got, err = client.CreatePod(pod) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if got.ID == "" { - t.Errorf("unexpected empty pod ID %v", got) - } + // get a created pod + pod.DesiredState.Manifest.Containers[0].Image = "an-image" + got, err = client.CreatePod(pod) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got.ID == "" { + t.Errorf("unexpected empty pod ID %v", got) + } - // pod is shown, but not scheduled - pods, err = client.ListPods(labels.Everything()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if len(pods.Items) != 1 { - t.Errorf("expected one pod, got %#v", pods) - } - actual := pods.Items[0] - if actual.ID != got.ID { - t.Errorf("expected pod %#v, got %#v", got, actual) - } - if actual.CurrentState.Host != "" { - t.Errorf("expected pod to be unscheduled, got %#v", actual) + // pod is shown, but not scheduled + pods, err = client.ListPods(labels.Everything()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(pods.Items) != 1 { + t.Errorf("expected one pod, got %#v", pods) + } + actual := pods.Items[0] + if actual.ID != got.ID { + t.Errorf("expected pod %#v, got %#v", got, actual) + } + if actual.CurrentState.Host != "" { + t.Errorf("expected pod to be unscheduled, got %#v", actual) + } } } diff --git a/test/integration/utils.go b/test/integration/utils.go index 8bfceeebcd..a1b442067b 100644 --- a/test/integration/utils.go +++ b/test/integration/utils.go @@ -41,3 +41,17 @@ func withEtcdKey(f func(string)) { defer newEtcdClient().Delete(prefix, true) f(prefix) } + +func deleteAllEtcdKeys() { + client := newEtcdClient() + keys, err := client.Get("/", false, false) + if err != nil { + glog.Fatalf("Unable to list root etcd keys: %v", err) + } + for _, node := range keys.Node.Nodes { + if _, err := client.Delete(node.Key, true); err != nil { + glog.Fatalf("Unable delete key: %v", err) + } + } + +}