Skip to content

Commit 1094ce8

Browse files
committed
Add /api/v2/jim endpoint
1 parent cd3c7a5 commit 1094ce8

File tree

3 files changed

+126
-31
lines changed

3 files changed

+126
-31
lines changed

api/v2.go

+92-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
gotcha "github.com/ian-kent/gotcha/app"
99
"github.com/ian-kent/gotcha/http"
1010
"github.com/mailhog/MailHog-Server/config"
11+
"github.com/mailhog/MailHog-Server/monkey"
1112
"github.com/mailhog/data"
1213

1314
"github.com/ian-kent/goose"
@@ -38,6 +39,12 @@ func CreateAPIv2(conf *config.Config, app *gotcha.App) *APIv2 {
3839
r.Get("/api/v2/search/?", apiv2.search)
3940
r.Options("/api/v2/search/?", apiv2.defaultOptions)
4041

42+
r.Get("/api/v2/jim/?", apiv2.jim)
43+
r.Post("/api/v2/jim/?", apiv2.createJim)
44+
r.Put("/api/v2/jim/?", apiv2.updateJim)
45+
r.Delete("/api/v2/jim/?", apiv2.deleteJim)
46+
r.Options("/api/v2/jim/?", apiv2.defaultOptions)
47+
4148
return apiv2
4249
}
4350

@@ -125,7 +132,89 @@ func (apiv2 *APIv2) search(session *http.Session) {
125132
res.Items = []data.Message(*messages)
126133
res.Total = total
127134

128-
bytes, _ := json.Marshal(res)
129-
session.Response.Headers.Add("Content-Type", "text/json")
130-
session.Response.Write(bytes)
135+
b, _ := json.Marshal(res)
136+
session.Response.Headers.Add("Content-Type", "application/json")
137+
session.Response.Write(b)
138+
}
139+
140+
func (apiv2 *APIv2) jim(session *http.Session) {
141+
log.Println("[APIv2] GET /jim")
142+
143+
apiv2.defaultOptions(session)
144+
145+
if apiv2.config.Monkey == nil {
146+
session.Response.Status = 404
147+
return
148+
}
149+
150+
b, _ := json.Marshal(apiv2.config.Monkey)
151+
session.Response.Headers.Add("Content-Type", "application/json")
152+
session.Response.Write(b)
153+
}
154+
155+
func (apiv2 *APIv2) deleteJim(session *http.Session) {
156+
log.Println("[APIv2] DELETE /jim")
157+
158+
apiv2.defaultOptions(session)
159+
160+
if apiv2.config.Monkey == nil {
161+
session.Response.Status = 404
162+
return
163+
}
164+
165+
apiv2.config.Monkey = nil
166+
}
167+
168+
func (apiv2 *APIv2) createJim(session *http.Session) {
169+
log.Println("[APIv2] POST /jim")
170+
171+
apiv2.defaultOptions(session)
172+
173+
if apiv2.config.Monkey != nil {
174+
session.Response.Status = 400
175+
return
176+
}
177+
178+
apiv2.config.Monkey = config.Jim
179+
180+
// Try, but ignore errors
181+
// Could be better (e.g., ok if no json, error if badly formed json)
182+
// but this works for now
183+
apiv2.newJimFromBody(session)
184+
185+
session.Response.Status = 201
186+
}
187+
188+
func (apiv2 *APIv2) newJimFromBody(session *http.Session) error {
189+
var jim monkey.Jim
190+
191+
dec := json.NewDecoder(session.Request.Body())
192+
err := dec.Decode(&jim)
193+
194+
if err != nil {
195+
return err
196+
}
197+
198+
jim.ConfigureFrom(config.Jim)
199+
200+
config.Jim = &jim
201+
apiv2.config.Monkey = &jim
202+
203+
return nil
204+
}
205+
206+
func (apiv2 *APIv2) updateJim(session *http.Session) {
207+
log.Println("[APIv2] PUT /jim")
208+
209+
apiv2.defaultOptions(session)
210+
211+
if apiv2.config.Monkey == nil {
212+
session.Response.Status = 404
213+
return
214+
}
215+
216+
err := apiv2.newJimFromBody(session)
217+
if err != nil {
218+
session.Response.Status = 400
219+
}
131220
}

