@@ -2,15 +2,36 @@ package matcher
2
2
3
3
import (
4
4
"fmt"
5
- "strings"
6
-
7
5
"github.com/mattfenwick/collections/pkg/json"
8
6
"github.com/mattfenwick/collections/pkg/slice"
7
+ "golang.org/x/exp/slices"
8
+ v1 "k8s.io/api/core/v1"
9
+ "strings"
10
+
9
11
"github.com/mattfenwick/cyclonus/pkg/kube"
10
12
"github.com/olekukonko/tablewriter"
11
13
"github.com/pkg/errors"
12
14
)
13
15
16
+ // peerProtocolGroup groups all anps and banps in single struct
17
+ type peerProtocolGroup struct {
18
+ port string
19
+ subject string
20
+ policies map [string ]* anpGroup
21
+ }
22
+
23
+ // dummy implementation of the interface so we add the struct to the targer peers
24
+ func (p * peerProtocolGroup ) Matches (subject , peer * TrafficPeer , portInt int , portName string , protocol v1.Protocol ) bool {
25
+ return false
26
+ }
27
+
28
+ type anpGroup struct {
29
+ name string
30
+ priority int
31
+ effects []string
32
+ kind PolicyKind
33
+ }
34
+
14
35
type SliceBuilder struct {
15
36
Prefix []string
16
37
Elements [][]string
@@ -26,14 +47,14 @@ func (p *Policy) ExplainTable() string {
26
47
table .SetAutoWrapText (false )
27
48
table .SetRowLine (true )
28
49
table .SetAutoMergeCells (true )
29
- // FIXME add action/priority column
30
- table .SetHeader ([]string {"Type" , "Subject" , "Source rules" , "Peer" , "Port/Protocol" })
50
+
51
+ table .SetHeader ([]string {"Type" , "Subject" , "Source rules" , "Peer" , "Action" , " Port/Protocol" })
31
52
32
53
builder := & SliceBuilder {}
33
54
ingresses , egresses := p .SortedTargets ()
34
55
builder .TargetsTableLines (ingresses , true )
35
- // FIXME add action/priority column
36
- builder .Elements = append (builder .Elements , []string {"" , "" , "" , "" , "" })
56
+
57
+ builder .Elements = append (builder .Elements , []string {"" , "" , "" , "" , "" , "" })
37
58
builder .TargetsTableLines (egresses , false )
38
59
39
60
table .AppendBulk (builder .Elements )
@@ -50,68 +71,89 @@ func (s *SliceBuilder) TargetsTableLines(targets []*Target, isIngress bool) {
50
71
ruleType = "Egress"
51
72
}
52
73
for _ , target := range targets {
53
- sourceRules := slice . Sort ( target .SourceRules )
74
+ sourceRules := target .SourceRules
54
75
sourceRulesStrings := make ([]string , 0 , len (sourceRules ))
55
76
for _ , rule := range sourceRules {
56
77
sourceRulesStrings = append (sourceRulesStrings , string (rule ))
57
78
}
79
+ slices .Sort (sourceRulesStrings )
58
80
rules := strings .Join (sourceRulesStrings , "\n " )
59
81
s .Prefix = []string {ruleType , target .TargetString (), rules }
60
82
61
83
if len (target .Peers ) == 0 {
62
- s .Append ("no pods, no ips" , "no ports, no protocols" )
63
- } else {
64
- for _ , peer := range slice .SortOn (func (p PeerMatcher ) string { return json .MustMarshalToString (p ) }, target .Peers ) {
65
- switch a := peer .(type ) {
66
- case * PeerMatcherAdmin :
67
- s .PodPeerMatcherTableLines (a .PodPeerMatcher , a .effectFromMatch )
68
- case * AllPeersMatcher :
69
- s .Append ("all pods, all ips" , "all ports, all protocols" )
70
- case * PortsForAllPeersMatcher :
71
- pps := PortMatcherTableLines (a .Port , NetworkPolicyV1 )
72
- s .Append ("all pods, all ips" , strings .Join (pps , "\n " ))
73
- case * IPPeerMatcher :
74
- s .IPPeerMatcherTableLines (a )
75
- case * PodPeerMatcher :
76
- s .PodPeerMatcherTableLines (a , NewV1Effect (true ))
77
- default :
78
- panic (errors .Errorf ("invalid PeerMatcher type %T" , a ))
79
- }
84
+ s .Append ("no pods, no ips" , "NPv1: All peers allowed" , "no ports, no protocols" )
85
+ continue
86
+ }
87
+
88
+ peers := groupAnbAndBanp (target .Peers )
89
+ for _ , p := range slice .SortOn (func (p PeerMatcher ) string { return json .MustMarshalToString (p ) }, peers ) {
90
+ switch t := p .(type ) {
91
+ case * AllPeersMatcher :
92
+ s .Append ("all pods, all ips" , "NPv1: All peers allowed" , "all ports, all protocols" )
93
+ case * PortsForAllPeersMatcher :
94
+ pps := PortMatcherTableLines (t .Port , NetworkPolicyV1 )
95
+ s .Append ("all pods, all ips" , "NPv1: All peers allowed" , strings .Join (pps , "\n " ))
96
+ case * IPPeerMatcher :
97
+ s .IPPeerMatcherTableLines (t )
98
+ case * PodPeerMatcher :
99
+ s .Append (resolveSubject (t ), "NPv1: All peers allowed" , strings .Join (PortMatcherTableLines (t .Port , NewV1Effect (true ).PolicyKind ), "\n " ))
100
+ case * peerProtocolGroup :
101
+ s .peerProtocolGroupTableLines (t )
102
+ default :
103
+ panic (errors .Errorf ("invalid PeerMatcher type %T" , p ))
80
104
}
81
105
}
106
+
82
107
}
83
108
}
84
109
85
110
func (s * SliceBuilder ) IPPeerMatcherTableLines (ip * IPPeerMatcher ) {
86
111
peer := ip .IPBlock .CIDR + "\n " + fmt .Sprintf ("except %+v" , ip .IPBlock .Except )
87
112
pps := PortMatcherTableLines (ip .Port , NetworkPolicyV1 )
88
- s .Append (peer , strings .Join (pps , "\n " ))
113
+ s .Append (peer , "NPv1: All peers allowed" , strings .Join (pps , "\n " ))
89
114
}
90
115
91
- func (s * SliceBuilder ) PodPeerMatcherTableLines (nsPodMatcher * PodPeerMatcher , e Effect ) {
92
- // FIXME add action/priority column using fields of the Effect parameter "e"
93
- var namespaces string
94
- switch ns := nsPodMatcher .Namespace .(type ) {
95
- case * AllNamespaceMatcher :
96
- namespaces = "all"
97
- case * LabelSelectorNamespaceMatcher :
98
- namespaces = kube .LabelSelectorTableLines (ns .Selector )
99
- // FIXME handle SameLabels, NotSameLabels
100
- case * ExactNamespaceMatcher :
101
- namespaces = ns .Namespace
102
- default :
103
- panic (errors .Errorf ("invalid NamespaceMatcher type %T" , ns ))
116
+ func (s * SliceBuilder ) peerProtocolGroupTableLines (t * peerProtocolGroup ) {
117
+ actions := []string {}
118
+
119
+ anps := make ([]* anpGroup , 0 , len (t .policies ))
120
+ for _ , v := range t .policies {
121
+ if v .kind == AdminNetworkPolicy {
122
+ anps = append (anps , v )
123
+ }
104
124
}
105
- var pods string
106
- switch p := nsPodMatcher .Pod .(type ) {
107
- case * AllPodMatcher :
108
- pods = "all"
109
- case * LabelSelectorPodMatcher :
110
- pods = kube .LabelSelectorTableLines (p .Selector )
111
- default :
112
- panic (errors .Errorf ("invalid PodMatcher type %T" , p ))
125
+ if len (anps ) > 0 {
126
+ actions = append (actions , "ANP:" )
127
+ slices .SortFunc (anps , func (a , b * anpGroup ) bool {
128
+ return a .priority < b .priority
129
+ })
130
+ for _ , v := range anps {
131
+ if len (v .effects ) > 1 {
132
+ actions = append (actions , fmt .Sprintf (" pri=%d (%s): %s (ineffective rules: %s)" , v .priority , v .name , v .effects [0 ], strings .Join (v .effects [1 :], ", " )))
133
+ } else {
134
+ actions = append (actions , fmt .Sprintf (" pri=%d (%s): %s" , v .priority , v .name , v .effects [0 ]))
135
+ }
136
+ }
137
+ }
138
+
139
+ banps := make ([]* anpGroup , 0 , len (t .policies ))
140
+ for _ , v := range t .policies {
141
+ if v .kind == BaselineAdminNetworkPolicy {
142
+ banps = append (banps , v )
143
+ }
144
+ }
145
+ if len (banps ) > 0 {
146
+ actions = append (actions , "BANP:" )
147
+ for _ , v := range banps {
148
+ if len (v .effects ) > 1 {
149
+ actions = append (actions , fmt .Sprintf (" %s (ineffective rules: %s)" , v .effects [0 ], strings .Join (v .effects [1 :], ", " )))
150
+ } else {
151
+ actions = append (actions , fmt .Sprintf (" %s" , v .effects [0 ]))
152
+ }
153
+ }
113
154
}
114
- s .Append ("namespace: " + namespaces + "\n " + "pods: " + pods , strings .Join (PortMatcherTableLines (nsPodMatcher .Port , e .PolicyKind ), "\n " ))
155
+
156
+ s .Append (t .subject , strings .Join (actions , "\n " ), t .port )
115
157
}
116
158
117
159
func PortMatcherTableLines (pm PortMatcher , kind PolicyKind ) []string {
@@ -141,3 +183,81 @@ func PortMatcherTableLines(pm PortMatcher, kind PolicyKind) []string {
141
183
panic (errors .Errorf ("invalid PortMatcher type %T" , port ))
142
184
}
143
185
}
186
+
187
+ func groupAnbAndBanp (p []PeerMatcher ) []PeerMatcher {
188
+ result := make ([]PeerMatcher , 0 , len (p ))
189
+ groups := map [string ]* peerProtocolGroup {}
190
+
191
+ for _ , v := range p {
192
+ switch t := v .(type ) {
193
+ case * PeerMatcherAdmin :
194
+ k := t .Port .GetPrimaryKey () + t .Pod .PrimaryKey () + t .Namespace .PrimaryKey ()
195
+ if _ , ok := groups [k ]; ! ok {
196
+ groups [k ] = & peerProtocolGroup {
197
+ port : strings .Join (PortMatcherTableLines (t .PodPeerMatcher .Port , t .effectFromMatch .PolicyKind ), "\n " ),
198
+ subject : resolveSubject (t .PodPeerMatcher ),
199
+ policies : map [string ]* anpGroup {},
200
+ }
201
+ }
202
+ kg := t .Name
203
+ if _ , ok := groups [k ].policies [kg ]; ! ok {
204
+ groups [k ].policies [kg ] = & anpGroup {
205
+ name : t .Name ,
206
+ priority : t .effectFromMatch .Priority ,
207
+ effects : []string {},
208
+ kind : t .effectFromMatch .PolicyKind ,
209
+ }
210
+ }
211
+ groups [k ].policies [kg ].effects = append (groups [k ].policies [kg ].effects , string (t .effectFromMatch .Verdict ))
212
+ default :
213
+ result = append (result , v )
214
+ }
215
+ }
216
+
217
+ groupResult := make ([]* peerProtocolGroup , 0 , len (groups ))
218
+ for _ , v := range groups {
219
+ groupResult = append (groupResult , v )
220
+ }
221
+ slices .SortFunc (groupResult , func (a , b * peerProtocolGroup ) bool {
222
+ if a .port == b .port {
223
+ return a .subject < b .subject
224
+ }
225
+ return a .port < b .port
226
+ })
227
+
228
+ for _ , v := range groupResult {
229
+ result = append (result , v )
230
+ }
231
+
232
+ return result
233
+ }
234
+
235
+ func resolveSubject (nsPodMatcher * PodPeerMatcher ) string {
236
+ var namespaces string
237
+ var pods string
238
+ switch ns := nsPodMatcher .Namespace .(type ) {
239
+ case * AllNamespaceMatcher :
240
+ namespaces = "all"
241
+ case * LabelSelectorNamespaceMatcher :
242
+ namespaces = kube .LabelSelectorTableLines (ns .Selector )
243
+ case * SameLabelsNamespaceMatcher :
244
+ namespaces = fmt .Sprintf ("Same labels - %s" , strings .Join (ns .labels , ", " ))
245
+ case * NotSameLabelsNamespaceMatcher :
246
+ namespaces = fmt .Sprintf ("Not Same labels - %s" , strings .Join (ns .labels , ", " ))
247
+ case * ExactNamespaceMatcher :
248
+ namespaces = ns .Namespace
249
+ default :
250
+ panic (errors .Errorf ("invalid NamespaceMatcher type %T" , ns ))
251
+ }
252
+
253
+ switch p := nsPodMatcher .Pod .(type ) {
254
+ case * AllPodMatcher :
255
+ pods = "all"
256
+ case * LabelSelectorPodMatcher :
257
+ pods = kube .LabelSelectorTableLines (p .Selector )
258
+ default :
259
+ panic (errors .Errorf ("invalid PodMatcher type %T" , p ))
260
+ }
261
+
262
+ return fmt .Sprintf ("Namespace:\n %s\n Pod:\n %s" , strings .TrimSpace (namespaces ), strings .TrimSpace (pods ))
263
+ }
0 commit comments