Merge ~jacekn/prometheus-registration/+git/prometheus-registration:master into prometheus-registration:master

Proposed by Jacek Nykis
Status: Merged
Approved by: Tom Haddon
Approved revision: 40447fdca8db1f166ab38c9137731518e47e8c0a
Merged at revision: 47e5f85b3fb83cc90c553c46d0412c77f9b50993
Proposed branch: ~jacekn/prometheus-registration/+git/prometheus-registration:master
Merge into: prometheus-registration:master
Diff against target: 199 lines (+64/-21)
5 files modified
README.md (+4/-1)
debian/changelog (+6/-0)
promreg/http.go (+12/-0)
promreg/promreg_test.go (+41/-19)
snapcraft.yaml (+1/-1)
Reviewer Review Type Date Requested Status
Joel Sing (community) +1 Approve
Jacek Nykis (community) Needs Resubmitting
Review via email: mp+333398@code.launchpad.net

Commit message

Add sanity checks when adding new targets. Fixes LP#1722774

Description of the change

Add sanity checks when adding new targets. Fixes LP1722774

To post a comment you must log in.
Revision history for this message
Joel Sing (jsing) :
review: Needs Fixing
Revision history for this message
Jacek Nykis (jacekn) wrote :

Addressed comments from Joel

review: Needs Resubmitting
Revision history for this message
Joel Sing (jsing) wrote :

LGTM, see comment.

