let users decide how many stats/samples they want to retrieve
This commit is contained in:
parent
46a9792ca8
commit
4d0b365d43
@ -21,10 +21,10 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/cadvisor/info"
|
||||
"github.com/google/cadvisor/manager"
|
||||
)
|
||||
|
||||
@ -34,9 +34,11 @@ const (
|
||||
MachineApi = "machine"
|
||||
)
|
||||
|
||||
func HandleRequest(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
|
||||
func HandleRequest(m manager.Manager, w http.ResponseWriter, r *http.Request) error {
|
||||
start := time.Now()
|
||||
|
||||
u := r.URL
|
||||
|
||||
// Get API request type.
|
||||
requestType := u.Path[len(ApiResource):]
|
||||
i := strings.Index(requestType, "/")
|
||||
@ -46,7 +48,8 @@ func HandleRequest(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
|
||||
requestType = requestType[:i]
|
||||
}
|
||||
|
||||
if requestType == MachineApi {
|
||||
switch {
|
||||
case requestType == MachineApi:
|
||||
log.Printf("Api - Machine")
|
||||
|
||||
// Get the MachineInfo
|
||||
@ -60,14 +63,21 @@ func HandleRequest(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
|
||||
fmt.Fprintf(w, "Failed to marshall MachineInfo with error: %s", err)
|
||||
}
|
||||
w.Write(out)
|
||||
} else if requestType == ContainersApi {
|
||||
case requestType == ContainersApi:
|
||||
// The container name is the path after the requestType
|
||||
containerName := requestArgs
|
||||
|
||||
log.Printf("Api - Container(%s)", containerName)
|
||||
|
||||
var query info.ContainerInfoQuery
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&query)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "unable to decode the json value: %v", err)
|
||||
return err
|
||||
}
|
||||
// Get the container.
|
||||
cont, err := m.GetContainerInfo(containerName)
|
||||
cont, err := m.GetContainerInfo(containerName, &query)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "Failed to get container \"%s\" with error: %s", containerName, err)
|
||||
return err
|
||||
@ -79,7 +89,7 @@ func HandleRequest(m manager.Manager, w http.ResponseWriter, u *url.URL) error {
|
||||
fmt.Fprintf(w, "Failed to marshall container %q with error: %s", containerName, err)
|
||||
}
|
||||
w.Write(out)
|
||||
} else {
|
||||
default:
|
||||
return fmt.Errorf("unknown API request type %q", requestType)
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ func main() {
|
||||
|
||||
// Handler for the API.
|
||||
http.HandleFunc(api.ApiResource, func(w http.ResponseWriter, r *http.Request) {
|
||||
err := api.HandleRequest(containerManager, w, r.URL)
|
||||
err := api.HandleRequest(containerManager, w, r)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
|
@ -57,6 +57,34 @@ type ContainerReference struct {
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
}
|
||||
|
||||
type ContainerInfoQuery struct {
|
||||
NumStats int `json:"num_stats,omitempty"`
|
||||
NumSamples int `json:"num_samples,omitempty"`
|
||||
|
||||
CpuUsagePercentages []int `json:"cpu_usage_percentages,omitempty"`
|
||||
MemoryUsagePercentages []int `json:"memory_usage_percentages,omitempty"`
|
||||
}
|
||||
|
||||
func (self *ContainerInfoQuery) FillWithDefaultValues() *ContainerInfoQuery {
|
||||
ret := self
|
||||
if ret == nil {
|
||||
ret = new(ContainerInfoQuery)
|
||||
}
|
||||
if ret.NumStats <= 0 {
|
||||
ret.NumStats = 1024
|
||||
}
|
||||
if ret.NumSamples <= 0 {
|
||||
ret.NumSamples = 1024
|
||||
}
|
||||
if len(ret.CpuUsagePercentages) == 0 {
|
||||
ret.CpuUsagePercentages = []int{50, 80, 90, 99}
|
||||
}
|
||||
if len(ret.MemoryUsagePercentages) == 0 {
|
||||
ret.MemoryUsagePercentages = []int{50, 80, 90, 99}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
type ContainerInfo struct {
|
||||
ContainerReference
|
||||
|
||||
|
@ -46,6 +46,7 @@ func GenerateRandomStats(numStats, numCores int, duration time.Duration) []*info
|
||||
stats.Cpu.Usage.User = stats.Cpu.Usage.Total
|
||||
stats.Cpu.Usage.System = 0
|
||||
stats.Memory.Usage = uint64(rand.Int63n(4096))
|
||||
ret[i] = stats
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ type Manager interface {
|
||||
Start() error
|
||||
|
||||
// Get information about a container.
|
||||
GetContainerInfo(containerName string) (*info.ContainerInfo, error)
|
||||
GetContainerInfo(containerName string, query *info.ContainerInfoQuery) (*info.ContainerInfo, error)
|
||||
|
||||
// Get information about the machine.
|
||||
GetMachineInfo() (*info.MachineInfo, error)
|
||||
@ -106,8 +106,8 @@ func (m *manager) Start() error {
|
||||
}
|
||||
|
||||
// Get a container by name.
|
||||
func (m *manager) GetContainerInfo(containerName string) (*info.ContainerInfo, error) {
|
||||
log.Printf("Get(%s)", containerName)
|
||||
func (m *manager) GetContainerInfo(containerName string, query *info.ContainerInfoQuery) (*info.ContainerInfo, error) {
|
||||
log.Printf("Get(%s); %+v", containerName, query)
|
||||
var cont *containerData
|
||||
var ok bool
|
||||
func() {
|
||||
@ -130,21 +130,21 @@ func (m *manager) GetContainerInfo(containerName string) (*info.ContainerInfo, e
|
||||
var percentiles *info.ContainerStatsPercentiles
|
||||
var samples []*info.ContainerStatsSample
|
||||
var stats []*info.ContainerStats
|
||||
// TODO(monnand): These numbers should not be hard coded
|
||||
query = query.FillWithDefaultValues()
|
||||
percentiles, err = m.storageDriver.Percentiles(
|
||||
cinfo.Name,
|
||||
[]int{50, 80, 90, 99},
|
||||
[]int{50, 80, 90, 99},
|
||||
query.CpuUsagePercentages,
|
||||
query.MemoryUsagePercentages,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
samples, err = m.storageDriver.Samples(cinfo.Name, 1024)
|
||||
samples, err = m.storageDriver.Samples(cinfo.Name, query.NumSamples)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stats, err = m.storageDriver.RecentStats(cinfo.Name, 1024)
|
||||
stats, err = m.storageDriver.RecentStats(cinfo.Name, query.NumStats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
337
manager/manager_test.go
Normal file
337
manager/manager_test.go
Normal file
@ -0,0 +1,337 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Per-container manager.
|
||||
|
||||
package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/cadvisor/container"
|
||||
ctest "github.com/google/cadvisor/container/test"
|
||||
"github.com/google/cadvisor/info"
|
||||
itest "github.com/google/cadvisor/info/test"
|
||||
stest "github.com/google/cadvisor/storage/test"
|
||||
)
|
||||
|
||||
func createManagerAndAddContainers(
|
||||
driver *stest.MockStorageDriver,
|
||||
containers []string,
|
||||
f func(*ctest.MockContainerHandler),
|
||||
t *testing.T,
|
||||
) *manager {
|
||||
if driver == nil {
|
||||
driver = &stest.MockStorageDriver{}
|
||||
}
|
||||
factory := &ctest.FactoryForMockContainerHandler{
|
||||
Name: "factoryForManager",
|
||||
PrepareContainerHandlerFunc: func(name string, handler *ctest.MockContainerHandler) {
|
||||
handler.Name = name
|
||||
found := false
|
||||
for _, c := range containers {
|
||||
if c == name {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Asked to create a container with name %v, which is unknown.", name)
|
||||
}
|
||||
f(handler)
|
||||
},
|
||||
}
|
||||
container.RegisterContainerHandlerFactory("/", factory)
|
||||
mif, err := New(driver)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ret, ok := mif.(*manager); ok {
|
||||
for _, container := range containers {
|
||||
ret.containers[container], err = NewContainerData(container, driver)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
t.Fatal("Wrong type")
|
||||
return nil
|
||||
}
|
||||
|
||||
func randomStatsAndSamples(numStats, numSamples int) ([]*info.ContainerStats, []*info.ContainerStatsSample, error) {
|
||||
stats := itest.GenerateRandomStats(numStats, 4, 1*time.Second)
|
||||
samples := make([]*info.ContainerStatsSample, 0, numSamples)
|
||||
for i, s := range stats[1:] {
|
||||
prev := stats[i]
|
||||
sample, err := info.NewSample(prev, s)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Unable to generate sample from %+v and %+v: %v",
|
||||
prev, s, err)
|
||||
}
|
||||
samples = append(samples, sample)
|
||||
if len(samples) == numSamples {
|
||||
break
|
||||
}
|
||||
}
|
||||
return stats, samples, nil
|
||||
}
|
||||
|
||||
func TestGetContainerInfo(t *testing.T) {
|
||||
containers := []string{
|
||||
"/c1",
|
||||
"/c2",
|
||||
}
|
||||
|
||||
query := &info.ContainerInfoQuery{
|
||||
NumStats: 256,
|
||||
NumSamples: 128,
|
||||
CpuUsagePercentages: []int{10, 50, 90},
|
||||
MemoryUsagePercentages: []int{10, 80, 90},
|
||||
}
|
||||
|
||||
infosMap := make(map[string]*info.ContainerInfo, len(containers))
|
||||
handlerMap := make(map[string]*ctest.MockContainerHandler, len(containers))
|
||||
|
||||
for _, container := range containers {
|
||||
stats, samples, err := randomStatsAndSamples(query.NumStats, query.NumSamples)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cpuPercentiles := make([]info.Percentile, 0, len(query.CpuUsagePercentages))
|
||||
for _, p := range query.CpuUsagePercentages {
|
||||
percentile := info.Percentile{p, uint64(rand.Int63n(1000))}
|
||||
cpuPercentiles = append(cpuPercentiles, percentile)
|
||||
}
|
||||
memPercentiles := make([]info.Percentile, 0, len(query.MemoryUsagePercentages))
|
||||
for _, p := range query.MemoryUsagePercentages {
|
||||
percentile := info.Percentile{p, uint64(rand.Int63n(1000))}
|
||||
memPercentiles = append(memPercentiles, percentile)
|
||||
}
|
||||
|
||||
percentiles := &info.ContainerStatsPercentiles{
|
||||
MaxMemoryUsage: uint64(rand.Int63n(4096)),
|
||||
MemoryUsagePercentiles: memPercentiles,
|
||||
CpuUsagePercentiles: cpuPercentiles,
|
||||
}
|
||||
|
||||
spec := itest.GenerateRandomContainerSpec(4)
|
||||
|
||||
infosMap[container] = &info.ContainerInfo{
|
||||
ContainerReference: info.ContainerReference{
|
||||
Name: container,
|
||||
},
|
||||
Spec: spec,
|
||||
StatsPercentiles: percentiles,
|
||||
Samples: samples,
|
||||
Stats: stats,
|
||||
}
|
||||
}
|
||||
|
||||
driver := &stest.MockStorageDriver{}
|
||||
m := createManagerAndAddContainers(
|
||||
driver,
|
||||
containers,
|
||||
func(h *ctest.MockContainerHandler) {
|
||||
cinfo := infosMap[h.Name]
|
||||
stats := cinfo.Stats
|
||||
samples := cinfo.Samples
|
||||
percentiles := cinfo.StatsPercentiles
|
||||
spec := cinfo.Spec
|
||||
driver.On(
|
||||
"Percentiles",
|
||||
h.Name,
|
||||
query.CpuUsagePercentages,
|
||||
query.MemoryUsagePercentages,
|
||||
).Return(
|
||||
percentiles,
|
||||
nil,
|
||||
)
|
||||
|
||||
driver.On(
|
||||
"Samples",
|
||||
h.Name,
|
||||
query.NumSamples,
|
||||
).Return(
|
||||
samples,
|
||||
nil,
|
||||
)
|
||||
|
||||
driver.On(
|
||||
"RecentStats",
|
||||
h.Name,
|
||||
query.NumStats,
|
||||
).Return(
|
||||
stats,
|
||||
nil,
|
||||
)
|
||||
|
||||
h.On("ListContainers", container.LIST_SELF).Return(
|
||||
[]info.ContainerReference(nil),
|
||||
nil,
|
||||
)
|
||||
h.On("GetSpec").Return(
|
||||
spec,
|
||||
nil,
|
||||
)
|
||||
handlerMap[h.Name] = h
|
||||
},
|
||||
t,
|
||||
)
|
||||
|
||||
returnedInfos := make(map[string]*info.ContainerInfo, len(containers))
|
||||
|
||||
for _, container := range containers {
|
||||
cinfo, err := m.GetContainerInfo(container, query)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to get info for container %v: %v", container, err)
|
||||
}
|
||||
returnedInfos[container] = cinfo
|
||||
}
|
||||
|
||||
for container, handler := range handlerMap {
|
||||
handler.AssertExpectations(t)
|
||||
returned := returnedInfos[container]
|
||||
expected := infosMap[container]
|
||||
if !reflect.DeepEqual(returned, expected) {
|
||||
t.Errorf("returned unexpected info for container %v; returned %+v; expected %+v", container, returned, expected)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetContainerInfoWithDefaultValue(t *testing.T) {
|
||||
containers := []string{
|
||||
"/c1",
|
||||
"/c2",
|
||||
}
|
||||
|
||||
var query *info.ContainerInfoQuery
|
||||
query = query.FillWithDefaultValues()
|
||||
|
||||
infosMap := make(map[string]*info.ContainerInfo, len(containers))
|
||||
handlerMap := make(map[string]*ctest.MockContainerHandler, len(containers))
|
||||
|
||||
for _, container := range containers {
|
||||
stats, samples, err := randomStatsAndSamples(query.NumStats, query.NumSamples)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cpuPercentiles := make([]info.Percentile, 0, len(query.CpuUsagePercentages))
|
||||
for _, p := range query.CpuUsagePercentages {
|
||||
percentile := info.Percentile{p, uint64(rand.Int63n(1000))}
|
||||
cpuPercentiles = append(cpuPercentiles, percentile)
|
||||
}
|
||||
memPercentiles := make([]info.Percentile, 0, len(query.MemoryUsagePercentages))
|
||||
for _, p := range query.MemoryUsagePercentages {
|
||||
percentile := info.Percentile{p, uint64(rand.Int63n(1000))}
|
||||
memPercentiles = append(memPercentiles, percentile)
|
||||
}
|
||||
|
||||
percentiles := &info.ContainerStatsPercentiles{
|
||||
MaxMemoryUsage: uint64(rand.Int63n(4096)),
|
||||
MemoryUsagePercentiles: memPercentiles,
|
||||
CpuUsagePercentiles: cpuPercentiles,
|
||||
}
|
||||
|
||||
spec := itest.GenerateRandomContainerSpec(4)
|
||||
|
||||
infosMap[container] = &info.ContainerInfo{
|
||||
ContainerReference: info.ContainerReference{
|
||||
Name: container,
|
||||
},
|
||||
Spec: spec,
|
||||
StatsPercentiles: percentiles,
|
||||
Samples: samples,
|
||||
Stats: stats,
|
||||
}
|
||||
}
|
||||
|
||||
driver := &stest.MockStorageDriver{}
|
||||
m := createManagerAndAddContainers(
|
||||
driver,
|
||||
containers,
|
||||
func(h *ctest.MockContainerHandler) {
|
||||
cinfo := infosMap[h.Name]
|
||||
stats := cinfo.Stats
|
||||
samples := cinfo.Samples
|
||||
percentiles := cinfo.StatsPercentiles
|
||||
spec := cinfo.Spec
|
||||
driver.On(
|
||||
"Percentiles",
|
||||
h.Name,
|
||||
query.CpuUsagePercentages,
|
||||
query.MemoryUsagePercentages,
|
||||
).Return(
|
||||
percentiles,
|
||||
nil,
|
||||
)
|
||||
|
||||
driver.On(
|
||||
"Samples",
|
||||
h.Name,
|
||||
query.NumSamples,
|
||||
).Return(
|
||||
samples,
|
||||
nil,
|
||||
)
|
||||
|
||||
driver.On(
|
||||
"RecentStats",
|
||||
h.Name,
|
||||
query.NumStats,
|
||||
).Return(
|
||||
stats,
|
||||
nil,
|
||||
)
|
||||
|
||||
h.On("ListContainers", container.LIST_SELF).Return(
|
||||
[]info.ContainerReference(nil),
|
||||
nil,
|
||||
)
|
||||
h.On("GetSpec").Return(
|
||||
spec,
|
||||
nil,
|
||||
)
|
||||
handlerMap[h.Name] = h
|
||||
},
|
||||
t,
|
||||
)
|
||||
|
||||
returnedInfos := make(map[string]*info.ContainerInfo, len(containers))
|
||||
|
||||
for _, container := range containers {
|
||||
// nil should give us default values
|
||||
cinfo, err := m.GetContainerInfo(container, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to get info for container %v: %v", container, err)
|
||||
}
|
||||
returnedInfos[container] = cinfo
|
||||
}
|
||||
|
||||
for container, handler := range handlerMap {
|
||||
handler.AssertExpectations(t)
|
||||
returned := returnedInfos[container]
|
||||
expected := infosMap[container]
|
||||
if !reflect.DeepEqual(returned, expected) {
|
||||
t.Errorf("returned unexpected info for container %v; returned %+v; expected %+v", container, returned, expected)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -162,7 +162,7 @@ func ServerContainersPage(m manager.Manager, w http.ResponseWriter, u *url.URL)
|
||||
containerName := u.Path[len(ContainersPage)-1:]
|
||||
|
||||
// Get the container.
|
||||
cont, err := m.GetContainerInfo(containerName)
|
||||
cont, err := m.GetContainerInfo(containerName, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get container \"%s\" with error: %s", containerName, err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user