Add recursive and type options (raw/docker) to spec and summary endpoints.
spec, stats, and summary now all support the same options (except count, which is only parsed for stats).
This commit is contained in:
parent
d8b9a50994
commit
6db3717426
@ -18,7 +18,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/google/cadvisor/events"
|
||||
@ -39,8 +38,6 @@ const (
|
||||
storageApi = "storage"
|
||||
attributesApi = "attributes"
|
||||
versionApi = "version"
|
||||
typeName = "name"
|
||||
typeDocker = "docker"
|
||||
)
|
||||
|
||||
// Interface for a cAdvisor API version
|
||||
@ -310,6 +307,10 @@ func (self *version2_0) SupportedRequestTypes() []string {
|
||||
}
|
||||
|
||||
func (self *version2_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
|
||||
opt, err := getRequestOptions(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch requestType {
|
||||
case versionApi:
|
||||
glog.V(2).Infof("Api - Version")
|
||||
@ -342,77 +343,33 @@ func (self *version2_0) HandleRequest(requestType string, request []string, m ma
|
||||
return writeResult(machineInfo, w)
|
||||
case summaryApi:
|
||||
containerName := getContainerName(request)
|
||||
glog.V(2).Infof("Api - Summary(%v)", containerName)
|
||||
glog.V(2).Infof("Api - Summary for container %q, options %+v", containerName, opt)
|
||||
|
||||
stats, err := m.GetContainerDerivedStats(containerName)
|
||||
stats, err := m.GetDerivedStats(containerName, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeResult(stats, w)
|
||||
case statsApi:
|
||||
name := getContainerName(request)
|
||||
sr, err := getStatsRequest(name, r)
|
||||
glog.V(2).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt)
|
||||
conts, err := m.GetRequestedContainersInfo(name, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(2).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, sr)
|
||||
query := info.ContainerInfoRequest{
|
||||
NumStats: sr.Count,
|
||||
}
|
||||
switch sr.IdType {
|
||||
case typeName:
|
||||
contStats := make(map[string][]v2.ContainerStats, 0)
|
||||
if sr.Recursive == false {
|
||||
cont, err := m.GetContainerInfo(name, &query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get container %q: %v", name, err)
|
||||
}
|
||||
contStats[name] = convertStats(cont)
|
||||
} else {
|
||||
containers, err := m.SubcontainersInfo(name, &query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get subcontainers for container %q with error: %s", name, err)
|
||||
}
|
||||
for _, cont := range containers {
|
||||
contStats[cont.Name] = convertStats(cont)
|
||||
}
|
||||
}
|
||||
return writeResult(contStats, w)
|
||||
case typeDocker:
|
||||
contStats := make(map[string][]v2.ContainerStats, 0)
|
||||
if name == "/" {
|
||||
// special case: get all docker containers.
|
||||
if sr.Recursive == false {
|
||||
return fmt.Errorf("unknown Docker container %q", name)
|
||||
}
|
||||
containers, err := m.AllDockerContainers(&query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get all docker containers: %v", err)
|
||||
}
|
||||
for name, cont := range containers {
|
||||
contStats[name] = convertStats(&cont)
|
||||
}
|
||||
} else {
|
||||
name = strings.TrimPrefix(name, "/")
|
||||
cont, err := m.DockerContainer(name, &query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get Docker container %q with error: %v", name, err)
|
||||
}
|
||||
contStats[cont.Name] = convertStats(&cont)
|
||||
}
|
||||
return writeResult(contStats, w)
|
||||
default:
|
||||
return fmt.Errorf("unknown id type %q for container name %q", sr.IdType, name)
|
||||
contStats := make(map[string][]v2.ContainerStats, 0)
|
||||
for name, cont := range conts {
|
||||
contStats[name] = convertStats(cont)
|
||||
}
|
||||
return writeResult(contStats, w)
|
||||
case specApi:
|
||||
containerName := getContainerName(request)
|
||||
glog.V(2).Infof("Api - Spec(%v)", containerName)
|
||||
spec, err := m.GetContainerSpec(containerName)
|
||||
glog.V(2).Infof("Api - Spec for container %q, options %+v", containerName, opt)
|
||||
specs, err := m.GetContainerSpec(containerName, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeResult(spec, w)
|
||||
return writeResult(specs, w)
|
||||
case storageApi:
|
||||
var err error
|
||||
fi := []v2.FsInfo{}
|
||||
@ -469,35 +426,35 @@ func convertStats(cont *info.ContainerInfo) []v2.ContainerStats {
|
||||
return stats
|
||||
}
|
||||
|
||||
func getStatsRequest(id string, r *http.Request) (v2.StatsRequest, error) {
|
||||
func getRequestOptions(r *http.Request) (v2.RequestOptions, error) {
|
||||
supportedTypes := map[string]bool{
|
||||
typeName: true,
|
||||
typeDocker: true,
|
||||
v2.TypeName: true,
|
||||
v2.TypeDocker: true,
|
||||
}
|
||||
// fill in the defaults.
|
||||
sr := v2.StatsRequest{
|
||||
IdType: typeName,
|
||||
opt := v2.RequestOptions{
|
||||
IdType: v2.TypeName,
|
||||
Count: 64,
|
||||
Recursive: false,
|
||||
}
|
||||
idType := r.URL.Query().Get("type")
|
||||
if len(idType) != 0 {
|
||||
if !supportedTypes[idType] {
|
||||
return sr, fmt.Errorf("unknown 'type' %q for container name %q", idType, id)
|
||||
return opt, fmt.Errorf("unknown 'type' %q", idType)
|
||||
}
|
||||
sr.IdType = idType
|
||||
opt.IdType = idType
|
||||
}
|
||||
count := r.URL.Query().Get("count")
|
||||
if len(count) != 0 {
|
||||
n, err := strconv.ParseUint(count, 10, 32)
|
||||
if err != nil {
|
||||
return sr, fmt.Errorf("failed to parse 'count' option: %v", count)
|
||||
return opt, fmt.Errorf("failed to parse 'count' option: %v", count)
|
||||
}
|
||||
sr.Count = int(n)
|
||||
opt.Count = int(n)
|
||||
}
|
||||
recursive := r.URL.Query().Get("recursive")
|
||||
if recursive == "true" {
|
||||
sr.Recursive = true
|
||||
opt.Recursive = true
|
||||
}
|
||||
return sr, nil
|
||||
return opt, nil
|
||||
}
|
||||
|
@ -22,6 +22,11 @@ import (
|
||||
"github.com/google/cadvisor/info/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
TypeName = "name"
|
||||
TypeDocker = "docker"
|
||||
)
|
||||
|
||||
type CpuSpec struct {
|
||||
// Requested cpu shares. Default is 1024.
|
||||
Limit uint64 `json:"limit"`
|
||||
@ -150,7 +155,7 @@ type FsInfo struct {
|
||||
Labels []string `json:"labels"`
|
||||
}
|
||||
|
||||
type StatsRequest struct {
|
||||
type RequestOptions struct {
|
||||
// Type of container identifier specified - "name", "dockerid", dockeralias"
|
||||
IdType string `json:"type"`
|
||||
// Number of stats to return
|
||||
|
@ -63,11 +63,14 @@ type Manager interface {
|
||||
// Gets information about a specific Docker container. The specified name is within the Docker namespace.
|
||||
DockerContainer(dockerName string, query *info.ContainerInfoRequest) (info.ContainerInfo, error)
|
||||
|
||||
// Gets spec for a container.
|
||||
GetContainerSpec(containerName string) (v2.ContainerSpec, error)
|
||||
// Gets spec for all containers based on request options.
|
||||
GetContainerSpec(containerName string, options v2.RequestOptions) (map[string]v2.ContainerSpec, error)
|
||||
|
||||
// Get derived stats for a container.
|
||||
GetContainerDerivedStats(containerName string) (v2.DerivedStats, error)
|
||||
// Gets summary stats for all containers based on request options.
|
||||
GetDerivedStats(containerName string, options v2.RequestOptions) (map[string]v2.DerivedStats, error)
|
||||
|
||||
// Get info for all requested containers based on the request options.
|
||||
GetRequestedContainersInfo(containerName string, options v2.RequestOptions) (map[string]*info.ContainerInfo, error)
|
||||
|
||||
// Get information about the machine.
|
||||
GetMachineInfo() (*info.MachineInfo, error)
|
||||
@ -297,17 +300,37 @@ func (self *manager) getContainerData(containerName string) (*containerData, err
|
||||
return cont, nil
|
||||
}
|
||||
|
||||
func (self *manager) GetContainerSpec(containerName string) (v2.ContainerSpec, error) {
|
||||
cont, err := self.getContainerData(containerName)
|
||||
func (self *manager) GetDerivedStats(containerName string, options v2.RequestOptions) (map[string]v2.DerivedStats, error) {
|
||||
conts, err := self.getRequestedContainers(containerName, options)
|
||||
if err != nil {
|
||||
return v2.ContainerSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
cinfo, err := cont.GetInfo()
|
||||
stats := make(map[string]v2.DerivedStats)
|
||||
for name, cont := range conts {
|
||||
d, err := cont.DerivedStats()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats[name] = d
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (self *manager) GetContainerSpec(containerName string, options v2.RequestOptions) (map[string]v2.ContainerSpec, error) {
|
||||
conts, err := self.getRequestedContainers(containerName, options)
|
||||
if err != nil {
|
||||
return v2.ContainerSpec{}, err
|
||||
return nil, err
|
||||
}
|
||||
spec := self.getV2Spec(cinfo)
|
||||
return spec, nil
|
||||
specs := make(map[string]v2.ContainerSpec)
|
||||
for name, cont := range conts {
|
||||
cinfo, err := cont.GetInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spec := self.getV2Spec(cinfo)
|
||||
specs[name] = spec
|
||||
}
|
||||
return specs, nil
|
||||
}
|
||||
|
||||
// Get V2 container spec from v1 container info.
|
||||
@ -377,22 +400,34 @@ func (self *manager) containerDataToContainerInfo(cont *containerData, query *in
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *manager) SubcontainersInfo(containerName string, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error) {
|
||||
var containersMap map[string]*containerData
|
||||
func() {
|
||||
self.containersLock.RLock()
|
||||
defer self.containersLock.RUnlock()
|
||||
containersMap = make(map[string]*containerData, len(self.containers))
|
||||
func (self *manager) getContainer(containerName string) (*containerData, error) {
|
||||
self.containersLock.RLock()
|
||||
defer self.containersLock.RUnlock()
|
||||
cont, ok := self.containers[namespacedContainerName{Name: containerName}]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown container %q", containerName)
|
||||
}
|
||||
return cont, nil
|
||||
}
|
||||
|
||||
// Get all the unique subcontainers of the specified container
|
||||
matchedName := path.Join(containerName, "/")
|
||||
for i := range self.containers {
|
||||
name := self.containers[i].info.Name
|
||||
if name == containerName || strings.HasPrefix(name, matchedName) {
|
||||
containersMap[self.containers[i].info.Name] = self.containers[i]
|
||||
}
|
||||
func (self *manager) getSubcontainers(containerName string) map[string]*containerData {
|
||||
self.containersLock.RLock()
|
||||
defer self.containersLock.RUnlock()
|
||||
containersMap := make(map[string]*containerData, len(self.containers))
|
||||
|
||||
// Get all the unique subcontainers of the specified container
|
||||
matchedName := path.Join(containerName, "/")
|
||||
for i := range self.containers {
|
||||
name := self.containers[i].info.Name
|
||||
if name == containerName || strings.HasPrefix(name, matchedName) {
|
||||
containersMap[self.containers[i].info.Name] = self.containers[i]
|
||||
}
|
||||
}()
|
||||
}
|
||||
return containersMap
|
||||
}
|
||||
|
||||
func (self *manager) SubcontainersInfo(containerName string, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error) {
|
||||
containersMap := self.getSubcontainers(containerName)
|
||||
|
||||
containers := make([]*containerData, 0, len(containersMap))
|
||||
for _, cont := range containersMap {
|
||||
@ -401,20 +436,22 @@ func (self *manager) SubcontainersInfo(containerName string, query *info.Contain
|
||||
return self.containerDataSliceToContainerInfoSlice(containers, query)
|
||||
}
|
||||
|
||||
func (self *manager) AllDockerContainers(query *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error) {
|
||||
var containers map[string]*containerData
|
||||
func() {
|
||||
self.containersLock.RLock()
|
||||
defer self.containersLock.RUnlock()
|
||||
containers = make(map[string]*containerData, len(self.containers))
|
||||
func (self *manager) getAllDockerContainers() map[string]*containerData {
|
||||
self.containersLock.RLock()
|
||||
defer self.containersLock.RUnlock()
|
||||
containers := make(map[string]*containerData, len(self.containers))
|
||||
|
||||
// Get containers in the Docker namespace.
|
||||
for name, cont := range self.containers {
|
||||
if name.Namespace == docker.DockerNamespace {
|
||||
containers[cont.info.Name] = cont
|
||||
}
|
||||
// Get containers in the Docker namespace.
|
||||
for name, cont := range self.containers {
|
||||
if name.Namespace == docker.DockerNamespace {
|
||||
containers[cont.info.Name] = cont
|
||||
}
|
||||
}()
|
||||
}
|
||||
return containers
|
||||
}
|
||||
|
||||
func (self *manager) AllDockerContainers(query *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error) {
|
||||
containers := self.getAllDockerContainers()
|
||||
|
||||
output := make(map[string]info.ContainerInfo, len(containers))
|
||||
for name, cont := range containers {
|
||||
@ -427,23 +464,25 @@ func (self *manager) AllDockerContainers(query *info.ContainerInfoRequest) (map[
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (self *manager) DockerContainer(containerName string, query *info.ContainerInfoRequest) (info.ContainerInfo, error) {
|
||||
var container *containerData = nil
|
||||
func() {
|
||||
self.containersLock.RLock()
|
||||
defer self.containersLock.RUnlock()
|
||||
func (self *manager) getDockerContainer(containerName string) (*containerData, error) {
|
||||
self.containersLock.RLock()
|
||||
defer self.containersLock.RUnlock()
|
||||
|
||||
// Check for the container in the Docker container namespace.
|
||||
cont, ok := self.containers[namespacedContainerName{
|
||||
Namespace: docker.DockerNamespace,
|
||||
Name: containerName,
|
||||
}]
|
||||
if ok {
|
||||
container = cont
|
||||
}
|
||||
}()
|
||||
if container == nil {
|
||||
return info.ContainerInfo{}, fmt.Errorf("unable to find Docker container %q", containerName)
|
||||
// Check for the container in the Docker container namespace.
|
||||
cont, ok := self.containers[namespacedContainerName{
|
||||
Namespace: docker.DockerNamespace,
|
||||
Name: containerName,
|
||||
}]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to find Docker container %q", containerName)
|
||||
}
|
||||
return cont, nil
|
||||
}
|
||||
|
||||
func (self *manager) DockerContainer(containerName string, query *info.ContainerInfoRequest) (info.ContainerInfo, error) {
|
||||
container, err := self.getDockerContainer(containerName)
|
||||
if err != nil {
|
||||
return info.ContainerInfo{}, err
|
||||
}
|
||||
|
||||
inf, err := self.containerDataToContainerInfo(container, query)
|
||||
@ -472,18 +511,60 @@ func (self *manager) containerDataSliceToContainerInfoSlice(containers []*contai
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (self *manager) GetContainerDerivedStats(containerName string) (v2.DerivedStats, error) {
|
||||
var ok bool
|
||||
var cont *containerData
|
||||
func() {
|
||||
self.containersLock.RLock()
|
||||
defer self.containersLock.RUnlock()
|
||||
cont, ok = self.containers[namespacedContainerName{Name: containerName}]
|
||||
}()
|
||||
if !ok {
|
||||
return v2.DerivedStats{}, fmt.Errorf("unknown container %q", containerName)
|
||||
func (self *manager) GetRequestedContainersInfo(containerName string, options v2.RequestOptions) (map[string]*info.ContainerInfo, error) {
|
||||
containers, err := self.getRequestedContainers(containerName, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cont.DerivedStats()
|
||||
containersMap := make(map[string]*info.ContainerInfo)
|
||||
query := info.ContainerInfoRequest{
|
||||
NumStats: options.Count,
|
||||
}
|
||||
for name, data := range containers {
|
||||
info, err := self.containerDataToContainerInfo(data, &query)
|
||||
if err != nil {
|
||||
// Skip containers with errors, we try to degrade gracefully.
|
||||
continue
|
||||
}
|
||||
containersMap[name] = info
|
||||
}
|
||||
return containersMap, nil
|
||||
}
|
||||
|
||||
func (self *manager) getRequestedContainers(containerName string, options v2.RequestOptions) (map[string]*containerData, error) {
|
||||
containersMap := make(map[string]*containerData)
|
||||
switch options.IdType {
|
||||
case v2.TypeName:
|
||||
if options.Recursive == false {
|
||||
cont, err := self.getContainer(containerName)
|
||||
if err != nil {
|
||||
return containersMap, err
|
||||
}
|
||||
containersMap[cont.info.Name] = cont
|
||||
} else {
|
||||
containersMap = self.getSubcontainers(containerName)
|
||||
if len(containersMap) == 0 {
|
||||
return containersMap, fmt.Errorf("unknown container: %q", containerName)
|
||||
}
|
||||
}
|
||||
case v2.TypeDocker:
|
||||
if options.Recursive == false {
|
||||
containerName = strings.TrimPrefix(containerName, "/")
|
||||
cont, err := self.getDockerContainer(containerName)
|
||||
if err != nil {
|
||||
return containersMap, err
|
||||
}
|
||||
containersMap[cont.info.Name] = cont
|
||||
} else {
|
||||
if containerName != "/" {
|
||||
return containersMap, fmt.Errorf("invalid request for docker container %q with subcontainers", containerName)
|
||||
}
|
||||
containersMap = self.getAllDockerContainers()
|
||||
}
|
||||
default:
|
||||
return containersMap, fmt.Errorf("invalid request type %q", options.IdType)
|
||||
}
|
||||
return containersMap, nil
|
||||
}
|
||||
|
||||
func (self *manager) GetFsInfo(label string) ([]v2.FsInfo, error) {
|
||||
|
@ -55,14 +55,19 @@ func (c *ManagerMock) DockerContainer(name string, query *info.ContainerInfoRequ
|
||||
return args.Get(0).(info.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetContainerSpec(containerName string) (info.ContainerSpec, error) {
|
||||
args := c.Called(containerName)
|
||||
return args.Get(0).(info.ContainerSpec), args.Error(1)
|
||||
func (c *ManagerMock) GetContainerSpec(containerName string, options v2.RequestOptions) (map[string]v2.ContainerSpec, error) {
|
||||
args := c.Called(containerName, options)
|
||||
return args.Get(0).(map[string]v2.ContainerSpec), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetContainerDerivedStats(containerName string) (v2.DerivedStats, error) {
|
||||
args := c.Called(containerName)
|
||||
return args.Get(0).(v2.DerivedStats), args.Error(1)
|
||||
func (c *ManagerMock) GetDerivedStats(containerName string, options v2.RequestOptions) (map[string]v2.DerivedStats, error) {
|
||||
args := c.Called(containerName, options)
|
||||
return args.Get(0).(map[string]v2.DerivedStats), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) GetRequestedContainersInfo(containerName string, options v2.RequestOptions) (map[string]*info.ContainerInfo, error) {
|
||||
args := c.Called(containerName, options)
|
||||
return args.Get(0).(map[string]*info.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (c *ManagerMock) WatchForEvents(queryuest *events.Request, passedChannel chan *events.Event) error {
|
||||
|
Loading…
Reference in New Issue
Block a user