Skip to content

Commit

Permalink
Add global /usage endpoint for containers
Browse files Browse the repository at this point in the history
  • Loading branch information
Soulou committed Nov 4, 2015
1 parent 9f6d49d commit 6307ed7
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 39 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# CHANGELOG

## v0.3.0

* /containers/:id/cpu returns an object with `.usage_in_percents`
* /containers/:id/memory drops `.swap_memory_usage`, `.swap_memory_limit` and `.max_swap_memory`
* /containers/:id/memory get `.swap_usage`, `.swap_limit` and `.max_swap_usage`
* NEW /containers/:id/usage to get cpu + mem and optionally net usage

## v0.2.4

* Remove dependency to app from API client (client package)
Expand Down
22 changes: 11 additions & 11 deletions Godeps/_workspace/src/github.com/Scalingo/go-netstat/netstat.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 46 additions & 14 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package client

import (
"encoding/json"
"fmt"
"net/http"
"net/url"

"github.com/Scalingo/go-netstat"
"gopkg.in/errgo.v1"
)

Expand All @@ -21,10 +23,22 @@ type CpuUsage struct {
UsageInPercents int `json:"usage_in_percents"`
}

type NetUsage struct {
netstat.NetworkStat
RxBps int64 `json:"rx_bps"`
TxBps int64 `json:"tx_bps"`
}

type Client struct {
Endpoint string
}

type Usage struct {
Memory *MemoryUsage `json:"memory"`
Cpu *CpuUsage `json:"cpu"`
Net *NetUsage `json:"net,omitempty"`
}

