forked from kiali/kiali
-
Notifications
You must be signed in to change notification settings - Fork 0
/
vizceral.go
173 lines (153 loc) · 4.88 KB
/
vizceral.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
// THIS CODE IS PROVIDED AS-IS and the resulting config JSON is targeted for
// standalone Vizceral use.
//
// The following link explains how to run the Vizceral example. Replace the example
// version of sample_data.json with the configuration generated by this handler
// and you should be able to render the Vizceral service graph.
//
// https://github.com/Netflix/vizceral-example
//
// The following link gives some information about the config format, although
// good documentation on volume handling is hard to find.
//
// https://github.com/Netflix/Vizceral/wiki/How-to-Use#graph-data-format
//
package vizceral
import (
"fmt"
"net/url"
"time"
"github.com/kiali/kiali/graph"
"github.com/kiali/kiali/graph/options"
)
type Metadata struct {
}
type Metrics struct {
Danger float64 `json:"danger,omitempty"`
Warning float64 `json:"warning,omitempty"`
Normal float64 `json:"normal,omitempty"`
}
type Connection struct {
Source string `json:"source"`
Target string `json:"target"`
Metadata Metadata `json:"metadata,omitempty"`
Metrics Metrics `json:"metrics,omitempty"`
}
type Notice struct {
Title string `json:"title"`
Link string `json:"link,omitempty"`
Severity int `json:"severity,omitempty"`
}
type Node struct {
Renderer string `json:"renderer,omitempty"`
Name string `json:"name"`
DisplayName string `json:"displayName,omitempty"`
Class string `json:"class,omitempty"`
Updated int64 `json:"updated,omitempty"`
MaxVolume float64 `json:"maxVolume,omitempty"`
Metadata Metadata `json:"metadata,omitempty"`
Nodes []Node `json:"nodes,omitempty"`
Connections []Connection `json:"connections,omitempty"`
Notices []Notice `json:"notices,omitempty"`
}
type Config Node
func NewConfig(namespace string, trafficMap graph.TrafficMap, o options.VendorOptions) (result Config) {
namespaceOrphanNode := Node{
Renderer: "focusedChild",
Name: "orphan",
DisplayName: "orphans (no traffic)",
}
namespaceNodes := []Node{namespaceOrphanNode}
var namespaceConnections []Connection
var maxVolume float64
buildConfig(trafficMap, &namespaceNodes, &namespaceConnections, &maxVolume, o)
regionNamespaceNode := Node{
Renderer: "region",
Name: namespace,
Updated: time.Now().Unix(),
MaxVolume: maxVolume,
Nodes: namespaceNodes,
Connections: namespaceConnections,
}
regionInternetNode := Node{
Renderer: "region",
Name: "INTERNET",
}
regionInternetConnection := Connection{
Source: "INTERNET",
Target: namespace,
Metrics: Metrics{
// TODO, should break up MaxVolume by code from the actual unknown/ingress nodes
Normal: maxVolume * 1.0,
},
}
regionNodes := []Node{regionInternetNode, regionNamespaceNode}
regionConnections := []Connection{regionInternetConnection}
result = Config{
Renderer: "global",
Name: "edge",
Nodes: regionNodes,
Connections: regionConnections,
}
return result
}
func buildConfig(trafficMap graph.TrafficMap, nodes *[]Node, connections *[]Connection, volume *float64, o options.VendorOptions) {
for _, sn := range trafficMap {
name := fmt.Sprintf("%v (%v)", sn.Name, sn.Version)
displayName := fmt.Sprintf("%v (%v)", sn.ServiceName, sn.Version)
n := Node{
Renderer: "focusedChild",
Name: name,
DisplayName: displayName,
Notices: []Notice{
{
Title: "Prometheus Graph",
Link: linkPromGraph(sn.Name, sn.Version),
}},
}
*nodes = append(*nodes, n)
for _, e := range sn.Edges {
var c Connection
var source string
isUnused := sn.Metadata["isUnused"] == "true"
if isUnused {
source = "orphan"
} else {
source = fmt.Sprintf("%v (%v)", sn.Name, sn.Version)
}
rate := 0.0
normal := 0.0
warning := 0.0
danger := 0.0
if !isUnused {
rate = e.Metadata["rate"].(float64)
normal = e.Metadata["rate_2xx"].(float64) / (rate + 0.0001)
warning = e.Metadata["rate_3xx"].(float64) / (rate + 0.0001)
danger = (e.Metadata["rate_4xx"].(float64) + e.Metadata["rate_5xx"].(float64)) / (rate + 0.0001)
*volume += rate
}
c = Connection{
Source: source,
Target: e.Dest.Name,
Metrics: Metrics{
Normal: normal,
Warning: warning,
Danger: danger,
},
}
*connections = append(*connections, c)
}
}
}
func linkPromGraph(name, version string) (link string) {
// todo: this hardcoded address makes some assumptions...
address := "http://prometheus-istio-system.127.0.0.1.nip.io"
var promExpr string
if graph.UnknownVersion == version {
promExpr = fmt.Sprintf("istio_request_count{source_service=\"%v\",source_version=\"%v\"}", name, version)
} else {
promExpr = fmt.Sprintf("istio_request_count{destination_service=\"%v\",destination_version=\"%v\"}", name, version)
}
link = fmt.Sprintf("%v/graph?g0.range_input=1h&g0.tab=0&g0.expr=%v", address, url.QueryEscape(promExpr))
return link
}