Skip to content

Commit 11b964f

Browse files
authored
NETOBSERV-2056 manage field types & formats from config (#681)
* manage field types & formats from config * rename 'array' to '[]' * improve forceType to avoid failures on wrong types
1 parent e4fe8e5 commit 11b964f

File tree

9 files changed

+100
-84
lines changed

9 files changed

+100
-84
lines changed

config/sample-config.yaml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,7 @@ frontend:
12721272
description: Source namespace
12731273
- name: SrcAddr
12741274
type: string
1275+
format: IP
12751276
description: Source IP address (ipv4 or ipv6)
12761277
- name: SrcPort
12771278
type: number
@@ -1281,6 +1282,7 @@ frontend:
12811282
description: Source MAC address
12821283
- name: SrcK8S_HostIP
12831284
type: string
1285+
format: IP
12841286
description: Source node IP
12851287
- name: SrcK8S_HostName
12861288
type: string
@@ -1308,6 +1310,7 @@ frontend:
13081310
description: Destination namespace
13091311
- name: DstAddr
13101312
type: string
1313+
format: IP
13111314
description: Destination IP address (ipv4 or ipv6)
13121315
- name: DstPort
13131316
type: number
@@ -1317,6 +1320,7 @@ frontend:
13171320
description: Destination MAC address
13181321
- name: DstK8S_HostIP
13191322
type: string
1323+
format: IP
13201324
description: Destination node IP
13211325
- name: DstK8S_HostName
13221326
type: string
@@ -1353,16 +1357,16 @@ frontend:
13531357
- 1: Egress (outgoing traffic, from the node observation point) +
13541358
- 2: Inner (with the same source and destination node)
13551359
- name: IfDirections
1356-
type: number
1360+
type: number[]
13571361
description: |
13581362
Flow directions from the network interface observation point. Can be one of: +
13591363
- 0: Ingress (interface incoming traffic) +
13601364
- 1: Egress (interface outgoing traffic)
13611365
- name: Interfaces
1362-
type: string
1366+
type: string[]
13631367
description: Network interfaces
13641368
- name: Flags
1365-
type: string
1369+
type: string[]
13661370
description: |
13671371
Logical OR combination of unique TCP flags comprised in the flow, as per RFC-9293, with additional custom flags to represent the following per-packet combinations: +
13681372
- SYN+ACK (0x100) +
@@ -1410,7 +1414,7 @@ frontend:
14101414
type: number
14111415
description: TCP Smoothed Round Trip Time (SRTT), in nanoseconds
14121416
- name: NetworkEvents
1413-
type: string
1417+
type: string[]
14141418
description: Network events flow monitoring
14151419
- name: K8S_ClusterName
14161420
type: string

pkg/config/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ type QuickFilter struct {
115115
type FieldConfig struct {
116116
Name string `yaml:"name" json:"name"`
117117
Type string `yaml:"type" json:"type"`
118+
Format string `yaml:"format,omitempty" json:"format,omitempty"`
118119
Description string `yaml:"description" json:"description"`
119120
// lokiLabel flag is for documentation only. Use loki.labels instead
120121
Filter string `yaml:"filter,omitempty" json:"filter,omitempty"`
@@ -218,6 +219,12 @@ func ReadFile(version, date, filename string) (*Config, error) {
218219
if cfg.IsLokiEnabled() {
219220
cfg.Frontend.DataSources = append(cfg.Frontend.DataSources, string(constants.DataSourceLoki))
220221
cfg.Frontend.LokiMocks = cfg.Loki.UseMocks
222+
cfg.Loki.FieldsType = make(map[string]string)
223+
cfg.Loki.FieldsFormat = make(map[string]string)
224+
for _, f := range cfg.Frontend.Fields {
225+
cfg.Loki.FieldsType[f.Name] = f.Type
226+
cfg.Loki.FieldsFormat[f.Name] = f.Format
227+
}
221228
}
222229

223230
if cfg.IsPromEnabled() {

pkg/config/loki.go

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
package config
22

3-
import "github.com/netobserv/network-observability-console-plugin/pkg/utils"
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/netobserv/network-observability-console-plugin/pkg/utils"
8+
)
49

510
type Loki struct {
6-
URL string `yaml:"url" json:"url"`
7-
Labels []string `yaml:"labels" json:"labels"`
8-
StatusURL string `yaml:"statusUrl,omitempty" json:"statusUrl,omitempty"`
9-
Timeout Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`
10-
TenantID string `yaml:"tenantID,omitempty" json:"tenantID,omitempty"`
11-
TokenPath string `yaml:"tokenPath,omitempty" json:"tokenPath,omitempty"`
12-
SkipTLS bool `yaml:"skipTls,omitempty" json:"skipTls,omitempty"`
13-
CAPath string `yaml:"caPath,omitempty" json:"caPath,omitempty"`
14-
StatusSkipTLS bool `yaml:"statusSkipTls,omitempty" json:"statusSkipTls,omitempty"`
15-
StatusCAPath string `yaml:"statusCaPath,omitempty" json:"statusCaPath,omitempty"`
16-
StatusUserCertPath string `yaml:"statusUserCertPath,omitempty" json:"statusUserCertPath,omitempty"`
17-
StatusUserKeyPath string `yaml:"statusUserKeyPath,omitempty" json:"statusUserKeyPath,omitempty"`
18-
UseMocks bool `yaml:"useMocks,omitempty" json:"useMocks,omitempty"`
19-
ForwardUserToken bool `yaml:"forwardUserToken,omitempty" json:"forwardUserToken,omitempty"`
11+
URL string `yaml:"url" json:"url"`
12+
Labels []string `yaml:"labels" json:"labels"`
13+
FieldsType map[string]string `yaml:"fieldsType" json:"fieldsType"`
14+
FieldsFormat map[string]string `yaml:"fieldsFormat" json:"fieldsFormat"`
15+
StatusURL string `yaml:"statusUrl,omitempty" json:"statusUrl,omitempty"`
16+
Timeout Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`
17+
TenantID string `yaml:"tenantID,omitempty" json:"tenantID,omitempty"`
18+
TokenPath string `yaml:"tokenPath,omitempty" json:"tokenPath,omitempty"`
19+
SkipTLS bool `yaml:"skipTls,omitempty" json:"skipTls,omitempty"`
20+
CAPath string `yaml:"caPath,omitempty" json:"caPath,omitempty"`
21+
StatusSkipTLS bool `yaml:"statusSkipTls,omitempty" json:"statusSkipTls,omitempty"`
22+
StatusCAPath string `yaml:"statusCaPath,omitempty" json:"statusCaPath,omitempty"`
23+
StatusUserCertPath string `yaml:"statusUserCertPath,omitempty" json:"statusUserCertPath,omitempty"`
24+
StatusUserKeyPath string `yaml:"statusUserKeyPath,omitempty" json:"statusUserKeyPath,omitempty"`
25+
UseMocks bool `yaml:"useMocks,omitempty" json:"useMocks,omitempty"`
26+
ForwardUserToken bool `yaml:"forwardUserToken,omitempty" json:"forwardUserToken,omitempty"`
2027
labelsMap map[string]struct{}
2128
}
2229

@@ -34,3 +41,22 @@ func (l *Loki) IsLabel(key string) bool {
3441
_, isLabel := l.labelsMap[key]
3542
return isLabel
3643
}
44+
45+
func (l *Loki) IsNumeric(v string) bool {
46+
// check on Field / SrcField / DstField since we remove prefix in some cases for common filtering
47+
types := fmt.Sprintf("%s|%s|%s", l.FieldsType[v], l.FieldsType["Src"+v], l.FieldsType["Dst"+v])
48+
return strings.Contains(types, "number")
49+
}
50+
51+
func (l *Loki) IsIP(v string) bool {
52+
// check on Field / SrcField / DstField since we remove prefix in some cases for common filtering
53+
formats := fmt.Sprintf("%s|%s|%s", l.FieldsFormat[v], l.FieldsFormat["Src"+v], l.FieldsFormat["Dst"+v])
54+
return strings.Contains(formats, "IP")
55+
}
56+
57+
func (l *Loki) IsArray(v string) bool {
58+
// check on Field / SrcField / DstField since we remove prefix in some cases for common filtering
59+
types := fmt.Sprintf("%s|%s|%s", l.FieldsType[v], l.FieldsType["Src"+v], l.FieldsType["Dst"+v])
60+
return strings.Contains(types, "[]")
61+
62+
}

pkg/loki/flow_query.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (q *FlowQueryBuilder) addFilter(filter filters.Match) error {
119119
if lf, ok := filter.ToLabelFilter(); ok {
120120
q.labelFilters = append(q.labelFilters, lf)
121121
}
122-
} else if fields.IsIP(filter.Key) {
122+
} else if q.config.IsIP(filter.Key) {
123123
q.addIPFilters(filter.Key, values, filter.Not)
124124
} else {
125125
q.addLineFilters(filter.Key, values, filter.Not, filter.MoreThanOrEqual)
@@ -133,12 +133,12 @@ func (q *FlowQueryBuilder) addLineFilters(key string, values []string, not bool,
133133
return
134134
}
135135

136-
if fields.IsArray(key) {
136+
if q.config.IsArray(key) {
137137
q.lineFilters = append(q.lineFilters, filters.ArrayLineFilter(key, values, not))
138138
} else {
139139
var lf filters.LineFilter
140140
var hasEmptyMatch bool
141-
if fields.IsNumeric(key) {
141+
if q.config.IsNumeric(key) {
142142
lf, hasEmptyMatch = filters.NumericLineFilter(key, values, not, moreThan)
143143
} else {
144144
lf, hasEmptyMatch = filters.StringLineFilterCheckExact(key, values, not)

pkg/model/fields/fields.go

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -61,54 +61,3 @@ const (
6161
XlatDstAddr = "XlatDstAddr"
6262
XlatZoneID = "ZoneId"
6363
)
64-
65-
func IsNumeric(v string) bool {
66-
switch v {
67-
case
68-
DNSID,
69-
DNSLatency,
70-
DNSErrNo,
71-
TimeFlowRTT,
72-
Port,
73-
SrcPort,
74-
DstPort,
75-
Packets,
76-
Proto,
77-
Bytes,
78-
DSCP,
79-
XlatDstPort,
80-
XlatSrcPort,
81-
XlatZoneID:
82-
return true
83-
default:
84-
return false
85-
}
86-
}
87-
88-
func IsIP(f string) bool {
89-
switch f {
90-
case
91-
DstAddr,
92-
SrcAddr,
93-
DstHostIP,
94-
SrcHostIP,
95-
XlatDstAddr,
96-
XlatSrcAddr:
97-
return true
98-
default:
99-
return false
100-
}
101-
}
102-
103-
func IsArray(v string) bool {
104-
switch v {
105-
case
106-
IfDirections,
107-
Interfaces,
108-
NetworkEvents,
109-
TCPFlags:
110-
return true
111-
default:
112-
return false
113-
}
114-
}

pkg/server/server_flows_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,15 @@ func TestLokiFiltering(t *testing.T) {
259259
Loki: config.Loki{
260260
URL: lokiSvc.URL,
261261
Labels: []string{"SrcK8S_Namespace", "SrcK8S_OwnerName", "DstK8S_Namespace", "DstK8S_OwnerName", "FlowDirection"},
262+
FieldsType: map[string]string{
263+
"Proto": "number",
264+
"SrcPort": "number",
265+
"DstPort": "number",
266+
},
267+
FieldsFormat: map[string]string{
268+
"SrcAddr": "IP",
269+
"DstAddr": "IP",
270+
},
262271
},
263272
Frontend: config.Frontend{Deduper: config.Deduper{Mark: true}},
264273
}, &authM)

web/src/components/query-summary/summary-panel-content.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ export const SummaryPanelContent: React.FC<SummaryPanelContentProps> = ({
9696
<>
9797
{tc.objects
9898
.sort((a, b) => compareStrings(a.namespace ? a.namespace : '', b.namespace ? b.namespace : ''))
99-
.flatMap(o => (
100-
<AccordionExpandedContentBody>
99+
.flatMap((o, i) => (
100+
<AccordionExpandedContentBody key={`expanded-content-body${i}`}>
101101
{o.namespace && <ResourceLink key={`${tc.type}-${o.namespace}`} kind={'Namespace'} name={o.namespace} />}
102102
{o.names
103103
.sort((a, b) => compareStrings(a, b))

web/src/utils/column-parser.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,32 @@ const forceType = (id: ColumnsId, value: ColValue, type?: FieldType): ColValue =
8888
console.error('Column ' + id + " doesn't specify type");
8989
}
9090
// check if value type match and convert it if needed
91-
if (value && value !== '' && typeof value !== type && !Array.isArray(value)) {
92-
switch (type) {
93-
case 'number':
94-
return Number(value);
95-
case 'string':
96-
return String(value);
97-
default:
98-
throw new Error('forceType error: type ' + type + ' is not managed');
91+
if (value && value !== '' && typeof value !== type) {
92+
if (Array.isArray(value)) {
93+
switch (type) {
94+
case 'number[]':
95+
return value.map(v => Number(v));
96+
case 'string[]':
97+
return value.map(v => String(v));
98+
case 'number':
99+
case 'string':
100+
throw new Error("forceType error: can't convert an array to " + type);
101+
default:
102+
throw new Error('forceType error: type ' + type + ' is not managed for arrays');
103+
}
104+
} else {
105+
switch (type) {
106+
case 'number[]':
107+
return [Number(value)] as number[];
108+
case 'number':
109+
return Number(value);
110+
case 'string[]':
111+
return [String(value)] as string[];
112+
case 'string':
113+
return String(value);
114+
default:
115+
throw new Error('forceType error: type ' + type + ' is not managed');
116+
}
99117
}
100118
} else {
101119
// else return value directly

web/src/utils/fields.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
export type FieldType = 'string' | 'number';
1+
export type FieldType = 'string' | 'string[]' | 'number' | 'number[]';
2+
3+
export type FieldFormat = 'IP';
24

35
export interface FieldConfig {
46
name: string;
57
type: FieldType;
8+
format?: FieldFormat;
69
description: string;
710
filter?: string;
811
}

0 commit comments

Comments
 (0)