func NewClient(endpoint string) (*Client, error) {
_, err := url.Parse(endpoint)
if err != nil {
Expand All @@ -36,47 +50,65 @@ func NewClient(endpoint string) (*Client, error) {
}

func (c *Client) Memory(dockerId string) (*MemoryUsage, error) {
req, err := http.NewRequest("GET", c.Endpoint+"/containers/"+dockerId+"/mem", nil)
var mem *MemoryUsage
err := c.getResource(dockerId, "mem", mem)
if err != nil {
return nil, errgo.Mask(err)
}
return mem, nil
}

res, err := c.do(req)
func (c *Client) CpuUsage(dockerId string) (*CpuUsage, error) {
var cpu *CpuUsage
err := c.getResource(dockerId, "cpu", cpu)
if err != nil {
return nil, errgo.Mask(err)
}
return cpu, nil
}

defer res.Body.Close()

var mem *MemoryUsage
err = json.NewDecoder(res.Body).Decode(&mem)
func (c *Client) NetUsage(dockerId string) (*NetUsage, error) {
var net *NetUsage
err := c.getResource(dockerId, "net", net)
if err != nil {
return nil, errgo.Mask(err)
}

return mem, nil
return net, nil
}

func (c *Client) CpuUsage(dockerId string) (*CpuUsage, error) {
req, err := http.NewRequest("GET", c.Endpoint+"/containers/"+dockerId+"/cpu", nil)
func (c *Client) Usage(dockerId string, net bool) (*Usage, error) {
var usage *Usage
err := c.getResourceWithQuery(dockerId, "usage", fmt.Sprintf("net=%v", net), usage)
if err != nil {
return nil, errgo.Mask(err)
}
return usage, nil
}

func (c *Client) getResource(dockerId, resourceType string, data interface{}) error {
return c.getResourceWithQuery(dockerId, resourceType, "", data)
}

func (c *Client) getResourceWithQuery(dockerId, resourceType string, query string, data interface{}) error {
req, err := http.NewRequest("GET", c.Endpoint+"/containers/"+dockerId+"/"+resourceType+"&"+query, nil)
if err != nil {
return errgo.Mask(err)
}

res, err := c.do(req)
if err != nil {
return nil, errgo.Mask(err)
return errgo.Mask(err)
}

defer res.Body.Close()

var cpu *CpuUsage
err = json.NewDecoder(res.Body).Decode(&cpu)
err = json.NewDecoder(res.Body).Decode(&data)
if err != nil {
return nil, errgo.Mask(err)
return errgo.Mask(err)
}

return cpu, nil
return nil
}

func (c *Client) do(req *http.Request) (*http.Response, error) {
Expand Down
8 changes: 4 additions & 4 deletions cpu/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,20 @@ func monitorContainer(id string) {
}
}

func GetUsage(id string) (*Usage, error) {
func GetUsage(id string) (Usage, error) {
id, err := docker.ExpandId(id)
if err != nil {
log.Println("Error when expanding id:", err)
return nil, err
return Usage{}, err
}
if _, ok := previousCpuUsages[id]; !ok {
return nil, nil
return Usage{}, nil
}
deltaCpuUsage := float64(cpuUsages[id] - previousCpuUsages[id])
deltaSystemCpuUsage := float64(currentSystemUsage[id] - previousSystemUsage[id])

percents := int(deltaCpuUsage / deltaSystemCpuUsage * 100 * float64(runtime.NumCPU()))
return &Usage{
return Usage{
UsageInPercents: percents,
}, nil
}
Expand Down
17 changes: 7 additions & 10 deletions net/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import (
"sync"
"time"

"github.com/Scalingo/go-netstat"
"github.com/Scalingo/acadock-monitoring/client"
"github.com/Scalingo/acadock-monitoring/config"
"github.com/Scalingo/acadock-monitoring/docker"
"github.com/Scalingo/acadock-monitoring/runner"
"github.com/Scalingo/go-netstat"
)

var (
Expand Down Expand Up @@ -59,22 +60,18 @@ func monitorContainer(id string, iface string) {
}
}

type NetUsage struct {
netstat.NetworkStat
RxBps int64
TxBps int64
}
type Usage client.NetUsage

func GetUsage(id string) (*NetUsage, error) {
func GetUsage(id string) (Usage, error) {
id, err := docker.ExpandId(id)
if err != nil {
return nil, err
return Usage{}, err
}
usage := NetUsage{}
usage := Usage{}
usage.NetworkStat = netUsages[id]
usage.RxBps = int64(float64(netUsages[id].Received.Bytes-previousNetUsages[id].Received.Bytes) / float64(config.RefreshTime))
usage.TxBps = int64(float64(netUsages[id].Transmit.Bytes-previousNetUsages[id].Transmit.Bytes) / float64(config.RefreshTime))
return &usage, nil
return usage, nil
}

func stopMonitoring(id string) {
Expand Down
39 changes: 39 additions & 0 deletions server/acadock_monitoring.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,50 @@ import (
"net/http"
"net/http/pprof"

"github.com/Scalingo/acadock-monitoring/client"
"github.com/Scalingo/acadock-monitoring/cpu"
"github.com/Scalingo/acadock-monitoring/mem"
"github.com/Scalingo/acadock-monitoring/net"
"github.com/codegangsta/martini"
)

func containerUsageHandler(res http.ResponseWriter, req *http.Request, params martini.Params) {
id := params["id"]
usage := client.Usage{}

memUsage, err := mem.GetUsage(id)
if err != nil {
res.WriteHeader(500)
res.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(res, err.Error())
return
}
usage.Memory = &memUsage.MemoryUsage

cpuUsage, err := cpu.GetUsage(id)
if err != nil {
res.WriteHeader(500)
res.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(res, err.Error())
return
}
usage.Cpu = (*client.CpuUsage)(&cpuUsage)

if req.URL.Query().Get("net") == "true" {
netUsage, err := net.GetUsage(id)
if err != nil {
res.WriteHeader(500)
res.Header().Set("Content-Type", "text/plain")
res.Write([]byte(err.Error()))
return
}
usage.Net = (*client.NetUsage)(&netUsage)
}

res.WriteHeader(200)
json.NewEncoder(res).Encode(&usage)
}

func containerMemUsageHandler(params martini.Params, res http.ResponseWriter) {
id := params["id"]

Expand Down Expand Up @@ -72,6 +110,7 @@ func main() {
r.Get("/containers/:id/mem", containerMemUsageHandler)
r.Get("/containers/:id/cpu", containerCpuUsageHandler)
r.Get("/containers/:id/net", containerNetUsageHandler)
r.Get("/containers/:id/usage", containerUsageHandler)

if *doProfile {
log.Println("Enable profiling")
Expand Down

0 comments on commit 6307ed7

Please sign in to comment.