Merge pull request #995 from csrwng/refactor_api_client

Break up API client into per-resource client and common code
pull/6/head
Daniel Smith 2014-08-25 09:49:04 -07:00
commit ae0997807e
5 changed files with 63 additions and 27 deletions

View File

@ -34,28 +34,51 @@ import (
// Interface holds the methods for clients of Kubenetes, // Interface holds the methods for clients of Kubenetes,
// an interface to allow mock testing. // an interface to allow mock testing.
// TODO: split this up by resource?
// TODO: these should return/take pointers. // TODO: these should return/take pointers.
type Interface interface { 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) ListPods(selector labels.Selector) (api.PodList, error)
GetPod(name string) (api.Pod, error) GetPod(name string) (api.Pod, error)
DeletePod(name string) error DeletePod(name string) error
CreatePod(api.Pod) (api.Pod, error) CreatePod(api.Pod) (api.Pod, error)
UpdatePod(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) ListReplicationControllers(selector labels.Selector) (api.ReplicationControllerList, error)
GetReplicationController(name string) (api.ReplicationController, error) GetReplicationController(name string) (api.ReplicationController, error)
CreateReplicationController(api.ReplicationController) (api.ReplicationController, error) CreateReplicationController(api.ReplicationController) (api.ReplicationController, error)
UpdateReplicationController(api.ReplicationController) (api.ReplicationController, error) UpdateReplicationController(api.ReplicationController) (api.ReplicationController, error)
DeleteReplicationController(string) error DeleteReplicationController(string) error
WatchReplicationControllers(label, field labels.Selector, resourceVersion uint64) (watch.Interface, 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) GetService(name string) (api.Service, error)
CreateService(api.Service) (api.Service, error) CreateService(api.Service) (api.Service, error)
UpdateService(api.Service) (api.Service, error) UpdateService(api.Service) (api.Service, error)
DeleteService(string) 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 // 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. // and hence the expected return data is not available yet.
type StatusErr struct { type StatusErr struct {
@ -72,20 +95,23 @@ type AuthInfo struct {
Password string 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 // Host is the http://... base for the URL
type Client struct { type RESTClient struct {
host string host string
auth *AuthInfo auth *AuthInfo
httpClient *http.Client httpClient *http.Client
Sync bool Sync bool
PollPeriod time.Duration PollPeriod time.Duration
Timeout time.Duration Timeout time.Duration
Prefix string
} }
// New creates a new client object. // NewRESTClient creates a new RESTClient. This client performs generic REST functions
func New(host string, auth *AuthInfo) *Client { // such as Get, Put, Post, and Delete on specified paths.
return &Client{ func NewRESTClient(host string, auth *AuthInfo, prefix string) *RESTClient {
return &RESTClient{
auth: auth, auth: auth,
host: host, host: host,
httpClient: &http.Client{ httpClient: &http.Client{
@ -98,11 +124,19 @@ func New(host string, auth *AuthInfo) *Client {
Sync: false, Sync: false,
PollPeriod: time.Second * 2, PollPeriod: time.Second * 2,
Timeout: time.Second * 20, 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. // 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 { if c.auth != nil {
request.SetBasicAuth(c.auth.User, c.auth.Password) 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 // path is the path on the host to hit
// requestBody is the body of the request. Can be nil. // requestBody is the body of the request. Can be nil.
// target the interface to marshal the JSON response into. 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) request, err := http.NewRequest(method, c.makeURL(path), requestBody)
if err != nil { if err != nil {
return nil, err return nil, err
@ -167,8 +201,8 @@ func (c *Client) rawRequest(method, path string, requestBody io.Reader, target i
return body, err return body, err
} }
func (c *Client) makeURL(path string) string { func (c *RESTClient) makeURL(path string) string {
return c.host + "/api/v1beta1/" + path return c.host + c.Prefix + path
} }
// ListPods takes a selector, and returns the list of pods that match that selector // ListPods takes a selector, and returns the list of pods that match that selector

View File

@ -415,7 +415,7 @@ func TestMakeRequest(t *testing.T) {
{Request: testRequest{Method: "GET", Path: "/good"}, Response: Response{StatusCode: 200}}, {Request: testRequest{Method: "GET", Path: "/good"}, Response: Response{StatusCode: 200}},
{Request: testRequest{Method: "GET", Path: "/bad%ZZ"}, Error: true}, {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: 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: "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: "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}, {Request: testRequest{Method: "GET", Path: "/failread"}, Response: Response{StatusCode: 200, Body: "aaaaa"}, Target: &struct{}{}, Error: true},

View File

@ -19,6 +19,7 @@ package client
import ( import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch" "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}) c.Actions = append(c.Actions, FakeAction{Action: "delete-service", Value: service})
return nil 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
}

View File

@ -52,11 +52,11 @@ var specialParams = util.NewStringSet("sync", "timeout")
// if err != nil { ... } // if err != nil { ... }
// list, ok := resp.(*api.PodList) // list, ok := resp.(*api.PodList)
// //
func (c *Client) Verb(verb string) *Request { func (c *RESTClient) Verb(verb string) *Request {
return &Request{ return &Request{
verb: verb, verb: verb,
c: c, c: c,
path: "/api/v1beta1", path: c.Prefix,
sync: c.Sync, sync: c.Sync,
timeout: c.Timeout, timeout: c.Timeout,
params: map[string]string{}, 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"). // Post begins a POST request. Short for c.Verb("POST").
func (c *Client) Post() *Request { func (c *RESTClient) Post() *Request {
return c.Verb("POST") return c.Verb("POST")
} }
// Put begins a PUT request. Short for c.Verb("PUT"). // Put begins a PUT request. Short for c.Verb("PUT").
func (c *Client) Put() *Request { func (c *RESTClient) Put() *Request {
return c.Verb("PUT") return c.Verb("PUT")
} }
// Get begins a GET request. Short for c.Verb("GET"). // Get begins a GET request. Short for c.Verb("GET").
func (c *Client) Get() *Request { func (c *RESTClient) Get() *Request {
return c.Verb("GET") return c.Verb("GET")
} }
// Delete begins a DELETE request. Short for c.Verb("DELETE"). // Delete begins a DELETE request. Short for c.Verb("DELETE").
func (c *Client) Delete() *Request { func (c *RESTClient) Delete() *Request {
return c.Verb("DELETE") return c.Verb("DELETE")
} }
// PollFor makes a request to do a single poll of the completion of the given operation. // 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) 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 // Any errors are stored until the end of your call, so you only have to
// check once. // check once.
type Request struct { type Request struct {
c *Client c *RESTClient
err error err error
verb string verb string
path string path string

View File

@ -36,16 +36,11 @@ import (
) )
func GetServerVersion(client *client.Client) (*version.Info, error) { func GetServerVersion(client *client.Client) (*version.Info, error) {
body, err := client.Get().AbsPath("/version").Do().Raw() info, err := client.ServerVersion()
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("Got error: %v", err)
} }
var info version.Info return info, nil
err = json.Unmarshal(body, &info)
if err != nil {
return nil, fmt.Errorf("Got '%s': %v", string(body), err)
}
return &info, nil
} }
func promptForString(field string, r io.Reader) string { func promptForString(field string, r io.Reader) string {