diff --git a/client/client.go b/client/client.go index e129bb02..e973794f 100644 --- a/client/client.go +++ b/client/client.go @@ -21,6 +21,7 @@ import ( "fmt" "io/ioutil" "net/http" + "path" "strings" "github.com/google/cadvisor/info" @@ -32,18 +33,14 @@ type Client struct { } // NewClient returns a new client with the specified base URL. -// TODO(cAdvisor): Currently the error result is always nil. -func NewClient(URL string) (*Client, error) { - return &Client{ - baseUrl: strings.Join([]string{ - URL, - "api/v1.0", - }, "/"), - }, nil -} +func NewClient(url string) (*Client, error) { + if !strings.HasSuffix(url, "/") { + url += "/" + } -func (self *Client) machineInfoUrl() string { - return strings.Join([]string{self.baseUrl, "machine"}, "/") + return &Client{ + baseUrl: fmt.Sprintf("%sapi/v1.1/", url), + }, nil } // MachineInfo returns the JSON machine information for this client. @@ -59,11 +56,40 @@ func (self *Client) MachineInfo() (minfo *info.MachineInfo, err error) { return } -func (self *Client) containerInfoUrl(name string) string { - if name[0] == '/' { - name = name[1:] +// ContainerInfo returns the JSON container information for the specified +// container and request. +func (self *Client) ContainerInfo(name string, query *info.ContainerInfoRequest) (cinfo *info.ContainerInfo, err error) { + u := self.containerInfoUrl(name) + ret := new(info.ContainerInfo) + if err = self.httpGetJsonData(ret, query, u, fmt.Sprintf("container info for %q", name)); err != nil { + return } - return strings.Join([]string{self.baseUrl, "containers", name}, "/") + cinfo = ret + return +} + +// Returns the information about all subcontainers (recursive) of the specified container (including itself). +func (self *Client) SubcontainersInfo(name string, query *info.ContainerInfoRequest) ([]info.ContainerInfo, error) { + var response []info.ContainerInfo + url := self.subcontainersInfoUrl(name) + err := self.httpGetJsonData(&response, query, url, fmt.Sprintf("subcontainers container info for %q", name)) + if err != nil { + return []info.ContainerInfo{}, err + + } + return response, nil +} + +func (self *Client) machineInfoUrl() string { + return self.baseUrl + path.Join("machine") +} + +func (self *Client) containerInfoUrl(name string) string { + return self.baseUrl + path.Join("containers", name) +} + +func (self *Client) subcontainersInfoUrl(name string) string { + return self.baseUrl + path.Join("subcontainers", name) } func (self *Client) httpGetJsonData(data, postData interface{}, url, infoName string) error { @@ -95,16 +121,3 @@ func (self *Client) httpGetJsonData(data, postData interface{}, url, infoName st } return nil } - -// ContainerInfo returns the JSON container information for the specified -// container and request. -func (self *Client) ContainerInfo(name string, - query *info.ContainerInfoRequest) (cinfo *info.ContainerInfo, err error) { - u := self.containerInfoUrl(name) - ret := new(info.ContainerInfo) - if err = self.httpGetJsonData(ret, query, u, fmt.Sprintf("container info for %v", name)); err != nil { - return - } - cinfo = ret - return -} diff --git a/client/client_test.go b/client/client_test.go index 1258e09b..f05682b2 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -19,6 +19,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "path" "reflect" "testing" "time" @@ -56,7 +57,7 @@ func cadvisorTestClient(path string, expectedPostObj, expectedPostObjEmpty, repl } encoder := json.NewEncoder(w) encoder.Encode(replyObj) - } else if r.URL.Path == "/api/v1.0/machine" { + } else if r.URL.Path == "/api/v1.1/machine" { fmt.Fprint(w, `{"num_cores":8,"memory_capacity":31625871360}`) } else { w.WriteHeader(http.StatusNotFound) @@ -78,7 +79,7 @@ func TestGetMachineinfo(t *testing.T) { NumCores: 8, MemoryCapacity: 31625871360, } - client, server, err := cadvisorTestClient("/api/v1.0/machine", nil, nil, minfo, t) + client, server, err := cadvisorTestClient("/api/v1.1/machine", nil, nil, minfo, t) if err != nil { t.Fatalf("unable to get a client %v", err) } @@ -100,7 +101,7 @@ func TestGetContainerInfo(t *testing.T) { } containerName := "/some/container" cinfo := itest.GenerateRandomContainerInfo(containerName, 4, query, 1*time.Second) - client, server, err := cadvisorTestClient(fmt.Sprintf("/api/v1.0/containers%v", containerName), query, &info.ContainerInfoRequest{}, cinfo, t) + client, server, err := cadvisorTestClient(fmt.Sprintf("/api/v1.1/containers%v", containerName), query, &info.ContainerInfoRequest{}, cinfo, t) if err != nil { t.Fatalf("unable to get a client %v", err) } @@ -114,3 +115,40 @@ func TestGetContainerInfo(t *testing.T) { t.Error("received unexpected ContainerInfo") } } + +func TestGetSubcontainersInfo(t *testing.T) { + query := &info.ContainerInfoRequest{ + NumStats: 3, + } + containerName := "/some/container" + cinfo := itest.GenerateRandomContainerInfo(containerName, 4, query, 1*time.Second) + cinfo1 := itest.GenerateRandomContainerInfo(path.Join(containerName, "sub1"), 4, query, 1*time.Second) + cinfo2 := itest.GenerateRandomContainerInfo(path.Join(containerName, "sub2"), 4, query, 1*time.Second) + response := []info.ContainerInfo{ + *cinfo, + *cinfo1, + *cinfo2, + } + client, server, err := cadvisorTestClient(fmt.Sprintf("/api/v1.1/subcontainers%v", containerName), query, &info.ContainerInfoRequest{}, response, t) + if err != nil { + t.Fatalf("unable to get a client %v", err) + } + defer server.Close() + returned, err := client.SubcontainersInfo(containerName, query) + if err != nil { + t.Fatal(err) + } + + if len(returned) != 3 { + t.Errorf("unexpected number of results: got %d, expected 3", len(returned)) + } + if !returned[0].Eq(cinfo) { + t.Error("received unexpected ContainerInfo") + } + if !returned[1].Eq(cinfo1) { + t.Error("received unexpected ContainerInfo") + } + if !returned[2].Eq(cinfo2) { + t.Error("received unexpected ContainerInfo") + } +}