Skip to content

Commit 1b1e899

Browse files
author
Matthew Fisher
committed
feat(api): add healthchecks to API
1 parent eb17597 commit 1b1e899

2 files changed

Lines changed: 157 additions & 10 deletions

File tree

api/config.go

Lines changed: 96 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package api
22

3+
import (
4+
"bytes"
5+
"fmt"
6+
"text/template"
7+
)
8+
39
// ConfigSet is the definition of POST /v2/apps/<app id>/config/.
410
type ConfigSet struct {
511
Values map[string]string `json:"values"`
@@ -12,14 +18,94 @@ type ConfigUnset struct {
1218

1319
// Config is the structure of an app's config.
1420
type Config struct {
15-
Owner string `json:"owner,omitempty"`
16-
App string `json:"app,omitempty"`
17-
Values map[string]interface{} `json:"values,omitempty"`
18-
Memory map[string]interface{} `json:"memory,omitempty"`
19-
CPU map[string]interface{} `json:"cpu,omitempty"`
20-
Tags map[string]interface{} `json:"tags,omitempty"`
21-
Registry map[string]interface{} `json:"registry,omitempty"`
22-
Created string `json:"created,omitempty"`
23-
Updated string `json:"updated,omitempty"`
24-
UUID string `json:"uuid,omitempty"`
21+
Owner string `json:"owner,omitempty"`
22+
App string `json:"app,omitempty"`
23+
Values map[string]interface{} `json:"values,omitempty"`
24+
Memory map[string]interface{} `json:"memory,omitempty"`
25+
CPU map[string]interface{} `json:"cpu,omitempty"`
26+
Healthcheck map[string]*Healthcheck `json:"healthcheck,omitempty"`
27+
Tags map[string]interface{} `json:"tags,omitempty"`
28+
Registry map[string]interface{} `json:"registry,omitempty"`
29+
Created string `json:"created,omitempty"`
30+
Updated string `json:"updated,omitempty"`
31+
UUID string `json:"uuid,omitempty"`
32+
}
33+
34+
// Healthcheck is the structure for an application healthcheck.
35+
// Healthchecks only need to provide information about themselves.
36+
// All the information is pushed to the server and handled by kubernetes.
37+
type Healthcheck struct {
38+
InitialDelaySeconds int `json:"initialDelaySeconds"`
39+
TimeoutSeconds int `json:"timeoutSeconds"`
40+
PeriodSeconds int `json:"periodSeconds"`
41+
SuccessThreshold int `json:"successThreshold"`
42+
FailureThreshold int `json:"failureThreshold"`
43+
Exec *ExecProbe `json:"exec,omitempty"`
44+
HTTPGet *HTTPGetProbe `json:"httpGet,omitempty"`
45+
TCPSocket *TCPSocketProbe `json:"tcpSocket,omitempty"`
46+
}
47+
48+
// String displays the HealthcheckHTTPGetProbe in a readable format.
49+
func (h Healthcheck) String() string {
50+
var doc bytes.Buffer
51+
tmpl, err := template.New("healthcheck").Parse(`Initial Delay (seconds): {{.InitialDelaySeconds}}
52+
Timeout (seconds): {{.TimeoutSeconds}}
53+
Period (seconds): {{.PeriodSeconds}}
54+
Success Threshold: {{.SuccessThreshold}}
55+
Failure Threshold: {{.FailureThreshold}}
56+
Exec Probe: {{or .Exec "N/A"}}
57+
HTTP GET Probe: {{or .HTTPGet "N/A"}}
58+
TCP Socket Probe: {{or .TCPSocket "N/A"}}`)
59+
if err != nil { panic(err) }
60+
err = tmpl.Execute(&doc, h)
61+
if err != nil { panic(err) }
62+
return doc.String()
63+
}
64+
65+
// KVPair is a key/value pair used to parse values from
66+
// strings into a formal structure.
67+
type KVPair struct {
68+
Key string `json:"key"`
69+
Value string `json:"value"`
70+
}
71+
72+
func (k KVPair) String() string {
73+
return k.Key+"="+k.Value
74+
}
75+
76+
// ExecProbe executes a command within a Pod.
77+
type ExecProbe struct {
78+
Command []string `json:"command"`
79+
}
80+
81+
// String displays the ExecProbe in a readable format.
82+
func (e ExecProbe) String() string {
83+
return fmt.Sprintf(`Command=%s`, e.Command)
84+
}
85+
86+
// HTTPGetProbe performs an HTTP GET request to the Pod
87+
// with the given path, port and headers.
88+
type HTTPGetProbe struct {
89+
Path string `json:"path,omitempty"`
90+
Port int `json:"port"`
91+
HTTPHeaders []*KVPair `json:"httpHeaders,omitempty"`
92+
}
93+
94+
// String displays the HTTPGetProbe in a readable format.
95+
func (h HTTPGetProbe) String() string {
96+
return fmt.Sprintf(`Path="%s" Port=%d HTTPHeaders=%s`,
97+
h.Path,
98+
h.Port,
99+
h.HTTPHeaders)
100+
}
101+
102+
// TCPSocketProbe attempts to open a socket connection to the
103+
// Pod on the given port.
104+
type TCPSocketProbe struct {
105+
Port int `json:"port"`
106+
}
107+
108+
// String displays the TCPSocketProbe in a readable format.
109+
func (t TCPSocketProbe) String() string {
110+
return fmt.Sprintf("Port=%d", t.Port)
25111
}

api/config_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package api
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
func TestHealthcheckString(t *testing.T) {
9+
h := Healthcheck{}
10+
11+
expected := strings.TrimSpace(`Initial Delay (seconds): 0
12+
Timeout (seconds): 0
13+
Period (seconds): 0
14+
Success Threshold: 0
15+
Failure Threshold: 0
16+
Exec Probe: N/A
17+
HTTP GET Probe: N/A
18+
TCP Socket Probe: N/A`)
19+
20+
if strings.TrimSpace(h.String()) != expected {
21+
t.Errorf("Expected:\n\n%s\n\nGot:\n\n%s", expected, h.String())
22+
}
23+
24+
h.HTTPGet = &HTTPGetProbe{
25+
Path: "/",
26+
Port: 80,
27+
HTTPHeaders: []*KVPair{{ Key: "X-DEIS-IS", Value: "AWESOME" }},
28+
}
29+
30+
expected = strings.TrimSpace(`Initial Delay (seconds): 0
31+
Timeout (seconds): 0
32+
Period (seconds): 0
33+
Success Threshold: 0
34+
Failure Threshold: 0
35+
Exec Probe: N/A
36+
HTTP GET Probe: Path="/" Port=80 HTTPHeaders=[X-DEIS-IS=AWESOME]
37+
TCP Socket Probe: N/A`)
38+
39+
if strings.TrimSpace(h.String()) != expected {
40+
t.Errorf("Expected:\n\n%s\n\nGot:\n\n%s", expected, h.String())
41+
}
42+
43+
h.Exec = &ExecProbe{Command: []string{"echo", "hi"}}
44+
45+
h.TCPSocket = &TCPSocketProbe{
46+
Port: 80,
47+
}
48+
49+
expected = strings.TrimSpace(`Initial Delay (seconds): 0
50+
Timeout (seconds): 0
51+
Period (seconds): 0
52+
Success Threshold: 0
53+
Failure Threshold: 0
54+
Exec Probe: Command=[echo hi]
55+
HTTP GET Probe: Path="/" Port=80 HTTPHeaders=[X-DEIS-IS=AWESOME]
56+
TCP Socket Probe: Port=80`)
57+
58+
if strings.TrimSpace(h.String()) != expected {
59+
t.Errorf("Expected:\n\n%s\n\nGot:\n\n%s", expected, h.String())
60+
}
61+
}

0 commit comments

Comments
 (0)