forked from cloudfoundry/cli
/
app_events.go
142 lines (126 loc) · 3.38 KB
/
app_events.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
133
134
135
136
137
138
139
140
141
142
package api
import (
"cf/configuration"
"cf/errors"
"cf/models"
"cf/net"
"fmt"
"generic"
"net/url"
"strconv"
"strings"
"time"
)
type AppEventsRepository interface {
ListEvents(appGuid string, cb func(models.EventFields) bool) errors.Error
}
type CloudControllerAppEventsRepository struct {
config configuration.Reader
gateway net.Gateway
}
func NewCloudControllerAppEventsRepository(config configuration.Reader, gateway net.Gateway) (repo CloudControllerAppEventsRepository) {
repo.config = config
repo.gateway = gateway
return
}
func (repo CloudControllerAppEventsRepository) ListEvents(appGuid string, cb func(models.EventFields) bool) errors.Error {
apiErr := repo.gateway.ListPaginatedResources(
repo.config.ApiEndpoint(),
repo.config.AccessToken(),
fmt.Sprintf("/v2/events?q=%s", url.QueryEscape(fmt.Sprintf("actee:%s", appGuid))),
EventResourceNewV2{},
func(resource interface{}) bool {
return cb(resource.(EventResourceNewV2).ToFields())
})
// FIXME: needs semantic API version
switch apiErr.(type) {
case errors.HttpNotFoundError:
apiErr = repo.gateway.ListPaginatedResources(
repo.config.ApiEndpoint(),
repo.config.AccessToken(),
fmt.Sprintf("/v2/apps/%s/events", appGuid),
EventResourceOldV2{},
func(resource interface{}) bool {
return cb(resource.(EventResourceOldV2).ToFields())
})
}
return apiErr
}
const APP_EVENT_TIMESTAMP_FORMAT = "2006-01-02T15:04:05-07:00"
// FIXME: needs semantic versioning
type EventResourceOldV2 struct {
Resource
Entity struct {
Timestamp time.Time
ExitDescription string `json:"exit_description"`
ExitStatus int `json:"exit_status"`
InstanceIndex int `json:"instance_index"`
}
}
func (resource EventResourceOldV2) ToFields() models.EventFields {
return models.EventFields{
Guid: resource.Metadata.Guid,
Name: "app crashed",
Timestamp: resource.Entity.Timestamp,
Description: fmt.Sprintf("instance: %d, reason: %s, exit_status: %s", resource.Entity.InstanceIndex, resource.Entity.ExitDescription, strconv.Itoa(resource.Entity.ExitStatus)),
}
}
type EventResourceNewV2 struct {
Resource
Entity struct {
Timestamp time.Time
Type string
Metadata map[string]interface{}
}
}
var KNOWN_METADATA_KEYS = []string{
"index",
"reason",
"exit_description",
"exit_status",
"recursive",
"disk_quota",
"instances",
"memory",
"state",
"command",
"environment_json",
}
func (resource EventResourceNewV2) ToFields() models.EventFields {
metadata := generic.NewMap(resource.Entity.Metadata)
if metadata.Has("request") {
metadata = generic.NewMap(metadata.Get("request"))
}
return models.EventFields{
Guid: resource.Metadata.Guid,
Name: resource.Entity.Type,
Timestamp: resource.Entity.Timestamp,
Description: formatDescription(metadata, KNOWN_METADATA_KEYS),
}
}
func formatDescription(metadata generic.Map, keys []string) string {
parts := []string{}
for _, key := range keys {
value := metadata.Get(key)
if value != nil {
parts = append(parts, fmt.Sprintf("%s: %s", key, formatDescriptionPart(value)))
}
}
return strings.Join(parts, ", ")
}
func formatDescriptionPart(val interface{}) string {
switch val := val.(type) {
case string:
return val
case float64:
return strconv.FormatFloat(val, byte('f'), -1, 64)
case bool:
if val {
return "true"
} else {
return "false"
}
default:
return fmt.Sprintf("%s", val)
}
}