api_topologies: Add a selectType field to option groups

This field changes the option group behaviour depending on its value.
Currently only supports two values:
"one" (default): Old behaviour, one option can be selected
"union": Any number of options can be selected, and the filters are OR-ed togther

It is written in such a way as to easily enable a future "intersection" option,
as per union but AND-ing the filters. But this is not done here. YAGNI.
This commit is contained in:
Mike Lang
2017-03-21 18:40:02 -07:00
committed by jpellizzari
parent fff47ee609
commit efb68fb2da
2 changed files with 63 additions and 11 deletions

View File

@@ -5,8 +5,10 @@ import (
"net/http"
"net/url"
"sort"
"strings"
"sync"
log "github.com/Sirupsen/logrus"
"github.com/gorilla/mux"
"golang.org/x/net/context"
@@ -295,6 +297,59 @@ type APITopologyOptionGroup struct {
ID string `json:"id"`
Default string `json:"defaultValue,omitempty"`
Options []APITopologyOption `json:"options,omitempty"`
// SelectType describes how options can be picked. Currently defined values:
// "one": Default if empty. Exactly one option may be picked from the list.
// "union": Any number of options may be picked. Nodes matching any option filter selected are displayed.
// Value and Default should be a ","-seperated list.
SelectType string `json:"selectType,omitempty"`
}
// Get the render filters to use for this option group as a Decorator, if any.
// If second arg is false, no decorator was needed.
func (g APITopologyOptionGroup) getFilterDecorator(value string) (render.Decorator, bool) {
if value == "" {
value = g.Default
}
selectType := g.SelectType
if selectType == "" {
selectType = "one"
}
var values []string
switch selectType {
case "one":
values = []string{value}
case "union":
values = strings.Split(value, ",")
default:
log.Errorf("Invalid select type %s for option group %s, ignoring option", selectType, g.ID)
return nil, false
}
filters := []render.FilterFunc{}
for _, opt := range g.Options {
for _, v := range values {
if v != opt.Value {
continue
}
var filter render.FilterFunc
if opt.filter == nil {
// No filter means match everything (pseudo doesn't matter)
filter = func(n report.Node) bool { return true }
} else if opt.filterPseudo {
// Apply filter to pseudo topologies also
filter = opt.filter
} else {
// Allow all pseudo topology nodes, only apply filter to non-pseudo
filter = render.AnyFilterFunc(render.IsPseudoTopology, opt.filter)
}
filters = append(filters, filter)
}
}
if len(filters) == 0 {
return nil, false
}
// Since we've encoded whether to ignore pseudo topologies into each subfilter,
// we want no special behaviour for pseudo topologies here, which corresponds to MakePseudo
return render.MakeFilterPseudoDecorator(render.AnyFilterFunc(filters...)), true
}
// APITopologyOption describes a &param=value to a given topology.
@@ -437,17 +492,8 @@ func (r *Registry) RendererForTopology(topologyID string, values url.Values, rpt
var decorators []render.Decorator
for _, group := range topology.Options {
value := values.Get(group.ID)
for _, opt := range group.Options {
if opt.filter == nil {
continue
}
if (value == "" && group.Default == opt.Value) || (opt.Value != "" && opt.Value == value) {
if opt.filterPseudo {
decorators = append(decorators, render.MakeFilterPseudoDecorator(opt.filter))
} else {
decorators = append(decorators, render.MakeFilterDecorator(opt.filter))
}
}
if decorator, ok := group.getFilterDecorator(value); ok {
decorators = append(decorators, decorator)
}
}
if len(decorators) > 0 {

View File

@@ -321,6 +321,12 @@ func IsNotPseudo(n report.Node) bool {
return n.Topology != Pseudo || strings.HasSuffix(n.ID, TheInternetID) || strings.HasPrefix(n.ID, ServiceNodeIDPrefix)
}
// IsPseudoTopology returns true if the node is in a pseudo topology,
// mimicing the check performed by MakeFilter() instead of the more complex check in IsNotPseudo()
func IsPseudoTopology(n report.Node) bool {
return n.Topology == Pseudo
}
// IsNamespace checks if the node is a pod/service in the specified namespace
func IsNamespace(namespace string) FilterFunc {
return func(n report.Node) bool {