diff --git a/CHANGELOG.md b/CHANGELOG.md index c9be2ceb..e31814de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/Godeps/_workspace/src/github.com/Scalingo/go-netstat/netstat.go b/Godeps/_workspace/src/github.com/Scalingo/go-netstat/netstat.go index e2946403..ea451475 100644 --- a/Godeps/_workspace/src/github.com/Scalingo/go-netstat/netstat.go +++ b/Godeps/_workspace/src/github.com/Scalingo/go-netstat/netstat.go @@ -10,20 +10,20 @@ import ( type NetworkStats []NetworkStat type networkInfo struct { - Bytes int64 - Packets int64 - Drop int64 - Errs int64 - Fifo int64 - Frame int64 - Compressed int64 - Multicast int64 + Bytes int64 `json:"bytes"` + Packets int64 `json:"packets"` + Drop int64 `json:"drop"` + Errs int64 `json:"errs"` + Fifo int64 `json:"fifo"` + Frame int64 `json:"frame"` + Compressed int64 `json:"compressed"` + Multicast int64 `json:"multicast"` } type NetworkStat struct { - Interface string - Received networkInfo - Transmit networkInfo + Interface string `json:"interface"` + Received networkInfo `json:"received"` + Transmit networkInfo `json:"transmit"` } func Stats() (NetworkStats, error) { diff --git a/client/client.go b/client/client.go index 8656dc40..b3af11a9 100644 --- a/client/client.go +++ b/client/client.go @@ -2,9 +2,11 @@ package client import ( "encoding/json" + "fmt" "net/http" "net/url" + "github.com/Scalingo/go-netstat" "gopkg.in/errgo.v1" ) @@ -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 { @@ -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) { diff --git a/cpu/cpu.go b/cpu/cpu.go index b4711675..9487e18f 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -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 } diff --git a/net/net.go b/net/net.go index f607bedc..5fcc722f 100644 --- a/net/net.go +++ b/net/net.go @@ -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 ( @@ -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) { diff --git a/server/acadock_monitoring.go b/server/acadock_monitoring.go index e3f5d757..7ab6aad2 100644 --- a/server/acadock_monitoring.go +++ b/server/acadock_monitoring.go @@ -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"] @@ -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")