-
Notifications
You must be signed in to change notification settings - Fork 0
/
analysis.go
228 lines (187 loc) · 8.46 KB
/
analysis.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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package analysis
import (
"fmt"
"strconv"
"github.com/gonum/graph"
kapi "k8s.io/kubernetes/pkg/api"
osgraph "github.com/openshift/origin/pkg/api/graph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
routeapi "github.com/openshift/origin/pkg/route/api"
routeedges "github.com/openshift/origin/pkg/route/graph"
routegraph "github.com/openshift/origin/pkg/route/graph/nodes"
)
const (
// MissingRoutePortWarning is returned when a route has no route port specified
// and the service it routes to has multiple ports.
MissingRoutePortWarning = "MissingRoutePort"
// WrongRoutePortWarning is returned when a route has a route port specified
// but the service it points to has no such port (either as a named port or as
// a target port).
WrongRoutePortWarning = "WrongRoutePort"
// MissingServiceWarning is returned when there is no service for the specific route.
MissingServiceWarning = "MissingService"
// MissingTLSTerminationTypeErr is returned when a route with a tls config doesn't
// specify a tls termination type.
MissingTLSTerminationTypeErr = "MissingTLSTermination"
// PathBasedPassthroughErr is returned when a path based route is passthrough
// terminated.
PathBasedPassthroughErr = "PathBasedPassthrough"
// MissingTLSTerminationTypeErr is returned when a route with a tls config doesn't
// specify a tls termination type.
RouteNotAdmittedTypeErr = "RouteNotAdmitted"
// MissingRequiredRouterErr is returned when no router has been setup.
MissingRequiredRouterErr = "MissingRequiredRouter"
)
// FindPortMappingIssues checks all routes and reports any issues related to their ports.
// Also non-existent services for routes are reported here.
func FindPortMappingIssues(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastRouteNode := range g.NodesByKind(routegraph.RouteNodeKind) {
routeNode := uncastRouteNode.(*routegraph.RouteNode)
marker := routePortMarker(g, f, routeNode)
if marker != nil {
markers = append(markers, *marker)
}
}
return markers
}
func routePortMarker(g osgraph.Graph, f osgraph.Namer, routeNode *routegraph.RouteNode) *osgraph.Marker {
for _, uncastServiceNode := range g.SuccessorNodesByEdgeKind(routeNode, routeedges.ExposedThroughRouteEdgeKind) {
svcNode := uncastServiceNode.(*kubegraph.ServiceNode)
if !svcNode.Found() {
return &osgraph.Marker{
Node: routeNode,
RelatedNodes: []graph.Node{svcNode},
Severity: osgraph.WarningSeverity,
Key: MissingServiceWarning,
Message: fmt.Sprintf("%s is supposed to route traffic to %s but %s doesn't exist.",
f.ResourceName(routeNode), f.ResourceName(svcNode), f.ResourceName(svcNode)),
// TODO: Suggest 'oc create service' once that's a thing.
// See https://github.com/kubernetes/kubernetes/pull/19509
}
}
if len(svcNode.Spec.Ports) > 1 && (routeNode.Spec.Port == nil || len(routeNode.Spec.Port.TargetPort.String()) == 0) {
return &osgraph.Marker{
Node: routeNode,
RelatedNodes: []graph.Node{svcNode},
Severity: osgraph.WarningSeverity,
Key: MissingRoutePortWarning,
Message: fmt.Sprintf("%s doesn't have a port specified and is routing traffic to %s which uses multiple ports.",
f.ResourceName(routeNode), f.ResourceName(svcNode)),
}
}
if routeNode.Spec.Port == nil {
// If no port is specified, we don't need to analyze any further.
return nil
}
routePortString := routeNode.Spec.Port.TargetPort.String()
if routePort, err := strconv.Atoi(routePortString); err == nil {
for _, port := range svcNode.Spec.Ports {
if port.TargetPort.IntValue() == routePort {
return nil
}
}
// route has a numeric port, service has no port with that number as a targetPort.
marker := &osgraph.Marker{
Node: routeNode,
RelatedNodes: []graph.Node{svcNode},
Severity: osgraph.WarningSeverity,
Key: WrongRoutePortWarning,
Message: fmt.Sprintf("%s has a port specified (%d) but %s has no such targetPort.",
f.ResourceName(routeNode), routePort, f.ResourceName(svcNode)),
}
if len(svcNode.Spec.Ports) == 1 {
marker.Suggestion = osgraph.Suggestion(fmt.Sprintf("oc patch %s -p '{\"spec\":{\"port\":{\"targetPort\": %d}}}'", f.ResourceName(routeNode), svcNode.Spec.Ports[0].TargetPort.IntValue()))
}
return marker
}
for _, port := range svcNode.Spec.Ports {
if port.Name == routePortString {
return nil
}
}
// route has a named port, service has no port with that name.
marker := &osgraph.Marker{
Node: routeNode,
RelatedNodes: []graph.Node{svcNode},
Severity: osgraph.WarningSeverity,
Key: WrongRoutePortWarning,
Message: fmt.Sprintf("%s has a named port specified (%q) but %s has no such named port.",
f.ResourceName(routeNode), routePortString, f.ResourceName(svcNode)),
}
if len(svcNode.Spec.Ports) == 1 {
marker.Suggestion = osgraph.Suggestion(fmt.Sprintf("oc patch %s -p '{\"spec\":{\"port\":{\"targetPort\": %d}}}'", f.ResourceName(routeNode), svcNode.Spec.Ports[0].TargetPort.IntValue()))
}
return marker
}
return nil
}
func FindMissingTLSTerminationType(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastRouteNode := range g.NodesByKind(routegraph.RouteNodeKind) {
routeNode := uncastRouteNode.(*routegraph.RouteNode)
if routeNode.Spec.TLS != nil && len(routeNode.Spec.TLS.Termination) == 0 {
markers = append(markers, osgraph.Marker{
Node: routeNode,
Severity: osgraph.ErrorSeverity,
Key: MissingTLSTerminationTypeErr,
Message: fmt.Sprintf("%s has a TLS configuration but no termination type specified.", f.ResourceName(routeNode)),
Suggestion: osgraph.Suggestion(fmt.Sprintf("oc patch %s -p '{\"spec\":{\"tls\":{\"termination\":\"<type>\"}}}' (replace <type> with a valid termination type: edge, passthrough, reencrypt)", f.ResourceName(routeNode)))})
}
}
return markers
}
// FindRouteAdmissionFailures creates markers for any routes that were rejected by their routers
func FindRouteAdmissionFailures(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastRouteNode := range g.NodesByKind(routegraph.RouteNodeKind) {
routeNode := uncastRouteNode.(*routegraph.RouteNode)
Route:
for _, ingress := range routeNode.Status.Ingress {
switch status, condition := routeapi.IngressConditionStatus(&ingress, routeapi.RouteAdmitted); status {
case kapi.ConditionFalse:
markers = append(markers, osgraph.Marker{
Node: routeNode,
Severity: osgraph.ErrorSeverity,
Key: RouteNotAdmittedTypeErr,
Message: fmt.Sprintf("%s was not accepted by router %q: %s (%s)", f.ResourceName(routeNode), ingress.RouterName, condition.Message, condition.Reason),
})
break Route
}
}
}
return markers
}
// FindMissingRouter creates markers for all routes in case there is no running router.
func FindMissingRouter(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastRouteNode := range g.NodesByKind(routegraph.RouteNodeKind) {
routeNode := uncastRouteNode.(*routegraph.RouteNode)
if len(routeNode.Route.Status.Ingress) == 0 {
markers = append(markers, osgraph.Marker{
Node: routeNode,
Severity: osgraph.ErrorSeverity,
Key: MissingRequiredRouterErr,
Message: fmt.Sprintf("%s is routing traffic to svc/%s, but either the administrator has not installed a router or the router is not selecting this route.", f.ResourceName(routeNode), routeNode.Spec.To.Name),
Suggestion: osgraph.Suggestion("oc adm router -h"),
})
}
}
return markers
}
func FindPathBasedPassthroughRoutes(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
markers := []osgraph.Marker{}
for _, uncastRouteNode := range g.NodesByKind(routegraph.RouteNodeKind) {
routeNode := uncastRouteNode.(*routegraph.RouteNode)
if len(routeNode.Spec.Path) > 0 && routeNode.Spec.TLS != nil && routeNode.Spec.TLS.Termination == routeapi.TLSTerminationPassthrough {
markers = append(markers, osgraph.Marker{
Node: routeNode,
Severity: osgraph.ErrorSeverity,
Key: PathBasedPassthroughErr,
Message: fmt.Sprintf("%s is path-based and uses passthrough termination, which is an invalid combination.", f.ResourceName(routeNode)),
Suggestion: osgraph.Suggestion(fmt.Sprintf("1. use spec.tls.termination=edge or 2. use spec.tls.termination=reencrypt and specify spec.tls.destinationCACertificate or 3. remove spec.path")),
})
}
}
return markers
}