config/config.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type Config struct {
4141
}
4242

4343
var cfg = DefaultConfig()
44-
var jim = &monkey.Jim{}
44+
var Jim = &monkey.Jim{}
4545

4646
func Configure() *Config {
4747
switch cfg.StorageType {
@@ -63,10 +63,10 @@ func Configure() *Config {
6363
}
6464

6565
if cfg.InviteJim {
66-
jim.Configure(func(message string, args ...interface{}) {
66+
Jim.Configure(func(message string, args ...interface{}) {
6767
log.Printf(message, args...)
6868
})
69-
cfg.Monkey = jim
69+
cfg.Monkey = Jim
7070
}
7171

7272
return cfg
@@ -82,5 +82,5 @@ func RegisterFlags() {
8282
flag.StringVar(&cfg.MongoColl, "mongo-coll", envconf.FromEnvP("MH_MONGO_COLLECTION", "messages").(string), "MongoDB collection, e.g. messages")
8383
flag.StringVar(&cfg.CORSOrigin, "cors-origin", envconf.FromEnvP("MH_CORS_ORIGIN", "").(string), "CORS Access-Control-Allow-Origin header for API endpoints")
8484
flag.BoolVar(&cfg.InviteJim, "invite-jim", envconf.FromEnvP("MH_INVITE_JIM", false).(bool), "Decide whether to invite Jim (beware, he causes trouble)")
85-
jim.RegisterFlags()
85+
Jim.RegisterFlags()
8686
}

monkey/jim.go

+30-24
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,27 @@ import (
1111

1212
// Jim is a chaos monkey
1313
type Jim struct {
14-
disconnectChance float64
15-
acceptChance float64
16-
linkSpeedAffect float64
17-
linkSpeedMin float64
18-
linkSpeedMax float64
19-
rejectSenderChance float64
20-
rejectRecipientChance float64
21-
rejectAuthChance float64
14+
DisconnectChance float64
15+
AcceptChance float64
16+
LinkSpeedAffect float64
17+
LinkSpeedMin float64
18+
LinkSpeedMax float64
19+
RejectSenderChance float64
20+
RejectRecipientChance float64
21+
RejectAuthChance float64
2222
logf func(message string, args ...interface{})
2323
}
2424

2525
// RegisterFlags implements ChaosMonkey.RegisterFlags
2626
func (j *Jim) RegisterFlags() {
27-
flag.Float64Var(&j.disconnectChance, "jim-disconnect", 0.005, "Chance of disconnect")
28-
flag.Float64Var(&j.acceptChance, "jim-accept", 0.99, "Chance of accept")
29-
flag.Float64Var(&j.linkSpeedAffect, "jim-linkspeed-affect", 0.1, "Chance of affecting link speed")
30-
flag.Float64Var(&j.linkSpeedMin, "jim-linkspeed-min", 1024, "Minimum link speed (in bytes per second)")
31-
flag.Float64Var(&j.linkSpeedMax, "jim-linkspeed-max", 10240, "Maximum link speed (in bytes per second)")
32-
flag.Float64Var(&j.rejectSenderChance, "jim-reject-sender", 0.05, "Chance of rejecting a sender (MAIL FROM)")
33-
flag.Float64Var(&j.rejectRecipientChance, "jim-reject-recipient", 0.05, "Chance of rejecting a recipient (RCPT TO)")
34-
flag.Float64Var(&j.rejectAuthChance, "jim-reject-auth", 0.05, "Chance of rejecting authentication (AUTH)")
27+
flag.Float64Var(&j.DisconnectChance, "jim-disconnect", 0.005, "Chance of disconnect")
28+
flag.Float64Var(&j.AcceptChance, "jim-accept", 0.99, "Chance of accept")
29+
flag.Float64Var(&j.LinkSpeedAffect, "jim-linkspeed-affect", 0.1, "Chance of affecting link speed")
30+
flag.Float64Var(&j.LinkSpeedMin, "jim-linkspeed-min", 1024, "Minimum link speed (in bytes per second)")
31+
flag.Float64Var(&j.LinkSpeedMax, "jim-linkspeed-max", 10240, "Maximum link speed (in bytes per second)")
32+
flag.Float64Var(&j.RejectSenderChance, "jim-reject-sender", 0.05, "Chance of rejecting a sender (MAIL FROM)")
33+
flag.Float64Var(&j.RejectRecipientChance, "jim-reject-recipient", 0.05, "Chance of rejecting a recipient (RCPT TO)")
34+
flag.Float64Var(&j.RejectAuthChance, "jim-reject-auth", 0.05, "Chance of rejecting authentication (AUTH)")
3535
}
3636

3737
// Configure implements ChaosMonkey.Configure
@@ -40,9 +40,15 @@ func (j *Jim) Configure(logf func(string, ...interface{})) {
4040
rand.Seed(time.Now().Unix())
4141
}
4242

43+
// ConfigureFrom lets us configure a new Jim from an old one without
44+
// having to expose logf (and any other future private vars)
45+
func (j *Jim) ConfigureFrom(j2 *Jim) {
46+
j.Configure(j2.logf)
47+
}
48+
4349
// Accept implements ChaosMonkey.Accept
4450
func (j *Jim) Accept(conn net.Conn) bool {
45-
if rand.Float64() > j.acceptChance {
51+
if rand.Float64() > j.AcceptChance {
4652
j.logf("Jim: Rejecting connection\n")
4753
return false
4854
}
@@ -53,9 +59,9 @@ func (j *Jim) Accept(conn net.Conn) bool {
5359
// LinkSpeed implements ChaosMonkey.LinkSpeed
5460
func (j *Jim) LinkSpeed() *linkio.Throughput {
5561
rand.Seed(time.Now().Unix())
56-
if rand.Float64() < j.linkSpeedAffect {
57-
lsDiff := j.linkSpeedMax - j.linkSpeedMin
58-
lsAffect := j.linkSpeedMin + (lsDiff * rand.Float64())
62+
if rand.Float64() < j.LinkSpeedAffect {
63+
lsDiff := j.LinkSpeedMax - j.LinkSpeedMin
64+
lsAffect := j.LinkSpeedMin + (lsDiff * rand.Float64())
5965
f := linkio.Throughput(lsAffect) * linkio.BytePerSecond
6066
j.logf("Jim: Restricting throughput to %s\n", f)
6167
return &f
@@ -66,7 +72,7 @@ func (j *Jim) LinkSpeed() *linkio.Throughput {
6672

6773
// ValidRCPT implements ChaosMonkey.ValidRCPT
6874
func (j *Jim) ValidRCPT(rcpt string) bool {
69-
if rand.Float64() < j.rejectRecipientChance {
75+
if rand.Float64() < j.RejectRecipientChance {
7076
j.logf("Jim: Rejecting recipient %s\n", rcpt)
7177
return false
7278
}
@@ -76,7 +82,7 @@ func (j *Jim) ValidRCPT(rcpt string) bool {
7682

7783
// ValidMAIL implements ChaosMonkey.ValidMAIL
7884
func (j *Jim) ValidMAIL(mail string) bool {
79-
if rand.Float64() < j.rejectSenderChance {
85+
if rand.Float64() < j.RejectSenderChance {
8086
j.logf("Jim: Rejecting sender %s\n", mail)
8187
return false
8288
}
@@ -86,7 +92,7 @@ func (j *Jim) ValidMAIL(mail string) bool {
8692

8793
// ValidAUTH implements ChaosMonkey.ValidAUTH
8894
func (j *Jim) ValidAUTH(mechanism string, args ...string) bool {
89-
if rand.Float64() < j.rejectAuthChance {
95+
if rand.Float64() < j.RejectAuthChance {
9096
j.logf("Jim: Rejecting authentication %s: %s\n", mechanism, args)
9197
return false
9298
}
@@ -96,7 +102,7 @@ func (j *Jim) ValidAUTH(mechanism string, args ...string) bool {
96102

97103
// Disconnect implements ChaosMonkey.Disconnect
98104
func (j *Jim) Disconnect() bool {
99-
if rand.Float64() < j.disconnectChance {
105+
if rand.Float64() < j.DisconnectChance {
100106
j.logf("Jim: Being nasty, kicking them off\n")
101107
return true
102108
}

0 commit comments

Comments
 (0)