-
Notifications
You must be signed in to change notification settings - Fork 8
/
index.go
169 lines (147 loc) · 4.01 KB
/
index.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
package stats
import (
_ "embed"
"fmt"
"html/template"
"log"
"net/http"
"sort"
"time"
)
var profileDescriptions = map[string]string{}
type Index struct {
history SnapshotHistory
}
func NewIndex(history SnapshotHistory) Index {
return Index{
history: history,
}
}
type profile struct {
Name string
Href string
Desc string
}
type pageData struct {
Profiles []profile
Formats []string
ResourceTypes []string
Clusters []string
Namespaces []string
}
//go:embed html/index.html
var indexHtml string
//go:embed html/fallback.html
var fallbackHtml string
func (index Index) Generate(w http.ResponseWriter, r *http.Request) {
var profiles []profile
// Adding other profiles exposed from within this package
for p, pd := range profileDescriptions {
profiles = append(profiles, profile{
Name: p,
Href: p,
Desc: pd,
})
}
sort.Slice(profiles, func(i, j int) bool {
return profiles[i].Name < profiles[j].Name
})
template, data := getTemplate(index, profiles)
templateErr := template.Execute(w, data)
if templateErr != nil {
http.Error(w, templateErr.Error(), http.StatusInternalServerError)
return
}
}
type getIndexTemplateResult struct {
Template *template.Template
PageData pageData
Error error
}
func getTemplate(index Index, profiles []profile) (*template.Template, pageData) {
result := make(chan getIndexTemplateResult, 1)
go func() {
result <- getIndexTemplate(index, profiles)
}()
select {
case <-time.After(time.Duration(1.5 * float64(time.Second))):
log.Println("Timed out getting filters. Revert to fallback template.")
return getFallbackTemplate(index, profiles)
case result := <-result:
if result.Error != nil {
log.Printf("Unhandled error. Revert to fallback template. Error: %s\n", result.Error.Error())
return getFallbackTemplate(index, profiles)
}
return result.Template, result.PageData
}
}
func getIndexTemplate(index Index, profiles []profile) (result getIndexTemplateResult) {
defer func() {
if r := recover(); r != nil {
result = getIndexTemplateResult{
Error: fmt.Errorf("Recovered. Error: %s", r),
}
}
}()
clusters := make(map[string]bool)
namespaces := make(map[string]bool)
// Capture clusters, namespaces, resourceTypes from input.
// We can use these as filters for both output & input
input, err := index.history.GetInputCopy()
if err != nil {
return getIndexTemplateResult{
Error: err,
}
}
translator := input["translator"].(map[string]interface{})
resourceTypes := make([]string, 0, len(translator))
for id := range translator {
resourceTypes = append(resourceTypes, id)
}
for resourceType, resources := range translator {
for _, resource := range resources.([]interface{}) {
resource := resource.(map[string]interface{})
metadata := resource["metadata"].(map[string]interface{})
cluster, ok := metadata["clusterName"].(string)
if ok && cluster != "" {
clusters[cluster] = true
}
namespace, ok := metadata["namespace"].(string)
if resourceType == "/v1, Kind=Namespace" {
namespace, ok = metadata["name"].(string)
}
if ok && namespace != "" {
namespaces[namespace] = true
}
}
}
clusterSlice := make([]string, 0, len(clusters))
for k := range clusters {
clusterSlice = append(clusterSlice, k)
}
namespaceSlice := make([]string, 0, len(namespaces))
for k := range namespaces {
namespaceSlice = append(namespaceSlice, k)
}
sort.Strings(resourceTypes)
sort.Strings(clusterSlice)
sort.Strings(namespaceSlice)
return getIndexTemplateResult{
Template: template.Must(template.New("index").Parse(indexHtml)),
PageData: pageData{
Profiles: profiles,
Formats: []string{"", "json", "yaml"},
ResourceTypes: resourceTypes,
Clusters: clusterSlice,
Namespaces: namespaceSlice,
},
Error: nil,
}
}
func getFallbackTemplate(index Index, profiles []profile) (*template.Template, pageData) {
data := pageData{
Profiles: profiles,
Formats: []string{"", "json", "yaml"},
}
return template.Must(template.New("index").Parse(fallbackHtml)), data
}