mirror of https://github.com/k3s-io/k3s
commit
6908c9ca4a
|
@ -79,13 +79,13 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/client",
|
||||
"Comment": "0.4.0",
|
||||
"Rev": "5a6d06c02600b1e57e55a9d9f71dbac1bfc9fe6c"
|
||||
"Comment": "0.5.0",
|
||||
"Rev": "8c4f650e62f096710da794e536de86e34447fca9"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/cadvisor/info",
|
||||
"Comment": "0.4.0",
|
||||
"Rev": "5a6d06c02600b1e57e55a9d9f71dbac1bfc9fe6c"
|
||||
"Comment": "0.5.0",
|
||||
"Rev": "8c4f650e62f096710da794e536de86e34447fca9"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/gofuzz",
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
# Example REST API Client
|
||||
|
||||
This is an implementation of a cAdvisor REST API in Go. You can use it like this:
|
||||
|
||||
```go
|
||||
client, err := client.NewClient("http://192.168.59.103:8080/")
|
||||
```
|
||||
|
||||
Obviously, replace the URL with the path to your actual cAdvisor REST endpoint.
|
||||
|
||||
|
||||
### MachineInfo
|
||||
|
||||
```go
|
||||
client.MachineInfo()
|
||||
```
|
||||
|
||||
This method returns a cadvisor/info.MachineInfo struct with all the fields filled in. Here is an example return value:
|
||||
|
||||
```
|
||||
(*info.MachineInfo)(0xc208022b10)({
|
||||
NumCores: (int) 4,
|
||||
MemoryCapacity: (int64) 2106028032,
|
||||
Filesystems: ([]info.FsInfo) (len=1 cap=4) {
|
||||
(info.FsInfo) {
|
||||
Device: (string) (len=9) "/dev/sda1",
|
||||
Capacity: (uint64) 19507089408
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
You can see the full specification of the [MachineInfo struct in the source](../info/container.go)
|
||||
|
||||
### ContainerInfo
|
||||
|
||||
Given a container name and a ContainerInfoRequest, will return all information about the specified container. The ContainerInfoRequest struct just has one field, NumStats, which is the number of stat entries that you want returned.
|
||||
|
||||
```go
|
||||
request := info.ContainerInfoRequest{10}
|
||||
sInfo, err := client.ContainerInfo("/docker/d9d3eb10179e6f93a...", &request)
|
||||
```
|
||||
Returns a [ContainerInfo struct](../info/container.go)
|
||||
|
||||
### SubcontainersInfo
|
||||
|
||||
Given a container name and a ContainerInfoRequest, will recursively return all info about the container and all subcontainers contained within the container. The ContainerInfoRequest struct just has one field, NumStats, which is the number of stat entries that you want returned.
|
||||
|
||||
```go
|
||||
request := info.ContainerInfoRequest{10}
|
||||
sInfo, err := client.SubcontainersInfo("/docker", &request)
|
||||
```
|
||||
|
||||
Returns a [ContainerInfo struct](../info/container.go) with the Subcontainers field populated.
|
|
@ -12,7 +12,8 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cadvisor
|
||||
// TODO(cAdvisor): Package comment.
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -20,45 +21,75 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/google/cadvisor/info"
|
||||
)
|
||||
|
||||
// Client represents the base URL for a cAdvisor client.
|
||||
type Client struct {
|
||||
baseUrl string
|
||||
}
|
||||
|
||||
func NewClient(URL string) (*Client, error) {
|
||||
c := &Client{
|
||||
baseUrl: strings.Join([]string{
|
||||
URL,
|
||||
"api/v1.0",
|
||||
}, "/"),
|
||||
// NewClient returns a new client with the specified base URL.
|
||||
func NewClient(url string) (*Client, error) {
|
||||
if !strings.HasSuffix(url, "/") {
|
||||
url += "/"
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
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.
|
||||
// A non-nil error result indicates a problem with obtaining
|
||||
// the JSON machine information data.
|
||||
func (self *Client) MachineInfo() (minfo *info.MachineInfo, err error) {
|
||||
u := self.machineInfoUrl()
|
||||
ret := new(info.MachineInfo)
|
||||
err = self.httpGetJsonData(ret, nil, u, "machine info")
|
||||
if err != nil {
|
||||
if err = self.httpGetJsonData(ret, nil, u, "machine info"); err != nil {
|
||||
return
|
||||
}
|
||||
minfo = ret
|
||||
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 {
|
||||
|
@ -84,23 +115,9 @@ func (self *Client) httpGetJsonData(data, postData interface{}, url, infoName st
|
|||
err = fmt.Errorf("unable to read all %v: %v", infoName, err)
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(body, data)
|
||||
if err != nil {
|
||||
if err = json.Unmarshal(body, data); err != nil {
|
||||
err = fmt.Errorf("unable to unmarshal %v (%v): %v", infoName, string(body), err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Client) ContainerInfo(
|
||||
name string,
|
||||
query *info.ContainerInfoRequest) (cinfo *info.ContainerInfo, err error) {
|
||||
u := self.containerInfoUrl(name)
|
||||
ret := new(info.ContainerInfo)
|
||||
err = self.httpGetJsonData(ret, query, u, fmt.Sprintf("container info for %v", name))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cinfo = ret
|
||||
return
|
||||
}
|
||||
|
|
|
@ -12,13 +12,14 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cadvisor
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -47,8 +48,7 @@ func cadvisorTestClient(path string, expectedPostObj, expectedPostObjEmpty, repl
|
|||
if r.URL.Path == path {
|
||||
if expectedPostObj != nil {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(expectedPostObjEmpty)
|
||||
if err != nil {
|
||||
if err := decoder.Decode(expectedPostObjEmpty); err != nil {
|
||||
t.Errorf("Received invalid object: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(expectedPostObj, expectedPostObjEmpty) {
|
||||
|
@ -57,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)
|
||||
|
@ -72,12 +72,14 @@ func cadvisorTestClient(path string, expectedPostObj, expectedPostObjEmpty, repl
|
|||
return client, ts, err
|
||||
}
|
||||
|
||||
// TestGetMachineInfo performs one test to check if MachineInfo()
|
||||
// in a cAdvisor client returns the correct result.
|
||||
func TestGetMachineinfo(t *testing.T) {
|
||||
minfo := &info.MachineInfo{
|
||||
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)
|
||||
}
|
||||
|
@ -91,13 +93,15 @@ func TestGetMachineinfo(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestGetContainerInfo generates a random container information object
|
||||
// and then checks that ContainerInfo returns the expected result.
|
||||
func TestGetContainerInfo(t *testing.T) {
|
||||
query := &info.ContainerInfoRequest{
|
||||
NumStats: 3,
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
@ -111,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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,8 +40,15 @@ type MemorySpec struct {
|
|||
}
|
||||
|
||||
type ContainerSpec struct {
|
||||
Cpu *CpuSpec `json:"cpu,omitempty"`
|
||||
Memory *MemorySpec `json:"memory,omitempty"`
|
||||
HasCpu bool `json:"has_cpu"`
|
||||
Cpu CpuSpec `json:"cpu,omitempty"`
|
||||
|
||||
HasMemory bool `json:"has_memory"`
|
||||
Memory MemorySpec `json:"memory,omitempty"`
|
||||
|
||||
HasNetwork bool `json:"has_network"`
|
||||
|
||||
HasFilesystem bool `json:"has_filesystem"`
|
||||
}
|
||||
|
||||
// Container reference contains enough information to uniquely identify a container
|
||||
|
@ -66,7 +73,7 @@ type ContainerInfo struct {
|
|||
Subcontainers []ContainerReference `json:"subcontainers,omitempty"`
|
||||
|
||||
// The isolation used in the container.
|
||||
Spec *ContainerSpec `json:"spec,omitempty"`
|
||||
Spec ContainerSpec `json:"spec,omitempty"`
|
||||
|
||||
// Historical statistics gathered from the container.
|
||||
Stats []*ContainerStats `json:"stats,omitempty"`
|
||||
|
@ -166,6 +173,19 @@ type CpuStats struct {
|
|||
Load int32 `json:"load"`
|
||||
}
|
||||
|
||||
type PerDiskStats struct {
|
||||
Major uint64 `json:"major"`
|
||||
Minor uint64 `json:"minor"`
|
||||
Stats map[string]uint64 `json:"stats"`
|
||||
}
|
||||
|
||||
type DiskIoStats struct {
|
||||
IoServiceBytes []PerDiskStats `json:"io_service_bytes,omitempty"`
|
||||
IoServiced []PerDiskStats `json:"io_serviced,omitempty"`
|
||||
IoQueued []PerDiskStats `json:"io_queued,omitempty"`
|
||||
Sectors []PerDiskStats `json:"sectors,omitempty"`
|
||||
}
|
||||
|
||||
type MemoryStats struct {
|
||||
// Memory limit, equivalent to "limit" in MemorySpec.
|
||||
// Units: Bytes.
|
||||
|
@ -211,12 +231,26 @@ type NetworkStats struct {
|
|||
TxDropped uint64 `json:"tx_dropped"`
|
||||
}
|
||||
|
||||
type FsStats struct {
|
||||
// The block device name associated with the filesystem.
|
||||
Device string `json:"device,omitempty"`
|
||||
|
||||
// Number of bytes that can be consumed by the container on this filesystem.
|
||||
Limit uint64 `json:"capacity"`
|
||||
|
||||
// Number of bytes that is consumed by the container on this filesystem.
|
||||
Usage uint64 `json:"usage"`
|
||||
}
|
||||
|
||||
type ContainerStats struct {
|
||||
// The time of this stat point.
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Cpu *CpuStats `json:"cpu,omitempty"`
|
||||
DiskIo DiskIoStats `json:"diskio,omitempty"`
|
||||
Memory *MemoryStats `json:"memory,omitempty"`
|
||||
Network *NetworkStats `json:"network,omitempty"`
|
||||
// Filesystem statistics
|
||||
Filesystem []FsStats `json:"filesystem,omitempty"`
|
||||
}
|
||||
|
||||
// Makes a deep copy of the ContainerStats and returns a pointer to the new
|
||||
|
|
|
@ -14,12 +14,23 @@
|
|||
|
||||
package info
|
||||
|
||||
type FsInfo struct {
|
||||
// Block device associated with the filesystem.
|
||||
Device string `json:"device"`
|
||||
|
||||
// Total number of bytes available on the filesystem.
|
||||
Capacity uint64 `json:"capacity"`
|
||||
}
|
||||
|
||||
type MachineInfo struct {
|
||||
// The number of cores in this machine.
|
||||
NumCores int `json:"num_cores"`
|
||||
|
||||
// The amount of memory (in bytes) in this machine
|
||||
MemoryCapacity int64 `json:"memory_capacity"`
|
||||
|
||||
// Filesystems on this machine.
|
||||
Filesystems []FsInfo `json:"filesystems"`
|
||||
}
|
||||
|
||||
type VersionInfo struct {
|
||||
|
|
|
@ -51,10 +51,10 @@ func GenerateRandomStats(numStats, numCores int, duration time.Duration) []*info
|
|||
return ret
|
||||
}
|
||||
|
||||
func GenerateRandomContainerSpec(numCores int) *info.ContainerSpec {
|
||||
ret := &info.ContainerSpec{
|
||||
Cpu: &info.CpuSpec{},
|
||||
Memory: &info.MemorySpec{},
|
||||
func GenerateRandomContainerSpec(numCores int) info.ContainerSpec {
|
||||
ret := info.ContainerSpec{
|
||||
Cpu: info.CpuSpec{},
|
||||
Memory: info.MemorySpec{},
|
||||
}
|
||||
ret.Cpu.Limit = uint64(1000 + rand.Int63n(2000))
|
||||
ret.Cpu.MaxLimit = uint64(1000 + rand.Int63n(2000))
|
||||
|
|
|
@ -15,4 +15,4 @@
|
|||
package info
|
||||
|
||||
// Version of cAdvisor.
|
||||
const VERSION = "0.4.0"
|
||||
const VERSION = "0.5.0"
|
||||
|
|
|
@ -45,7 +45,7 @@ import (
|
|||
"github.com/coreos/go-etcd/etcd"
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
"github.com/golang/glog"
|
||||
"github.com/google/cadvisor/client"
|
||||
cadvisor "github.com/google/cadvisor/client"
|
||||
)
|
||||
|
||||
const defaultRootDir = "/var/lib/kubelet"
|
||||
|
|
Loading…
Reference in New Issue