Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support new 'includeIdleEdges` option. False by default. #3522

Merged
merged 2 commits into from Dec 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 11 additions & 1 deletion doc.go
Expand Up @@ -178,7 +178,7 @@ type WorkloadParam struct {

// swagger:parameters graphApp graphAppVersion graphNamespaces graphService graphWorkload
type AppendersParam struct {
// Comma-separated list of Appenders to run. Available appenders: [deadNode, istio, aggregateNode, responseTime, securityPolicy, serviceEntry, sidecarsCheck, unusedNode].
// Comma-separated list of Appenders to run. Available appenders: [aggregateNode, deadNode, idleNode, istio, responseTime, securityPolicy, serviceEntry, sidecarsCheck].
//
// in: query
// required: false
Expand Down Expand Up @@ -216,6 +216,16 @@ type GroupByParam struct {
Name string `json:"groupBy"`
}

// swagger:parameters graphApp graphAppVersion graphNamespaces graphWorkload
type IncludeIdleEdges struct {
// Flag for including edges that have no request traffic for the time period.
//
// in: query
// required: false
// default: false
Name string `json:"includeIdleEdges"`
}

// swagger:parameters graphApp graphAppVersion graphNamespaces graphWorkload
type InjectServiceNodes struct {
// Flag for injecting the requested service node between source and destination nodes.
Expand Down
12 changes: 6 additions & 6 deletions graph/config/cytoscape/cytoscape.go
Expand Up @@ -73,12 +73,12 @@ type NodeData struct {
HasVS bool `json:"hasVS,omitempty"` // true (has route rule) | false
IsDead bool `json:"isDead,omitempty"` // true (has no pods) | false
IsGroup string `json:"isGroup,omitempty"` // set to the grouping type, current values: [ 'app', 'version' ]
IsIdle bool `json:"isIdle,omitempty"` // true | false
IsInaccessible bool `json:"isInaccessible,omitempty"` // true if the node exists in an inaccessible namespace
IsMisconfigured string `json:"isMisconfigured,omitempty"` // set to misconfiguration list, current values: [ 'labels' ]
IsOutside bool `json:"isOutside,omitempty"` // true | false
IsRoot bool `json:"isRoot,omitempty"` // true | false
IsServiceEntry string `json:"isServiceEntry,omitempty"` // set to the location, current values: [ 'MESH_EXTERNAL', 'MESH_INTERNAL' ]
IsUnused bool `json:"isUnused,omitempty"` // true | false
}

type EdgeData struct {
Expand Down Expand Up @@ -204,16 +204,16 @@ func buildConfig(trafficMap graph.TrafficMap, nodes *[]*NodeWrapper, edges *[]*E
nd.IsDead = val.(bool)
}

// node may be idle
if val, ok := n.Metadata[graph.IsIdle]; ok {
nd.IsIdle = val.(bool)
}

// node may be a root
if val, ok := n.Metadata[graph.IsRoot]; ok {
nd.IsRoot = val.(bool)
}

// node may be unused
if val, ok := n.Metadata[graph.IsUnused]; ok {
nd.IsUnused = val.(bool)
}

// node is not accessible to the current user
if val, ok := n.Metadata[graph.IsInaccessible]; ok {
nd.IsInaccessible = val.(bool)
Expand Down
2 changes: 1 addition & 1 deletion graph/meta.go
Expand Up @@ -22,13 +22,13 @@ const (
HasVS MetadataKey = "hasVS"
IsDead MetadataKey = "isDead"
IsEgressCluster MetadataKey = "isEgressCluster" // PassthroughCluster or BlackHoleCluster
IsIdle MetadataKey = "isIdle"
IsInaccessible MetadataKey = "isInaccessible"
IsMisconfigured MetadataKey = "isMisconfigured"
IsMTLS MetadataKey = "isMTLS"
IsOutside MetadataKey = "isOutside"
IsRoot MetadataKey = "isRoot"
IsServiceEntry MetadataKey = "isServiceEntry"
IsUnused MetadataKey = "isUnused"
ProtocolKey MetadataKey = "protocol"
ResponseTime MetadataKey = "responseTime"
SourcePrincipal MetadataKey = "sourcePrincipal"
Expand Down
14 changes: 14 additions & 0 deletions graph/options.go
Expand Up @@ -34,6 +34,7 @@ const (
defaultDuration string = "10m"
defaultGraphType string = GraphTypeWorkload
defaultGroupBy string = GroupByNone
defaultIncludeIdleEdges bool = false
defaultInjectServiceNodes bool = false
)

Expand Down Expand Up @@ -76,6 +77,7 @@ type RequestedAppenders struct {
type TelemetryOptions struct {
AccessibleNamespaces map[string]time.Time
Appenders RequestedAppenders // requested appenders, nil if param not supplied
IncludeIdleEdges bool // include edges with request rates of 0
InjectServiceNodes bool // inject destination service nodes between source and destination nodes.
Namespaces NamespaceInfoMap
CommonOptions
Expand Down Expand Up @@ -104,13 +106,15 @@ func NewOptions(r *net_http.Request) Options {
// query params
params := r.URL.Query()
var duration model.Duration
var includeIdleEdges bool
var injectServiceNodes bool
var queryTime int64
appenders := RequestedAppenders{All: true}
configVendor := params.Get("configVendor")
durationString := params.Get("duration")
graphType := params.Get("graphType")
groupBy := params.Get("groupBy")
includeIdleEdgesString := params.Get("includeIdleEdges")
injectServiceNodesString := params.Get("injectServiceNodes")
namespaces := params.Get("namespaces") // csl of namespaces
queryTimeString := params.Get("queryTime")
Expand Down Expand Up @@ -152,6 +156,15 @@ func NewOptions(r *net_http.Request) Options {
} else if groupBy != GroupByApp && groupBy != GroupByNone && groupBy != GroupByVersion {
BadRequest(fmt.Sprintf("Invalid groupBy [%s]", groupBy))
}
if includeIdleEdgesString == "" {
includeIdleEdges = defaultIncludeIdleEdges
} else {
var includeIdleEdgesErr error
includeIdleEdges, includeIdleEdgesErr = strconv.ParseBool(includeIdleEdgesString)
if includeIdleEdgesErr != nil {
BadRequest(fmt.Sprintf("Invalid includeIdleEdges [%s]", includeIdleEdgesString))
}
}
if injectServiceNodesString == "" {
injectServiceNodes = defaultInjectServiceNodes
} else {
Expand Down Expand Up @@ -237,6 +250,7 @@ func NewOptions(r *net_http.Request) Options {
TelemetryOptions: TelemetryOptions{
AccessibleNamespaces: accessibleNamespaces,
Appenders: appenders,
IncludeIdleEdges: includeIdleEdges,
InjectServiceNodes: injectServiceNodes,
Namespaces: namespaceMap,
CommonOptions: CommonOptions{
Expand Down
12 changes: 6 additions & 6 deletions graph/telemetry/istio/appender/appender.go
Expand Up @@ -26,6 +26,8 @@ func ParseAppenders(o graph.TelemetryOptions) []graph.Appender {
requestedAppenders[AggregateNodeAppenderName] = true
case DeadNodeAppenderName:
requestedAppenders[DeadNodeAppenderName] = true
case IdleNodeAppenderName:
requestedAppenders[IdleNodeAppenderName] = true
case IstioAppenderName:
requestedAppenders[IstioAppenderName] = true
case ResponseTimeAppenderName:
Expand All @@ -36,8 +38,6 @@ func ParseAppenders(o graph.TelemetryOptions) []graph.Appender {
requestedAppenders[ServiceEntryAppenderName] = true
case SidecarsCheckAppenderName:
requestedAppenders[SidecarsCheckAppenderName] = true
case UnusedNodeAppenderName:
requestedAppenders[UnusedNodeAppenderName] = true
case "":
// skip
default:
Expand All @@ -49,9 +49,9 @@ func ParseAppenders(o graph.TelemetryOptions) []graph.Appender {
// The appender order is important
// To pre-process service nodes run service_entry appender first
// To reduce processing, filter dead nodes next
// To reduce processing, next run appenders that don't apply to unused services
// To reduce processing, next run appenders that don't apply to idle (aka unused) services
// - lazily inject aggregate nodes so other decorations can influence the new nodes/edges, if necessary
// Add orphan (unused) services
// Add orphan (idle) services
// Run remaining appenders
var appenders []graph.Appender

Expand Down Expand Up @@ -111,9 +111,9 @@ func ParseAppenders(o graph.TelemetryOptions) []graph.Appender {
}
appenders = append(appenders, a)
}
if _, ok := requestedAppenders[UnusedNodeAppenderName]; ok || o.Appenders.All {
if _, ok := requestedAppenders[IdleNodeAppenderName]; ok || o.Appenders.All {
hasNodeOptions := o.App != "" || o.Workload != "" || o.Service != ""
a := UnusedNodeAppender{
a := IdleNodeAppender{
GraphType: o.GraphType,
InjectServiceNodes: o.InjectServiceNodes,
IsNodeGraph: hasNodeOptions,
Expand Down
Expand Up @@ -7,24 +7,24 @@ import (
"github.com/kiali/kiali/models"
)

const UnusedNodeAppenderName = "unusedNode"
const IdleNodeAppenderName = "idleNode"

// UnusedNodeAppender looks for services that have never seen request traffic. It adds nodes to represent the
// unused definitions. The added node types depend on the graph type and/or labeling on the definition.
// Name: unusedNode
type UnusedNodeAppender struct {
// IdleNodeAppender looks for services that have never seen request traffic. It adds nodes to represent the
// idle/unused definitions. The added node types depend on the graph type and/or labeling on the definition.
// Name: idleNode
type IdleNodeAppender struct {
GraphType string
InjectServiceNodes bool // This appender addes unused services only when service node are injected or graphType=service
InjectServiceNodes bool // This appender adds idle services only when service nodes are injected or graphType=service
IsNodeGraph bool // This appender does not operate on node detail graphs because we want to focus on the specific node.
}

// Name implements Appender
func (a UnusedNodeAppender) Name() string {
return UnusedNodeAppenderName
func (a IdleNodeAppender) Name() string {
return IdleNodeAppenderName
}

// AppendGraph implements Appender
func (a UnusedNodeAppender) AppendGraph(trafficMap graph.TrafficMap, globalInfo *graph.AppenderGlobalInfo, namespaceInfo *graph.AppenderNamespaceInfo) {
func (a IdleNodeAppender) AppendGraph(trafficMap graph.TrafficMap, globalInfo *graph.AppenderGlobalInfo, namespaceInfo *graph.AppenderNamespaceInfo) {
if a.IsNodeGraph {
return
}
Expand All @@ -50,30 +50,30 @@ func (a UnusedNodeAppender) AppendGraph(trafficMap graph.TrafficMap, globalInfo
services = getServiceDefinitionList(namespaceInfo).ServiceDefinitions
}

a.addUnusedNodes(trafficMap, namespaceInfo.Namespace, services, workloads)
a.addIdleNodes(trafficMap, namespaceInfo.Namespace, services, workloads)
}

func (a UnusedNodeAppender) addUnusedNodes(trafficMap graph.TrafficMap, namespace string, services []models.ServiceDetails, workloads []models.WorkloadListItem) {
unusedTrafficMap := a.buildUnusedTrafficMap(trafficMap, namespace, services, workloads)
func (a IdleNodeAppender) addIdleNodes(trafficMap graph.TrafficMap, namespace string, services []models.ServiceDetails, workloads []models.WorkloadListItem) {
idleNodeTrafficMap := a.buildIdleNodeTrafficMap(trafficMap, namespace, services, workloads)

// Integrate the unused nodes into the existing traffic map
for id, unusedNode := range unusedTrafficMap {
trafficMap[id] = unusedNode
// Integrate the idle nodes into the existing traffic map
for id, idleNode := range idleNodeTrafficMap {
trafficMap[id] = idleNode
}
}

func (a UnusedNodeAppender) buildUnusedTrafficMap(trafficMap graph.TrafficMap, namespace string, services []models.ServiceDetails, workloads []models.WorkloadListItem) graph.TrafficMap {
unusedTrafficMap := graph.NewTrafficMap()
func (a IdleNodeAppender) buildIdleNodeTrafficMap(trafficMap graph.TrafficMap, namespace string, services []models.ServiceDetails, workloads []models.WorkloadListItem) graph.TrafficMap {
idleNodeTrafficMap := graph.NewTrafficMap()

for _, s := range services {
id, nodeType := graph.Id(namespace, s.Service.Name, "", "", "", "", a.GraphType)
if _, found := trafficMap[id]; !found {
if _, found = unusedTrafficMap[id]; !found {
log.Tracef("Adding unused node for service [%s]", s.Service.Name)
if _, found = idleNodeTrafficMap[id]; !found {
log.Tracef("Adding idle node for service [%s]", s.Service.Name)
node := graph.NewNodeExplicit(id, namespace, "", "", "", s.Service.Name, nodeType, a.GraphType)
// note: we don't know what the protocol really should be, http is most common, it's a dead edge anyway
node.Metadata = graph.Metadata{"httpIn": 0.0, "httpOut": 0.0, "isUnused": true}
unusedTrafficMap[id] = &node
node.Metadata = graph.Metadata{"httpIn": 0.0, "httpOut": 0.0, graph.IsIdle: true}
idleNodeTrafficMap[id] = &node
}
}
}
Expand All @@ -93,14 +93,14 @@ func (a UnusedNodeAppender) buildUnusedTrafficMap(trafficMap graph.TrafficMap, n
}
id, nodeType := graph.Id("", "", namespace, w.Name, app, version, a.GraphType)
if _, found := trafficMap[id]; !found {
if _, found = unusedTrafficMap[id]; !found {
log.Tracef("Adding unused node for workload [%s] with labels [%v]", w.Name, labels)
if _, found = idleNodeTrafficMap[id]; !found {
log.Tracef("Adding idle node for workload [%s] with labels [%v]", w.Name, labels)
node := graph.NewNodeExplicit(id, namespace, w.Name, app, version, "", nodeType, a.GraphType)
// note: we don't know what the protocol really should be, http is most common, it's a dead edge anyway
node.Metadata = graph.Metadata{"httpIn": 0.0, "httpOut": 0.0, "isUnused": true}
unusedTrafficMap[id] = &node
node.Metadata = graph.Metadata{"httpIn": 0.0, "httpOut": 0.0, graph.IsIdle: true}
idleNodeTrafficMap[id] = &node
}
}
}
return unusedTrafficMap
return idleNodeTrafficMap
}