forked from kiali/kiali
-
Notifications
You must be signed in to change notification settings - Fork 0
/
no_dest_checker.go
128 lines (110 loc) · 3.33 KB
/
no_dest_checker.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
package destinationrules
import (
"strconv"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"github.com/kiali/kiali/config"
"github.com/kiali/kiali/kubernetes"
"github.com/kiali/kiali/models"
)
type NoDestinationChecker struct {
Namespace string
WorkloadList models.WorkloadList
DestinationRule kubernetes.IstioObject
ServiceEntries map[string][]string
Services []v1.Service
}
// Check parses the DestinationRule definitions and verifies that they point to an existing service, including any subset definitions
func (n NoDestinationChecker) Check() ([]*models.IstioCheck, bool) {
valid := true
validations := make([]*models.IstioCheck, 0)
if host, ok := n.DestinationRule.GetSpec()["host"]; ok {
if dHost, ok := host.(string); ok {
fqdn := kubernetes.ParseHost(dHost, n.DestinationRule.GetObjectMeta().Namespace, n.DestinationRule.GetObjectMeta().ClusterName)
if !n.hasMatchingService(fqdn.Service, dHost) {
validation := models.Build("destinationrules.nodest.matchingworkload", "spec/host")
validations = append(validations, &validation)
valid = false
}
if subsets, ok := n.DestinationRule.GetSpec()["subsets"]; ok {
if dSubsets, ok := subsets.([]interface{}); ok {
// Check that each subset has a matching workload somewhere..
for i, subset := range dSubsets {
if innerSubset, ok := subset.(map[string]interface{}); ok {
if labels, ok := innerSubset["labels"]; ok {
if dLabels, ok := labels.(map[string]interface{}); ok {
stringLabels := make(map[string]string, len(dLabels))
for k, v := range dLabels {
if s, ok := v.(string); ok {
stringLabels[k] = s
}
}
if !n.hasMatchingWorkload(fqdn.Service, stringLabels) {
validation := models.Build("destinationrules.nodest.subsetlabels",
"spec/subsets["+strconv.Itoa(i)+"]")
validations = append(validations, &validation)
valid = false
}
}
}
}
}
}
}
}
}
return validations, valid
}
func (n NoDestinationChecker) hasMatchingWorkload(service string, subsetLabels map[string]string) bool {
// Check wildcard hosts
if service == "*" {
return true
}
var selectors map[string]string
// Find the correct service
for _, s := range n.Services {
if s.Name == service {
selectors = s.Spec.Selector
}
}
// Check workloads
if selectors == nil || len(selectors) == 0 {
return false
}
selector := labels.SelectorFromSet(labels.Set(selectors))
subsetLabelSet := labels.Set(subsetLabels)
subsetSelector := labels.SelectorFromSet(subsetLabelSet)
for _, wl := range n.WorkloadList.Workloads {
wlLabelSet := labels.Set(wl.Labels)
if selector.Matches(wlLabelSet) {
if subsetSelector.Matches(wlLabelSet) {
return true
}
}
}
return false
}
func (n NoDestinationChecker) hasMatchingService(service, origHost string) bool {
appLabel := config.Get().IstioLabels.AppLabelName
// Check wildcard hosts
if service == "*" {
return true
}
// Check Workloads
for _, wl := range n.WorkloadList.Workloads {
if service == wl.Labels[appLabel] {
return true
}
}
// Check ServiceNames
for _, s := range n.Services {
if service == s.Name {
return true
}
}
// Check ServiceEntries
if _, found := n.ServiceEntries[origHost]; found {
return true
}
return false
}