This repository has been archived by the owner on Oct 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 183
/
observers.go
178 lines (154 loc) · 4.87 KB
/
observers.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package selfdescribe
import (
"go/doc"
"os"
"path/filepath"
"reflect"
"sort"
"github.com/signalfx/signalfx-agent/internal/core/services"
"github.com/signalfx/signalfx-agent/internal/observers"
log "github.com/sirupsen/logrus"
)
type observerMetadata struct {
structMetadata
ObserverType string `json:"observerType"`
Dimensions []dimMetadata `json:"dimensions"`
EndpointVariables []endpointVar `json:"endpointVariables"`
}
type endpointVar struct {
Name string `json:"name"`
Type string `json:"type"`
ElementKind string `json:"elementKind"`
Description string `json:"description"`
}
func observersStructMetadata() []observerMetadata {
sms := []observerMetadata{}
// Set to track undocumented observers
obsTypesSeen := make(map[string]bool)
filepath.Walk("internal/observers", func(path string, info os.FileInfo, err error) error {
if !info.IsDir() || err != nil {
return err
}
pkgDoc := packageDoc(path)
if pkgDoc == nil {
return nil
}
for obsType, obsDoc := range observerDocsInPackage(pkgDoc) {
if _, ok := observers.ConfigTemplates[obsType]; !ok {
log.Errorf("Found OBSERVER doc for observer type %s but it doesn't appear to be registered", obsType)
continue
}
t := reflect.TypeOf(observers.ConfigTemplates[obsType]).Elem()
obsTypesSeen[obsType] = true
allDocs := nestedPackageDocs(path)
dims := dimensionsFromNotesAndServicesPackage(allDocs)
mmd := observerMetadata{
structMetadata: getStructMetadata(t),
ObserverType: obsType,
Dimensions: dims,
EndpointVariables: endpointVariables(allDocs),
}
mmd.Doc = obsDoc
mmd.Package = path
sms = append(sms, mmd)
}
return nil
})
sort.Slice(sms, func(i, j int) bool {
return sms[i].ObserverType < sms[j].ObserverType
})
for k := range observers.ConfigTemplates {
if !obsTypesSeen[k] {
log.Warnf("Observer Type %s is registered but does not appear to have documentation", k)
}
}
return sms
}
func observerDocsInPackage(pkgDoc *doc.Package) map[string]string {
out := make(map[string]string)
for _, note := range pkgDoc.Notes["OBSERVER"] {
out[note.UID] = note.Body
}
return out
}
func dimensionsFromNotesAndServicesPackage(allDocs []*doc.Package) []dimMetadata {
var containerDims []dimMetadata
if isContainerObserver(allDocs) {
servicesDocs := nestedPackageDocs("internal/core/services")
for _, note := range notesFromDocs(servicesDocs, "CONTAINER_DIMENSION") {
containerDims = append(containerDims, dimMetadata{
Name: note.UID,
Description: commentTextToParagraphs(note.Body),
})
}
}
return append(
dimensionsFromNotes(allDocs),
containerDims...)
}
func isContainerObserver(obsDocs []*doc.Package) bool {
obsEndpointTypes := notesFromDocs(obsDocs, "ENDPOINT_TYPE")
if len(obsEndpointTypes) > 0 && obsEndpointTypes[0].UID == "ContainerEndpoint" {
return true
}
return false
}
func endpointVariables(obsDocs []*doc.Package) []endpointVar {
servicesDocs := nestedPackageDocs("internal/core/services")
var eType reflect.Type
isForContainers := isContainerObserver(obsDocs)
if isForContainers {
eType = reflect.TypeOf(services.ContainerEndpoint{})
} else {
eType = reflect.TypeOf(services.EndpointCore{})
}
sm := getStructMetadata(eType)
return append(
endpointVariablesFromNotes(append(obsDocs, servicesDocs...), isForContainers),
endpointVarsFromStructMetadataFields(sm.Fields)...)
}
func endpointVarsFromStructMetadataFields(fields []fieldMetadata) []endpointVar {
var endpointVars []endpointVar
for _, fm := range fields {
if fm.ElementStruct != nil {
endpointVars = append(endpointVars, endpointVarsFromStructMetadataFields(fm.ElementStruct.Fields)...)
continue
}
endpointVars = append(endpointVars, endpointVar{
Name: fm.YAMLName,
Type: fm.Type,
ElementKind: fm.ElementKind,
Description: fm.Doc,
})
}
sort.Slice(endpointVars, func(i, j int) bool {
return endpointVars[i].Name < endpointVars[j].Name
})
return endpointVars
}
func endpointVariablesFromNotes(allDocs []*doc.Package, includeContainerVars bool) []endpointVar {
var endpointVars []endpointVar
for _, note := range notesFromDocs(allDocs, "ENDPOINT_VAR") {
endpointVars = append(endpointVars, endpointVar{
Name: note.UID,
Type: "string",
Description: commentTextToParagraphs(note.Body),
})
}
// This is pretty hacky but is about the cleanest way to distinguish
// container derived variables from non-container vars so that docs aren't
// misleading.
if includeContainerVars {
for _, note := range notesFromDocs(allDocs, "CONTAINER_ENDPOINT_VAR") {
endpointVars = append(endpointVars, endpointVar{
Name: note.UID,
Type: "string",
Description: commentTextToParagraphs(note.Body),
})
}
}
sort.Slice(endpointVars, func(i, j int) bool {
return endpointVars[i].Name < endpointVars[j].Name
})
return endpointVars
}