review: Approve (+1)
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Unable to determine commit message from repository - please click "Set commit message" and enter the commit message manually.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change has no commit message, setting status to needs review.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 47e5f85b3fb83cc90c553c46d0412c77f9b50993

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/README.md b/README.md
2index b994490..2f1a3f9 100644
3--- a/README.md
4+++ b/README.md
5@@ -121,7 +121,9 @@ With the exception of the last dependency the rest straight forward and can be g
6 The last one is special in that it is not a library and itself contains a vendor directory which causes the build to choke.
7 This dependency is needed as it allows writing the targets.yaml with code from Prometheus to ensure it matches the same file format.
8 To work around this problem I am using [govendor](https://github.com/kardianos/govendor), to pull in all the dependencies.
9-After installing govendor just run `govendor sync` to retrieve dependencies and then build as usual.
10+After installing govendor just run `govendor sync` to retrieve dependencies and then build:
11+
12+ go install launchpad.net/prometheus-registration/cmd/promreg/
13
14 ## Misc
15 While the standard metrics path for nearly any target is `/metrics`, this can be changed by specifying a `__metrics_path__` label when the target is registered. This should work, but can depend on your Prometheus configuration and is untested functionality.
16@@ -131,3 +133,4 @@ While the standard metrics path for nearly any target is `/metrics`, this can be
17 - Implement a mix of HTTP and HTTPS targets. Promreg can manage both but would write out two files and
18 there would be different scrape_config jobs configured in Prometheus, one with "scheme: https".
19 - Implement filtering of list output.
20+- Improve testing so that it does not rely on specific result ordering
21diff --git a/debian/changelog b/debian/changelog
22index f0bdad8..43ffcba 100644
23--- a/debian/changelog
24+++ b/debian/changelog
25@@ -1,3 +1,9 @@
26+promreg (0.0.9) xenial; urgency=medium
27+
28+ * Improve data validation when adding new targets. Fixes LP1722774
29+
30+ -- Jacek Nykis <jacek.nykis@canonical.com> Wed, 08 Nov 2017 17:28:01 +0000
31+
32 promreg (0.0.8) xenial; urgency=medium
33
34 * README improvements and lots of other code cleanup following review.
35diff --git a/promreg/http.go b/promreg/http.go
36index f061d8c..68039e7 100644
37--- a/promreg/http.go
38+++ b/promreg/http.go
39@@ -15,7 +15,9 @@ import (
40 "fmt"
41 "io"
42 "log"
43+ "net"
44 "net/http"
45+ "strconv"
46 "strings"
47
48 "github.com/prometheus/common/model"
49@@ -101,6 +103,16 @@ func (th TargetsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
50 http.Error(w, "Target is undefined", http.StatusBadRequest)
51 return
52 }
53+ // Ensure we add host:port target
54+ _, port, err := net.SplitHostPort(t.Host)
55+ if err != nil {
56+ http.Error(w, fmt.Sprintf("Expected host:port, got: %v. E: %v" , t.Host, err), http.StatusBadRequest)
57+ return
58+ }
59+ if p, err := strconv.ParseUint(port, 10, 16); err != nil || p < 1 || p > 65535 {
60+ http.Error(w, fmt.Sprintf("Invalid port: %v", port), http.StatusBadRequest)
61+ return
62+ }
63 if err := t.ParseBody(r.Body); err != nil {
64 http.Error(w, fmt.Sprintf("Unable to parse request body %v", err), http.StatusBadRequest)
65 return
66diff --git a/promreg/promreg_test.go b/promreg/promreg_test.go
67index d54a5b1..b95bbea 100644
68--- a/promreg/promreg_test.go
69+++ b/promreg/promreg_test.go
70@@ -64,10 +64,11 @@ func TestAPI(t *testing.T) {
71
72 //Add a new target
73 rdr := strings.NewReader(`{"comment": "New web server", "labels": {"labelname1": "labelvalue1"}}`)
74- req = authedRequest("POST", testURL+"/targets/myhost", rdr)
75+ req = authedRequest("POST", testURL+"/targets/myhost:8080", rdr)
76 verifyResponse(t, req, http.StatusAccepted, "")
77- myhost := `{"Host":"myhost","labels":{"labelname1":"labelvalue1"}}`
78- verifyTargets(t, testURL, "["+selfTarget+","+myhost+"]")
79+ myhost := `{"Host":"myhost:8080","labels":{"labelname1":"labelvalue1"}}`
80+ targets := []string{selfTarget, myhost}
81+ verifyTargets(t, testURL, fmt.Sprintf("[%v]", strings.Join(targets, ",")))
82
83 // Verify git commit
84 cmd := exec.Command("git", "-C", tempDir, "log", "-1", "--pretty=format:%s")
85@@ -81,9 +82,24 @@ func TestAPI(t *testing.T) {
86
87 //Add again should error
88 rdr = strings.NewReader(`{"comment": "New web server", "labels": {"labelname1": "labelvalue1"}}`)
89- req = authedRequest("POST", testURL+"/targets/myhost", rdr)
90+ req = authedRequest("POST", testURL+"/targets/myhost:8080", rdr)
91 verifyResponse(t, req, http.StatusInternalServerError, "Storage Error attempting to add an already existing Host, update or delete first\n")
92
93+ //Add target without mandatory port
94+ rdr = strings.NewReader(`{"comment": "New web server", "labels": {"labelname1": "labelvalue1"}}`)
95+ req = authedRequest("POST", testURL+"/targets/host_no_port", rdr)
96+ verifyResponse(t, req, http.StatusBadRequest, "Expected host:port, got: host_no_port. E: missing port in address host_no_port\n")
97+
98+ //Add target with out of range port
99+ rdr = strings.NewReader(`{"comment": "New web server", "labels": {"labelname1": "labelvalue1"}}`)
100+ req = authedRequest("POST", testURL+"/targets/host:99999", rdr)
101+ verifyResponse(t, req, http.StatusBadRequest, "Invalid port: 99999\n")
102+
103+ //Add target with malformed port
104+ rdr = strings.NewReader(`{"comment": "New web server", "labels": {"labelname1": "labelvalue1"}}`)
105+ req = authedRequest("POST", testURL+"/targets/host:9103/metrics", rdr)
106+ verifyResponse(t, req, http.StatusBadRequest, "Invalid port: 9103/metrics\n")
107+
108 //Add unnamed target
109 rdr = strings.NewReader(`{"comment": "New blank server", "labels": {"labelname1": "labelvalue1"}}`)
110 req = authedRequest("POST", testURL+"/targets/", rdr)
111@@ -91,32 +107,35 @@ func TestAPI(t *testing.T) {
112
113 //Add target with invalid JSON body
114 rdr = strings.NewReader("{comment: New web server, labels: {labelname1: labelvalue1}}")
115- req = authedRequest("POST", testURL+"/targets/myhost", rdr)
116+ req = authedRequest("POST", testURL+"/targets/myhost:8080", rdr)
117 verifyResponse(t, req, http.StatusBadRequest, "Unable to parse request body invalid character 'c' looking for beginning of object key string\n")
118
119 //Add a 2nd target with the same labels
120 rdr = strings.NewReader(`{"comment": "New web server2", "labels": {"labelname1": "labelvalue2"}}`)
121- req = authedRequest("POST", testURL+"/targets/myhost2", rdr)
122+ req = authedRequest("POST", testURL+"/targets/myhost2:8080", rdr)
123 verifyResponse(t, req, http.StatusAccepted, "")
124- myhost2 := `{"Host":"myhost2","labels":{"labelname1":"labelvalue2"}}`
125- verifyTargets(t, testURL, "["+selfTarget+","+myhost+","+myhost2+"]")
126+ myhost2 := `{"Host":"myhost2:8080","labels":{"labelname1":"labelvalue2"}}`
127+ targets = []string{selfTarget, myhost2, myhost}
128+ verifyTargets(t, testURL, fmt.Sprintf("[%v]", strings.Join(targets, ",")))
129
130 //Add a 3rd target with different labels and a specific port
131 rdr = strings.NewReader(`{"comment": "New web server3", "labels": {"labelname1": "labelvalue1-3", "labelname3": "labelvalue3"}}`)
132 req = authedRequest("POST", testURL+"/targets/10.0.0.1:9127", rdr)
133 verifyResponse(t, req, http.StatusAccepted, "")
134 myhost3 := `{"Host":"10.0.0.1:9127","labels":{"labelname1":"labelvalue1-3","labelname3":"labelvalue3"}}`
135- verifyTargets(t, testURL, "["+myhost3+","+selfTarget+","+myhost+","+myhost2+"]")
136+ targets = []string{myhost3, selfTarget, myhost2, myhost}
137+ verifyTargets(t, testURL, fmt.Sprintf("[%v]", strings.Join(targets, ",")))
138
139 //Get a single host, multiple is being tested repeatedly in verifyTargets
140- req = authedRequest("GET", testURL+"/targets/myhost2", nil)
141+ req = authedRequest("GET", testURL+"/targets/myhost2:8080", nil)
142 verifyResponse(t, req, http.StatusOK, "["+myhost2+"]")
143
144 //Delete target3
145 rdr = strings.NewReader(`{"comment": "Decommissioning web server3"}`)
146 req = authedRequest("DELETE", testURL+"/targets/10.0.0.1:9127", rdr)
147 verifyResponse(t, req, http.StatusAccepted, "")
148- verifyTargets(t, testURL, "["+selfTarget+","+myhost+","+myhost2+"]")
149+ targets = []string{selfTarget, myhost2, myhost}
150+ verifyTargets(t, testURL, fmt.Sprintf("[%v]", strings.Join(targets, ",")))
151
152 //Try to get host3 should be empty
153 req = authedRequest("GET", testURL+"/targets/10.0.0.1:9127", nil)
154@@ -129,23 +148,26 @@ func TestAPI(t *testing.T) {
155
156 //Update Lables on target1
157 rdr = strings.NewReader(`{"comment": "Update web server lables", "labels": {"labelname1_updated": "labelvalue1_updated"}}`)
158- req = authedRequest("PUT", testURL+"/targets/myhost", rdr)
159+ req = authedRequest("PUT", testURL+"/targets/myhost:8080", rdr)
160 verifyResponse(t, req, http.StatusAccepted, "")
161- myhostmod := `{"Host":"myhost","labels":{"labelname1_updated":"labelvalue1_updated"}}`
162- verifyTargets(t, testURL, "["+selfTarget+","+myhostmod+","+myhost2+"]")
163+ myhostmod := `{"Host":"myhost:8080","labels":{"labelname1_updated":"labelvalue1_updated"}}`
164+ targets = []string{selfTarget, myhost2, myhostmod}
165+ verifyTargets(t, testURL, fmt.Sprintf("[%v]", strings.Join(targets, ",")))
166
167 //Add a 4th target with the same labels as target2
168 rdr = strings.NewReader(`{"comment": "New web server2", "labels": {"labelname1": "labelvalue2"}}`)
169- req = authedRequest("POST", testURL+"/targets/myhost4", rdr)
170+ req = authedRequest("POST", testURL+"/targets/myhost4:8080", rdr)
171 verifyResponse(t, req, http.StatusAccepted, "")
172- myhost4 := `{"Host":"myhost4","labels":{"labelname1":"labelvalue2"}}`
173- verifyTargets(t, testURL, "["+selfTarget+","+myhostmod+","+myhost2+","+myhost4+"]")
174+ myhost4 := `{"Host":"myhost4:8080","labels":{"labelname1":"labelvalue2"}}`
175+ targets = []string{selfTarget, myhost2, myhost4, myhostmod}
176+ verifyTargets(t, testURL, fmt.Sprintf("[%v]", strings.Join(targets, ",")))
177
178 //Delete 4th target it shares labels with target2 so exercises a different section of code.
179 rdr = strings.NewReader(`{"comment": "Decommissioning web server4"}`)
180- req = authedRequest("DELETE", testURL+"/targets/myhost4", rdr)
181+ req = authedRequest("DELETE", testURL+"/targets/myhost4:8080", rdr)
182 verifyResponse(t, req, http.StatusAccepted, "")
183- verifyTargets(t, testURL, "["+selfTarget+","+myhostmod+","+myhost2+"]")
184+ targets = []string{selfTarget, myhost2, myhostmod}
185+ verifyTargets(t, testURL, fmt.Sprintf("[%v]", strings.Join(targets, ",")))
186 }
187
188 //authedRequest returns an http.Request with the proper auth headers.
189diff --git a/snapcraft.yaml b/snapcraft.yaml
190index 9d019a8..ea0e3b4 100644
191--- a/snapcraft.yaml
192+++ b/snapcraft.yaml
193@@ -1,5 +1,5 @@
194 name: promreg
195-version: 0.0.8
196+version: 0.0.9
197 summary: A simple REST API for registering targets with Prometheus.
198 description: >
199 This service provides a simple API that can run alongside Prometheus allowing adding, updating and deleting of targets via a REST API and without reloading the Prometheus configuration. See https://launchpad.net/prometheus-registration for more information.

Subscribers

People subscribed via source and target branches

to all changes: