Skip to content

Commit

Permalink
Metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
vfarcic committed Jul 26, 2017
1 parent 09aa2d9 commit fdc13b3
Show file tree
Hide file tree
Showing 9 changed files with 461 additions and 11 deletions.
32 changes: 32 additions & 0 deletions ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!--
If you suspect your issue is a bug, please edit your issue description to
include the BUG REPORT INFORMATION shown below.
---------------------------------------------------
BUG REPORT INFORMATION
---------------------------------------------------
Use the commands below to provide key information from your environment:
You do NOT have to include this information if this is a FEATURE REQUEST
-->

**Description**

<!--
Briefly describe the problem you are having in a few paragraphs.
-->

**Steps to reproduce the issue:**
1.
2.
3.

**Describe the results you received:**


**Describe the results you expected:**


**Additional information you deem important (e.g. issue happens only occasionally):**


**Additional environment details (AWS, VirtualBox, physical, etc.):**
1 change: 0 additions & 1 deletion haproxy.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ defaults
stats enable
stats refresh 30s
stats realm Strictly\ Private
stats auth admin:admin
stats uri /admin?stats

frontend dummy-fe
Expand Down
33 changes: 33 additions & 0 deletions integration_tests/integration_swarm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,39 @@ func (s IntegrationSwarmTestSuite) Test_Config() {
s.Contains(actual, `{"go-demo-api":`)
}

func (s IntegrationSwarmTestSuite) Test_Metrics() {
defer func() {
exec.Command("/bin/sh", "-c", "docker service scale proxy=1").Output()
s.waitForContainers(1, "proxy")
}()
addr := fmt.Sprintf(
"http://%s:8080/v1/docker-flow-proxy/metrics",
s.hostIP,
)
out, err := exec.Command("/bin/sh", "-c", "docker service scale proxy=3").CombinedOutput()
if err != nil {
s.Fail("%s\n%s", err.Error(), string(out))
} else {
s.waitForContainers(3, "proxy")
}

resp, err := http.Get(addr)
s.NoError(err)

body, _ := ioutil.ReadAll(resp.Body)

// Cannot validate that the metrics are correct but only that some text is returned
s.Contains(string(body[:]), "services,FRONTEND")

resp, err = http.Get(addr + "?distribute=true")
s.NoError(err)

body, _ = ioutil.ReadAll(resp.Body)

// Cannot validate that the metrics are correct but only that some text is returned
s.Contains(string(body[:]), "services,FRONTENDxxx")
}

func (s IntegrationSwarmTestSuite) Test_Compression() {
defer func() {
exec.Command("/bin/sh", "-c", `docker service update --env-rm "COMPRESSION_ALGO" proxy`).Output()
Expand Down
6 changes: 4 additions & 2 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func (m *serve) Execute(args []string) error {
cert,
)
config := server.NewConfig()
metrics := server.NewMetrics("")
if err := m.reconfigure(server2); err != nil {
return err
}
Expand All @@ -58,10 +59,11 @@ func (m *serve) Execute(args []string) error {
r.HandleFunc("/v1/docker-flow-proxy/cert", m.certPutHandler).Methods("PUT")
r.HandleFunc("/v1/docker-flow-proxy/certs", m.certsHandler)
r.HandleFunc("/v1/docker-flow-proxy/config", config.Get)
r.HandleFunc("/v1/docker-flow-proxy/metrics", metrics.Get)
r.HandleFunc("/v1/docker-flow-proxy/ping", server2.PingHandler)
r.HandleFunc("/v1/docker-flow-proxy/reconfigure", server2.ReconfigureHandler)
r.HandleFunc("/v1/docker-flow-proxy/remove", server2.RemoveHandler)
r.HandleFunc("/v1/docker-flow-proxy/reload", server2.ReloadHandler)
r.HandleFunc("/v1/docker-flow-proxy/ping", server2.PingHandler)
r.HandleFunc("/v1/docker-flow-proxy/remove", server2.RemoveHandler)
r.HandleFunc("/v1/test", server2.Test1Handler)
r.HandleFunc("/v2/test", server2.Test2Handler)
if err := httpListenAndServe(address, r); err != nil {
Expand Down
7 changes: 1 addition & 6 deletions server/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ func TestConfigUnitTestSuite(t *testing.T) {
defer func() { proxy.Instance = proxyOrig }()
proxyMock := getProxyMock("")
proxy.Instance = proxyMock

// logPrintfOrig := logPrintf
// defer func() { logPrintf = logPrintfOrig }()
// logPrintf = func(format string, v ...interface{}) {}

s := new(ConfigTestSuite)
suite.Run(t, s)
}
Expand Down Expand Up @@ -103,7 +98,7 @@ func (s *ConfigTestSuite) Test_Get_WritesConfig() {
proxyOrig := proxy.Instance
defer func() { proxy.Instance = proxyOrig }()
proxyMock := getProxyMock("ReadConfig")
proxyMock.On("ReadConfig").Return(expected, fmt.Errorf("This is an error"))
proxyMock.On("ReadConfig").Return(expected, nil)
proxy.Instance = proxyMock

c.Get(w, req)
Expand Down
109 changes: 109 additions & 0 deletions server/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package server

import (
"net/http"
"fmt"
"os"
"strings"
"io/ioutil"
"bytes"
)

// TODO: Test with haproxy_exporter and Prometheus
// NOTE: It does not work until the first service is added
// TODO: Check whether data should be aggregated
// TODO: Document

type Metricer interface {
Get(w http.ResponseWriter, req *http.Request)
GetMetricsUrl() string
}

type Metrics struct {
metricsUrl string
}

func NewMetrics(metricsUrl string) Metricer {
if len(metricsUrl) == 0 {
metricsUrl = fmt.Sprintf("http://%slocalhost/admin?stats;csv", getCreds())
}
return &Metrics{metricsUrl: metricsUrl}
}

func (m *Metrics) GetMetricsUrl() string {
return m.metricsUrl
}

func (m *Metrics) Get(w http.ResponseWriter, req *http.Request) {
contentType := "text/html"
if strings.EqualFold(req.URL.Query().Get("distribute"), "true") {
dns := fmt.Sprintf("tasks.%s", os.Getenv("SERVICE_NAME"))
ips, err := lookupHost(dns)
if err != nil {
logPrintf(err.Error())
w.WriteHeader(http.StatusInternalServerError)
} else {
body, err := m.getAllHaProxyMetrics(req, ips)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
w.Write(body)
}
} else {
body, err := m.getHaProxyMetrics()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
w.Write(body)
}
httpWriterSetContentType(w, contentType)
}

func (m *Metrics) getHaProxyMetrics() ([]byte, error) {
resp, err := http.Get(m.metricsUrl)
if err != nil {
logPrintf("Failed to fetch metrics from %s\nERROR: %s", m.metricsUrl, err.Error())
return []byte(""), err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}

func (m *Metrics) getAllHaProxyMetrics(req *http.Request, ips []string) ([]byte, error) {
msg := []byte("")
for _, ip := range ips {
values := req.URL.Query()
values.Set("distribute", "false")
req.URL.RawQuery = values.Encode()
port := ""
if !strings.Contains(ip, ":") {
port = ":8080"
}
addr := fmt.Sprintf("http://%s%s/v1/docker-flow-proxy/metrics?%s", ip, port, req.URL.RawQuery)
resp, err := http.Get(addr)
if err != nil {
logPrintf("Failed to fetch metrics from %s\nERROR: %s", addr, err.Error())
return []byte(""), err
} else {
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return []byte(""), fmt.Errorf("Got response status %d", resp.StatusCode)
}
body, _ := ioutil.ReadAll(resp.Body)
msg = append(msg, body...)
if !bytes.HasSuffix(msg, []byte("\n")) {
msg = append(msg, byte('\n'))
}
}
}
return msg, nil
}

func getCreds() string {
statsUser := getSecretOrEnvVar(os.Getenv("STATS_USER_ENV"), "")
statsPass := getSecretOrEnvVar(os.Getenv("STATS_PASS_ENV"), "")
if len(statsUser) > 0 && !strings.EqualFold(statsUser, "none") && len(statsPass) > 0 && !strings.EqualFold(statsPass, "none") {
return fmt.Sprintf("%s:%s@", statsUser, statsPass)
}
return ""
}
Loading

0 comments on commit fdc13b3

Please sign in to comment.