-
Notifications
You must be signed in to change notification settings - Fork 0
/
plane.go
132 lines (120 loc) · 3.38 KB
/
plane.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package airstrike
import (
"errors"
"strings"
"time"
"github.com/dysolution/airstrike/ordnance"
"github.com/dysolution/sleepwalker"
)
// A Plane has an arsenal of deployable weapons. It represents a list of
// tasks that, perfored serially, compose a workflow.
//
// Many planes can deploy their arsenal at the same time, but each
// weapon in a plane's arsenal must be deployed one at a time.
//
// For example, a common workflow would be:
// 1. GET index - list all items in a collection
// 2. GET show - get the metadata for an item
// 3. POST create - create and/or associate an item related to the first
//
type Plane struct {
Name string `json:"name"`
Client sleepwalker.RESTClient `json:"-"`
Arsenal ordnance.Arsenal `json:"arsenal"`
}
// NewPlane ensures that the creation of each Plane is logged.
func NewPlane(name string, client sleepwalker.RESTClient) Plane {
desc := "airstrike.NewPlane"
log.WithFields(map[string]interface{}{
"name": name,
"client": client,
}).Debug(desc)
return Plane{Name: name, Client: client}
}
// Arm loads the given arsenal into the Plane and logs error conditions.
func (p *Plane) Arm(weapons ordnance.Arsenal) {
desc := "airstrike.(*Plane).Arm"
if len(weapons) == 0 {
log.WithFields(map[string]interface{}{
"plane": p.Name,
"weapons": weapons,
"error": errors.New("no weapons provided"),
}).Error(desc)
} else {
log.WithFields(map[string]interface{}{
"plane": p.Name,
"weapons": weapons,
}).Debug(desc)
p.Arsenal = weapons
}
}
// Launch tells a Plane to sequentially fires all of its weapons and report
// the results.
func (p Plane) Launch(logCh chan map[string]interface{}) ([]sleepwalker.Result, error) {
desc := "airstrike.Plane.Launch"
var results []sleepwalker.Result
logCh <- map[string]interface{}{
"source": desc,
"plane": p,
}
for _, weapon := range p.Arsenal {
result, err := p.fireWeapon(weapon, logCh)
if err != nil {
logCh <- map[string]interface{}{
"source": desc,
"error": err,
"plane": p,
"weapon": weapon,
"severity": "ERROR",
}
}
results = append(results, result)
}
return results, nil
}
// runs in a goroutine (Raid.Conduct)
func (p Plane) launchAndReport(urlInvariant string, logCh chan map[string]interface{}, squadronID string) {
results, err := p.Launch(logCh)
if err != nil {
logCh <- map[string]interface{}{
"error": err,
"severity": "ERROR",
}
}
for weaponID, result := range results {
var path string
parts := strings.SplitAfter(result.Path, urlInvariant)
if len(parts) >= 2 {
path = parts[1]
} else {
path = result.Path
}
stats := map[string]interface{}{
"plane": p.Name,
"weapon_id": weaponID,
"squadron_id": squadronID,
"method": result.Verb,
"path": path,
"response_time": result.Duration * time.Millisecond,
"status_code": result.StatusCode,
}
logCh <- stats
}
}
func (p Plane) fireWeapon(weapon ordnance.Weapon, logCh chan map[string]interface{}) (sleepwalker.Result, error) {
desc := "airstrike.Plane.fireWeapon"
if weapon == nil {
return sleepwalker.Result{}, errors.New("nil weapon")
}
if p.Client == nil {
return sleepwalker.Result{}, errors.New("nil client")
}
logCh <- map[string]interface{}{
"client": p.Client,
"plane": p,
"weapon": weapon,
"source": desc,
}
result, _ := weapon.Fire(p.Client)
return result, nil
}