From cab87f0fa5ef6474f70b3d68907ceaae45a38de0 Mon Sep 17 00:00:00 2001 From: jshaughn Date: Thu, 26 Apr 2018 17:30:39 -0400 Subject: [PATCH] KIALI-619 Add route rule appender - make public the individual IstioClientGetIstioDetails methods to allow for more targeted calls from the appenders. - update CB appender to use GetDestinationPolicies() directly - change some "isXxx" field names to "hasXxx" where appropriate - shorten some json field names where appropriate --- graph/appender/circuit_breaker.go | 10 ++-- graph/appender/route_rule.go | 54 +++++++++++++++++++ graph/cytoscape/cytoscape.go | 34 +++++++----- graph/options/options.go | 3 ++ handlers/graph.go | 3 -- .../testdata/test_namespace_graph.expected | 6 +-- handlers/testdata/test_service_graph.expected | 4 +- kubernetes/istio_details_service.go | 16 +++--- 8 files changed, 95 insertions(+), 35 deletions(-) create mode 100644 graph/appender/route_rule.go diff --git a/graph/appender/circuit_breaker.go b/graph/appender/circuit_breaker.go index b69777be02..b166053dfb 100644 --- a/graph/appender/circuit_breaker.go +++ b/graph/appender/circuit_breaker.go @@ -22,17 +22,17 @@ func (a CircuitBreakerAppender) AppendGraph(trees *[]tree.ServiceNode, namespace func applyCircuitBreakers(n *tree.ServiceNode, namespaceName string, istioClient *kubernetes.IstioClient) { // determine if there is a circuit breaker on this node - istioDetails, err := istioClient.GetIstioDetails(namespaceName, strings.Split(n.Name, ".")[0]) + destinationPolicies, err := istioClient.GetDestinationPolicies(namespaceName, strings.Split(n.Name, ".")[0]) if err == nil { - if istioDetails.DestinationPolicies != nil { + if destinationPolicies != nil { dps := make(models.DestinationPolicies, 0) - dps.Parse(istioDetails.DestinationPolicies) + dps.Parse(destinationPolicies) for _, dp := range dps { if dp.CircuitBreaker != nil { if d, ok := dp.Destination.(map[string]interface{}); ok { if labels, ok2 := d["labels"].(map[string]interface{}); ok2 { if labels["version"] == n.Version { - n.Metadata["isCircuitBreaker"] = "true" + n.Metadata["hasCircuitBreaker"] = "true" break // no need to keep going, we know it has at least one CB policy } } @@ -42,7 +42,7 @@ func applyCircuitBreakers(n *tree.ServiceNode, namespaceName string, istioClient } } else { log.Warningf("Cannot determine if service [%v:%v] has circuit breakers: %v", namespaceName, n.Name, err) - n.Metadata["isCircuitBreaker"] = "unknown" + n.Metadata["hasCircuitBreaker"] = "unknown" } for _, child := range n.Children { diff --git a/graph/appender/route_rule.go b/graph/appender/route_rule.go new file mode 100644 index 0000000000..af3a86863a --- /dev/null +++ b/graph/appender/route_rule.go @@ -0,0 +1,54 @@ +package appender + +import ( + "strings" + + "github.com/kiali/kiali/graph/tree" + "github.com/kiali/kiali/kubernetes" + "github.com/kiali/kiali/log" + "github.com/kiali/kiali/services/models" +) + +type RouteRuleAppender struct{} + +func (a RouteRuleAppender) AppendGraph(trees *[]tree.ServiceNode, namespaceName string) { + istioClient, err := kubernetes.NewClient() + checkError(err) + + for _, tree := range *trees { + applyRouteRules(&tree, namespaceName, istioClient) + } +} + +func applyRouteRules(n *tree.ServiceNode, namespaceName string, istioClient *kubernetes.IstioClient) { + // determine if there is a route rule on this node + routeRules, err := istioClient.GetRouteRules(namespaceName, strings.Split(n.Name, ".")[0]) + if err == nil { + if routeRules != nil { + rrs := make(models.RouteRules, 0) + rrs.Parse(routeRules) + Found: + for _, rr := range rrs { + if routeMaps, ok := rr.Route.([]interface{}); ok { + for _, routeMap := range routeMaps { + if rm, ok2 := routeMap.(map[string]interface{}); ok2 { + if labels, ok3 := rm["labels"].(map[string]interface{}); ok3 { + if labels["version"] == n.Version { + n.Metadata["hasRouteRule"] = "true" + break Found // no need to keep going, we know it has at least one RouteRule + } + } + } + } + } + } + } + } else { + log.Warningf("Cannot determine if service [%v:%v] has route rules: %v", namespaceName, n.Name, err) + n.Metadata["hasRouteRule"] = "unknown" + } + + for _, child := range n.Children { + applyRouteRules(child, namespaceName, istioClient) + } +} diff --git a/graph/cytoscape/cytoscape.go b/graph/cytoscape/cytoscape.go index 760ddbf20b..2dda7c2da8 100644 --- a/graph/cytoscape/cytoscape.go +++ b/graph/cytoscape/cytoscape.go @@ -30,17 +30,18 @@ type NodeData struct { Parent string `json:"parent,omitempty"` // Compound Node parent ID // App Fields (not required by Cytoscape) - Service string `json:"service"` - Version string `json:"version,omitempty"` - Rate string `json:"rate,omitempty"` // edge aggregate - Rate3xx string `json:"rate3XX,omitempty"` // edge aggregate - Rate4xx string `json:"rate4XX,omitempty"` // edge aggregate - Rate5xx string `json:"rate5XX,omitempty"` // edge aggregate - IsCircuitBreaker string `json:"isCircuitBreaker,omitempty"` // true | false | unknown - IsGroup string `json:"isGroup,omitempty"` // set to the grouping type, current values: [ 'version' ] - IsRoot string `json:"isRoot,omitempty"` // true | false - IsSelfInvoke string `json:"isSelfInvoke,omitempty"` // rate of selfInvoke - IsUnused string `json:"isUnused,omitempty"` // true | false + Service string `json:"service"` + Version string `json:"version,omitempty"` + Rate string `json:"rate,omitempty"` // edge aggregate + Rate3xx string `json:"rate3XX,omitempty"` // edge aggregate + Rate4xx string `json:"rate4XX,omitempty"` // edge aggregate + Rate5xx string `json:"rate5XX,omitempty"` // edge aggregate + RateSelfInvoke string `json:"rateSelfInvoke,omitempty"` // rate of selfInvoke + HasCircuitBreaker string `json:"hasCB,omitempty"` // true | false | unknown + HasRouteRule string `json:"hasRR,omitempty"` // true | false | unknown + IsGroup string `json:"isGroup,omitempty"` // set to the grouping type, current values: [ 'version' ] + IsRoot string `json:"isRoot,omitempty"` // true | false + IsUnused string `json:"isUnused,omitempty"` // true | false } type EdgeData struct { @@ -157,8 +158,13 @@ func walk(sn *tree.ServiceNode, nodes *[]*NodeWrapper, edges *[]*EdgeWrapper, pa } // node may have a circuit breaker - if cb, ok := sn.Metadata["isCircuitBreaker"]; ok { - nd.IsCircuitBreaker = cb.(string) + if cb, ok := sn.Metadata["hasCircuitBreaker"]; ok { + nd.HasCircuitBreaker = cb.(string) + } + + // node may have a route rule + if cb, ok := sn.Metadata["hasRouteRule"]; ok { + nd.HasRouteRule = cb.(string) } nw := NodeWrapper{ @@ -171,7 +177,7 @@ func walk(sn *tree.ServiceNode, nodes *[]*NodeWrapper, edges *[]*EdgeWrapper, pa //TODO If we can find a graph layout that handles loop edges well then // we can go back to allowing these but for now, flag the node text if parentNodeId == nd.Id { - nd.IsSelfInvoke = fmt.Sprintf("%.3f", sn.Metadata["rate"].(float64)) + nd.RateSelfInvoke = fmt.Sprintf("%.3f", sn.Metadata["rate"].(float64)) } else { edgeId := fmt.Sprintf("e%v", *edgeIdSequence) *edgeIdSequence++ diff --git a/graph/options/options.go b/graph/options/options.go index bda645de94..dc8dce5032 100644 --- a/graph/options/options.go +++ b/graph/options/options.go @@ -93,5 +93,8 @@ func parseAppenders(params url.Values) []appender.Appender { if csl == all || strings.Contains(csl, "circuit_breaker") { appenders = append(appenders, appender.CircuitBreakerAppender{}) } + if csl == all || strings.Contains(csl, "route_rule") { + appenders = append(appenders, appender.RouteRuleAppender{}) + } return appenders } diff --git a/handlers/graph.go b/handlers/graph.go index 14639d3e77..e0548f9d07 100644 --- a/handlers/graph.go +++ b/handlers/graph.go @@ -159,7 +159,6 @@ func buildNamespaceTree(sn *tree.ServiceNode, start time.Time, seenNodes map[str i := 0 for k, d := range destinations { s := strings.Split(k, " ") - // d["link_prom_graph"] = linkPromGraph(client.Address(), o.Metric, s[0], s[1]) child := tree.NewServiceNode(s[0], s[1]) child.Parent = sn child.Metadata = d @@ -246,7 +245,6 @@ func buildServiceTrees(o options.Options, client *prometheus.Client) (trees []tr rootService := string(sourceSvc) rootVersion := string(sourceVer) md := make(map[string]interface{}) - // md["link_prom_graph"] = linkPromGraph(client.Address(), o.Metric, rootService, rootVersion) root := tree.NewServiceNode(rootService, rootVersion) root.Parent = nil @@ -291,7 +289,6 @@ func buildServiceSubtree(sn *tree.ServiceNode, destinationSvc string, queryTime i := 0 for k, d := range destinations { s := strings.Split(k, " ") - // d["link_prom_graph"] = linkPromGraph(client.Address(), o.Metric, s[0], s[1]) child := tree.NewServiceNode(s[0], s[1]) child.Parent = sn child.Metadata = d diff --git a/handlers/testdata/test_namespace_graph.expected b/handlers/testdata/test_namespace_graph.expected index acde5dcb33..fdb3555d48 100644 --- a/handlers/testdata/test_namespace_graph.expected +++ b/handlers/testdata/test_namespace_graph.expected @@ -27,7 +27,7 @@ "service": "productpage.istio-system.svc.cluster.local", "version": "v1", "rate": "150.000", - "isSelfInvoke": "20.000" + "rateSelfInvoke": "20.000" } }, { @@ -61,7 +61,7 @@ "service": "reviews.istio-system.svc.cluster.local", "version": "v2", "rate": "20.000", - "isSelfInvoke": "20.000" + "rateSelfInvoke": "20.000" } }, { @@ -71,7 +71,7 @@ "service": "reviews.istio-system.svc.cluster.local", "version": "v3", "rate": "20.000", - "isSelfInvoke": "20.000" + "rateSelfInvoke": "20.000" } }, { diff --git a/handlers/testdata/test_service_graph.expected b/handlers/testdata/test_service_graph.expected index 4ee81ccf87..32de8d52bb 100644 --- a/handlers/testdata/test_service_graph.expected +++ b/handlers/testdata/test_service_graph.expected @@ -40,7 +40,7 @@ "service": "reviews.istio-system.svc.cluster.local", "version": "v2", "rate": "20.000", - "isSelfInvoke": "20.000" + "rateSelfInvoke": "20.000" } }, { @@ -50,7 +50,7 @@ "service": "reviews.istio-system.svc.cluster.local", "version": "v3", "rate": "20.000", - "isSelfInvoke": "20.000" + "rateSelfInvoke": "20.000" } } ], diff --git a/kubernetes/istio_details_service.go b/kubernetes/istio_details_service.go index 2177bc8f38..f0edf2e30e 100644 --- a/kubernetes/istio_details_service.go +++ b/kubernetes/istio_details_service.go @@ -17,22 +17,22 @@ func (in *IstioClient) GetIstioDetails(namespace string, serviceName string) (*I go func() { defer wg.Done() - routeRules, routeRulesErr = in.getRouteRules(namespace, serviceName) + routeRules, routeRulesErr = in.GetRouteRules(namespace, serviceName) }() go func() { defer wg.Done() - destinationPolicies, destinationPoliciesErr = in.getDestinationPolicies(namespace, serviceName) + destinationPolicies, destinationPoliciesErr = in.GetDestinationPolicies(namespace, serviceName) }() go func() { defer wg.Done() - virtualServices, virtualServicesErr = in.getVirtualServices(namespace, serviceName) + virtualServices, virtualServicesErr = in.GetVirtualServices(namespace, serviceName) }() go func() { defer wg.Done() - destinationRules, destinationRulesErr = in.getDestinationRules(namespace, serviceName) + destinationRules, destinationRulesErr = in.GetDestinationRules(namespace, serviceName) }() wg.Wait() @@ -62,7 +62,7 @@ func (in *IstioClient) GetIstioDetails(namespace string, serviceName string) (*I return &istioDetails, nil } -func (in *IstioClient) getRouteRules(namespace string, serviceName string) ([]IstioObject, error) { +func (in *IstioClient) GetRouteRules(namespace string, serviceName string) ([]IstioObject, error) { result, err := in.istioConfigApi.Get().Namespace(namespace).Resource(routeRules).Do().Get() if err != nil { return nil, err @@ -86,7 +86,7 @@ func (in *IstioClient) getRouteRules(namespace string, serviceName string) ([]Is return routerRules, nil } -func (in *IstioClient) getDestinationPolicies(namespace string, serviceName string) ([]IstioObject, error) { +func (in *IstioClient) GetDestinationPolicies(namespace string, serviceName string) ([]IstioObject, error) { result, err := in.istioConfigApi.Get().Namespace(namespace).Resource(destinationPolicies).Do().Get() if err != nil { return nil, err @@ -110,7 +110,7 @@ func (in *IstioClient) getDestinationPolicies(namespace string, serviceName stri return destinationPolicies, nil } -func (in *IstioClient) getVirtualServices(namespace string, serviceName string) ([]IstioObject, error) { +func (in *IstioClient) GetVirtualServices(namespace string, serviceName string) ([]IstioObject, error) { result, err := in.istioNetworkingApi.Get().Namespace(namespace).Resource(virtualServices).Do().Get() if err != nil { return nil, err @@ -138,7 +138,7 @@ func (in *IstioClient) getVirtualServices(namespace string, serviceName string) return virtualServices, nil } -func (in *IstioClient) getDestinationRules(namespace string, serviceName string) ([]IstioObject, error) { +func (in *IstioClient) GetDestinationRules(namespace string, serviceName string) ([]IstioObject, error) { result, err := in.istioNetworkingApi.Get().Namespace(namespace).Resource(destinationRules).Do().Get() if err != nil { return nil, err