diff --git a/pkg/client/client.go b/pkg/client/client.go index 45f269f49e..d624442910 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -34,28 +34,51 @@ import ( // Interface holds the methods for clients of Kubenetes, // an interface to allow mock testing. -// TODO: split this up by resource? // TODO: these should return/take pointers. type Interface interface { + PodInterface + ReplicationControllerInterface + ServiceInterface + VersionInterface +} + +// PodInterface has methods to work with Pod resources +type PodInterface interface { ListPods(selector labels.Selector) (api.PodList, error) GetPod(name string) (api.Pod, error) DeletePod(name string) error CreatePod(api.Pod) (api.Pod, error) UpdatePod(api.Pod) (api.Pod, error) +} +// ReplicationControllerInterface has methods to work with ReplicationController resources +type ReplicationControllerInterface interface { ListReplicationControllers(selector labels.Selector) (api.ReplicationControllerList, error) GetReplicationController(name string) (api.ReplicationController, error) CreateReplicationController(api.ReplicationController) (api.ReplicationController, error) UpdateReplicationController(api.ReplicationController) (api.ReplicationController, error) DeleteReplicationController(string) error WatchReplicationControllers(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) +} +// ServiceInterface has methods to work with Service resources +type ServiceInterface interface { GetService(name string) (api.Service, error) CreateService(api.Service) (api.Service, error) UpdateService(api.Service) (api.Service, error) DeleteService(string) error } +// VersionInterface has a method to retrieve the server version +type VersionInterface interface { + ServerVersion() (*version.Info, error) +} + +// Client is the actual implementation of a Kubernetes client. +type Client struct { + *RESTClient +} + // StatusErr might get returned from an api call if your request is still being processed // and hence the expected return data is not available yet. type StatusErr struct { @@ -72,20 +95,23 @@ type AuthInfo struct { Password string } -// Client is the actual implementation of a Kubernetes client. +// RESTClient holds common code used to work with API resources that follow the +// Kubernetes API pattern // Host is the http://... base for the URL -type Client struct { +type RESTClient struct { host string auth *AuthInfo httpClient *http.Client Sync bool PollPeriod time.Duration Timeout time.Duration + Prefix string } -// New creates a new client object. -func New(host string, auth *AuthInfo) *Client { - return &Client{ +// NewRESTClient creates a new RESTClient. This client performs generic REST functions +// such as Get, Put, Post, and Delete on specified paths. +func NewRESTClient(host string, auth *AuthInfo, prefix string) *RESTClient { + return &RESTClient{ auth: auth, host: host, httpClient: &http.Client{ @@ -98,11 +124,19 @@ func New(host string, auth *AuthInfo) *Client { Sync: false, PollPeriod: time.Second * 2, Timeout: time.Second * 20, + Prefix: prefix, } + +} + +// 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. +func New(host string, auth *AuthInfo) *Client { + return &Client{NewRESTClient(host, auth, "/api/v1beta1/")} } // Execute a request, adds authentication (if auth != nil), and HTTPS cert ignoring. -func (c *Client) doRequest(request *http.Request) ([]byte, error) { +func (c *RESTClient) doRequest(request *http.Request) ([]byte, error) { if c.auth != nil { request.SetBasicAuth(c.auth.User, c.auth.Password) } @@ -148,7 +182,7 @@ func (c *Client) doRequest(request *http.Request) ([]byte, error) { // path is the path on the host to hit // requestBody is the body of the request. Can be nil. // target the interface to marshal the JSON response into. Can be nil. -func (c *Client) rawRequest(method, path string, requestBody io.Reader, target interface{}) ([]byte, error) { +func (c *RESTClient) rawRequest(method, path string, requestBody io.Reader, target interface{}) ([]byte, error) { request, err := http.NewRequest(method, c.makeURL(path), requestBody) if err != nil { return nil, err @@ -167,8 +201,8 @@ func (c *Client) rawRequest(method, path string, requestBody io.Reader, target i return body, err } -func (c *Client) makeURL(path string) string { - return c.host + "/api/v1beta1/" + path +func (c *RESTClient) makeURL(path string) string { + return c.host + c.Prefix + path } // ListPods takes a selector, and returns the list of pods that match that selector diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 1371f03f3f..21c2d440d2 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -415,7 +415,7 @@ func TestMakeRequest(t *testing.T) { {Request: testRequest{Method: "GET", Path: "/good"}, Response: Response{StatusCode: 200}}, {Request: testRequest{Method: "GET", Path: "/bad%ZZ"}, Error: true}, {Client: New("", &AuthInfo{"foo", "bar"}), Request: testRequest{Method: "GET", Path: "/auth", Header: "Authorization"}, Response: Response{StatusCode: 200}}, - {Client: &Client{httpClient: http.DefaultClient}, Request: testRequest{Method: "GET", Path: "/nocertificate"}, Error: true}, + {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, Body: "aaaaa"}, Target: &struct{}{}, Error: true}, {Request: testRequest{Method: "GET", Path: "/failread"}, Response: Response{StatusCode: 200, Body: "aaaaa"}, Target: &struct{}{}, Error: true}, diff --git a/pkg/client/fake.go b/pkg/client/fake.go index 7a20ffa2dc..b03f599a1c 100644 --- a/pkg/client/fake.go +++ b/pkg/client/fake.go @@ -19,6 +19,7 @@ package client import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/version" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" ) @@ -110,3 +111,9 @@ func (c *Fake) DeleteService(service string) error { c.Actions = append(c.Actions, FakeAction{Action: "delete-service", Value: service}) return nil } + +func (c *Fake) ServerVersion() (*version.Info, error) { + c.Actions = append(c.Actions, FakeAction{Action: "get-version", Value: nil}) + versionInfo := version.Get() + return &versionInfo, nil +} diff --git a/pkg/client/request.go b/pkg/client/request.go index ceb0121599..3cc9b12461 100644 --- a/pkg/client/request.go +++ b/pkg/client/request.go @@ -52,11 +52,11 @@ var specialParams = util.NewStringSet("sync", "timeout") // if err != nil { ... } // list, ok := resp.(*api.PodList) // -func (c *Client) Verb(verb string) *Request { +func (c *RESTClient) Verb(verb string) *Request { return &Request{ verb: verb, c: c, - path: "/api/v1beta1", + path: c.Prefix, sync: c.Sync, timeout: c.Timeout, params: map[string]string{}, @@ -65,27 +65,27 @@ func (c *Client) Verb(verb string) *Request { } // Post begins a POST request. Short for c.Verb("POST"). -func (c *Client) Post() *Request { +func (c *RESTClient) Post() *Request { return c.Verb("POST") } // Put begins a PUT request. Short for c.Verb("PUT"). -func (c *Client) Put() *Request { +func (c *RESTClient) Put() *Request { return c.Verb("PUT") } // Get begins a GET request. Short for c.Verb("GET"). -func (c *Client) Get() *Request { +func (c *RESTClient) Get() *Request { return c.Verb("GET") } // Delete begins a DELETE request. Short for c.Verb("DELETE"). -func (c *Client) Delete() *Request { +func (c *RESTClient) Delete() *Request { return c.Verb("DELETE") } // PollFor makes a request to do a single poll of the completion of the given operation. -func (c *Client) PollFor(operationID string) *Request { +func (c *RESTClient) PollFor(operationID string) *Request { return c.Get().Path("operations").Path(operationID).Sync(false).PollPeriod(0) } @@ -93,7 +93,7 @@ func (c *Client) PollFor(operationID string) *Request { // Any errors are stored until the end of your call, so you only have to // check once. type Request struct { - c *Client + c *RESTClient err error verb string path string diff --git a/pkg/kubecfg/kubecfg.go b/pkg/kubecfg/kubecfg.go index 35e836e030..d9879c4c11 100644 --- a/pkg/kubecfg/kubecfg.go +++ b/pkg/kubecfg/kubecfg.go @@ -36,16 +36,11 @@ import ( ) func GetServerVersion(client *client.Client) (*version.Info, error) { - body, err := client.Get().AbsPath("/version").Do().Raw() + info, err := client.ServerVersion() if err != nil { - return nil, err + return nil, fmt.Errorf("Got error: %v", err) } - var info version.Info - err = json.Unmarshal(body, &info) - if err != nil { - return nil, fmt.Errorf("Got '%s': %v", string(body), err) - } - return &info, nil + return info, nil } func promptForString(field string, r io.Reader) string {