Skip to content

Commit 49a77da

Browse files
authored
NETOBSERV-2484: improve ovn subnets detection (#2169)
* NETOBSERV-2484: improve ovn subnets detection - Check for internal/masquerade/transitswitch subnets - Add API server * add configurable masquerade subnet * Fetch kube apiserver IP, small refactoring (moving files) * Avoid configuring cidrs already covered for apiserver endpoint * Refactor detect subnets - move to different file - split into smaller functions * Add role for reading networks * Use EXT:* pattern for external ips
1 parent bdc695f commit 49a77da

File tree

13 files changed

+260
-146
lines changed

13 files changed

+260
-146
lines changed

bundle/manifests/netobserv-operator.clusterserviceversion.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,14 @@ spec:
10021002
- list
10031003
- update
10041004
- watch
1005+
- apiGroups:
1006+
- operator.openshift.io
1007+
resources:
1008+
- networks
1009+
verbs:
1010+
- get
1011+
- list
1012+
- watch
10051013
- apiGroups:
10061014
- rbac.authorization.k8s.io
10071015
resources:

config/rbac/role.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,14 @@ rules:
228228
- list
229229
- update
230230
- watch
231+
- apiGroups:
232+
- operator.openshift.io
233+
resources:
234+
- networks
235+
verbs:
236+
- get
237+
- list
238+
- watch
231239
- apiGroups:
232240
- rbac.authorization.k8s.io
233241
resources:

helm/templates/clusterrole.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,14 @@ rules:
227227
- list
228228
- update
229229
- watch
230+
- apiGroups:
231+
- operator.openshift.io
232+
resources:
233+
- networks
234+
verbs:
235+
- get
236+
- list
237+
- watch
230238
- apiGroups:
231239
- rbac.authorization.k8s.io
232240
resources:
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package flp
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"net"
8+
9+
flowslatest "github.com/netobserv/network-observability-operator/api/flowcollector/v1beta2"
10+
"github.com/netobserv/network-observability-operator/internal/pkg/cluster"
11+
"github.com/netobserv/network-observability-operator/internal/pkg/helper"
12+
configv1 "github.com/openshift/api/config/v1"
13+
operatorv1 "github.com/openshift/api/operator/v1"
14+
"gopkg.in/yaml.v2"
15+
corev1 "k8s.io/api/core/v1"
16+
"k8s.io/apimachinery/pkg/types"
17+
"sigs.k8s.io/controller-runtime/pkg/client"
18+
)
19+
20+
func (r *Reconciler) getOpenShiftSubnets(ctx context.Context) ([]flowslatest.SubnetLabel, error) {
21+
if !r.mgr.ClusterInfo.HasCNO() {
22+
return nil, nil
23+
}
24+
var errs []error
25+
var svcMachineCIDRs []*net.IPNet
26+
27+
pods, services, extIPs, err := readNetworkConfig(ctx, r)
28+
if err != nil {
29+
errs = append(errs, err)
30+
}
31+
for _, strCIDR := range services {
32+
if _, parsed, err := net.ParseCIDR(strCIDR); err == nil {
33+
svcMachineCIDRs = append(svcMachineCIDRs, parsed)
34+
}
35+
}
36+
37+
machines, err := readClusterConfig(ctx, r)
38+
if err != nil {
39+
errs = append(errs, err)
40+
}
41+
for _, strCIDR := range machines {
42+
if _, parsed, err := net.ParseCIDR(strCIDR); err == nil {
43+
svcMachineCIDRs = append(svcMachineCIDRs, parsed)
44+
}
45+
}
46+
47+
// API server
48+
if apiserverIPs, err := cluster.GetAPIServerEndpointIPs(ctx, r, r.mgr.ClusterInfo); err == nil {
49+
// Check if this isn't already an IP covered in Services or Machines subnets
50+
for _, ip := range apiserverIPs {
51+
if parsed := net.ParseIP(ip); parsed != nil {
52+
var alreadyCovered bool
53+
for _, cidr := range svcMachineCIDRs {
54+
if cidr.Contains(parsed) {
55+
alreadyCovered = true
56+
break
57+
}
58+
}
59+
if !alreadyCovered {
60+
cidr := helper.IPToCIDR(ip)
61+
services = append(services, cidr)
62+
}
63+
}
64+
}
65+
} else {
66+
errs = append(errs, fmt.Errorf("can't get API server endpoint IPs: %w", err))
67+
}
68+
69+
// Additional OVN subnets
70+
moreMachines, err := readNetworkOperatorConfig(ctx, r)
71+
if err != nil {
72+
errs = append(errs, err)
73+
}
74+
machines = append(machines, moreMachines...)
75+
76+
var subnets []flowslatest.SubnetLabel
77+
if len(machines) > 0 {
78+
subnets = append(subnets, flowslatest.SubnetLabel{
79+
Name: "Machines",
80+
CIDRs: machines,
81+
})
82+
}
83+
if len(pods) > 0 {
84+
subnets = append(subnets, flowslatest.SubnetLabel{
85+
Name: "Pods",
86+
CIDRs: pods,
87+
})
88+
}
89+
if len(services) > 0 {
90+
subnets = append(subnets, flowslatest.SubnetLabel{
91+
Name: "Services",
92+
CIDRs: services,
93+
})
94+
}
95+
if len(extIPs) > 0 {
96+
subnets = append(subnets, flowslatest.SubnetLabel{
97+
Name: "EXT:ExternalIP",
98+
CIDRs: extIPs,
99+
})
100+
}
101+
return subnets, errors.Join(errs...)
102+
}
103+
104+
func readNetworkConfig(ctx context.Context, cl client.Client) ([]string, []string, []string, error) {
105+
// Pods and Services subnets are found in CNO config
106+
var pods, services, extIPs []string
107+
network := &configv1.Network{}
108+
if err := cl.Get(ctx, types.NamespacedName{Name: "cluster"}, network); err != nil {
109+
return nil, nil, nil, fmt.Errorf("can't get Network (config) information: %w", err)
110+
}
111+
for _, podsNet := range network.Spec.ClusterNetwork {
112+
pods = append(pods, podsNet.CIDR)
113+
}
114+
services = network.Spec.ServiceNetwork
115+
if network.Spec.ExternalIP != nil && len(network.Spec.ExternalIP.AutoAssignCIDRs) > 0 {
116+
extIPs = network.Spec.ExternalIP.AutoAssignCIDRs
117+
}
118+
return pods, services, extIPs, nil
119+
}
120+
121+
func readClusterConfig(ctx context.Context, cl client.Client) ([]string, error) {
122+
// Nodes subnet found in CM cluster-config-v1 (kube-system)
123+
cm := &corev1.ConfigMap{}
124+
if err := cl.Get(ctx, types.NamespacedName{Name: "cluster-config-v1", Namespace: "kube-system"}, cm); err != nil {
125+
return nil, fmt.Errorf(`can't read "cluster-config-v1" ConfigMap: %w`, err)
126+
}
127+
return readMachineFromConfig(cm)
128+
}
129+
130+
func readMachineFromConfig(cm *corev1.ConfigMap) ([]string, error) {
131+
type ClusterConfig struct {
132+
Networking struct {
133+
MachineNetwork []struct {
134+
CIDR string `yaml:"cidr"`
135+
} `yaml:"machineNetwork"`
136+
} `yaml:"networking"`
137+
}
138+
139+
var rawConfig string
140+
var ok bool
141+
if rawConfig, ok = cm.Data["install-config"]; !ok {
142+
return nil, fmt.Errorf(`can't find key "install-config" in "cluster-config-v1" ConfigMap`)
143+
}
144+
var config ClusterConfig
145+
if err := yaml.Unmarshal([]byte(rawConfig), &config); err != nil {
146+
return nil, fmt.Errorf(`can't deserialize content of "cluster-config-v1" ConfigMap: %w`, err)
147+
}
148+
149+
var cidrs []string
150+
for _, cidr := range config.Networking.MachineNetwork {
151+
cidrs = append(cidrs, cidr.CIDR)
152+
}
153+
154+
return cidrs, nil
155+
}
156+
157+
func readNetworkOperatorConfig(ctx context.Context, cl client.Client) ([]string, error) {
158+
// Additional OVN subnets: https://github.com/openshift/cluster-network-operator/blob/fda7a9f07ab6f78d032d310cdd77f21d04f1289a/pkg/network/ovn_kubernetes.go#L76-L77
159+
var machines []string
160+
networkOp := &operatorv1.Network{}
161+
if err := cl.Get(ctx, types.NamespacedName{Name: "cluster"}, networkOp); err != nil {
162+
return nil, fmt.Errorf("can't get Network (operator) information: %w", err)
163+
}
164+
internalSubnet := "100.64.0.0/16"
165+
transitSwitchSubnet := "100.88.0.0/16"
166+
masqueradeSubnet := "169.254.0.0/17"
167+
ovnk := networkOp.Spec.DefaultNetwork.OVNKubernetesConfig
168+
if ovnk != nil {
169+
if ovnk.V4InternalSubnet != "" {
170+
internalSubnet = ovnk.V4InternalSubnet
171+
}
172+
if ovnk.IPv4 != nil && ovnk.IPv4.InternalTransitSwitchSubnet != "" {
173+
transitSwitchSubnet = ovnk.IPv4.InternalTransitSwitchSubnet
174+
}
175+
if ovnk.GatewayConfig != nil && ovnk.GatewayConfig.IPv4.InternalMasqueradeSubnet != "" {
176+
masqueradeSubnet = ovnk.GatewayConfig.IPv4.InternalMasqueradeSubnet
177+
}
178+
}
179+
machines = append(machines, internalSubnet)
180+
machines = append(machines, transitSwitchSubnet)
181+
machines = append(machines, masqueradeSubnet)
182+
return machines, nil
183+
}

internal/controller/flp/flp_controller.go

Lines changed: 0 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,9 @@ import (
1313
"github.com/netobserv/network-observability-operator/internal/pkg/manager"
1414
"github.com/netobserv/network-observability-operator/internal/pkg/manager/status"
1515
"github.com/netobserv/network-observability-operator/internal/pkg/watchers"
16-
configv1 "github.com/openshift/api/config/v1"
17-
"gopkg.in/yaml.v2"
1816
appsv1 "k8s.io/api/apps/v1"
1917
ascv2 "k8s.io/api/autoscaling/v2"
2018
corev1 "k8s.io/api/core/v1"
21-
"k8s.io/apimachinery/pkg/types"
2219
ctrl "sigs.k8s.io/controller-runtime"
2320
"sigs.k8s.io/controller-runtime/pkg/client"
2421
"sigs.k8s.io/controller-runtime/pkg/handler"
@@ -230,89 +227,3 @@ func reconcileMonitoringCerts(ctx context.Context, info *reconcilers.Common, tls
230227

231228
return nil
232229
}
233-
234-
func (r *Reconciler) getOpenShiftSubnets(ctx context.Context) ([]flowslatest.SubnetLabel, error) {
235-
var subnets []flowslatest.SubnetLabel
236-
237-
// Pods and Services subnets are found in CNO config
238-
if r.mgr.ClusterInfo.HasCNO() {
239-
network := &configv1.Network{}
240-
err := r.Get(ctx, types.NamespacedName{Name: "cluster"}, network)
241-
if err != nil {
242-
return nil, fmt.Errorf("can't get Network information: %w", err)
243-
}
244-
var podCIDRs []string
245-
for _, podsNet := range network.Spec.ClusterNetwork {
246-
podCIDRs = append(podCIDRs, podsNet.CIDR)
247-
}
248-
if len(podCIDRs) > 0 {
249-
subnets = append(subnets, flowslatest.SubnetLabel{
250-
Name: "Pods",
251-
CIDRs: podCIDRs,
252-
})
253-
}
254-
if len(network.Spec.ServiceNetwork) > 0 {
255-
subnets = append(subnets, flowslatest.SubnetLabel{
256-
Name: "Services",
257-
CIDRs: network.Spec.ServiceNetwork,
258-
})
259-
}
260-
if network.Spec.ExternalIP != nil && len(network.Spec.ExternalIP.AutoAssignCIDRs) > 0 {
261-
subnets = append(subnets, flowslatest.SubnetLabel{
262-
Name: "ExternalIP",
263-
CIDRs: network.Spec.ExternalIP.AutoAssignCIDRs,
264-
})
265-
}
266-
}
267-
268-
// Nodes subnet found in CM cluster-config-v1 (kube-system)
269-
cm := &corev1.ConfigMap{}
270-
if err := r.Get(ctx, types.NamespacedName{Name: "cluster-config-v1", Namespace: "kube-system"}, cm); err != nil {
271-
return nil, fmt.Errorf(`can't read "cluster-config-v1" ConfigMap: %w`, err)
272-
}
273-
machines, err := readMachineNetworks(cm)
274-
if err != nil {
275-
return nil, err
276-
}
277-
278-
if len(machines) > 0 {
279-
subnets = append(subnets, machines...)
280-
}
281-
282-
return subnets, nil
283-
}
284-
285-
func readMachineNetworks(cm *corev1.ConfigMap) ([]flowslatest.SubnetLabel, error) {
286-
var subnets []flowslatest.SubnetLabel
287-
288-
type ClusterConfig struct {
289-
Networking struct {
290-
MachineNetwork []struct {
291-
CIDR string `yaml:"cidr"`
292-
} `yaml:"machineNetwork"`
293-
} `yaml:"networking"`
294-
}
295-
296-
var rawConfig string
297-
var ok bool
298-
if rawConfig, ok = cm.Data["install-config"]; !ok {
299-
return nil, fmt.Errorf(`can't find key "install-config" in "cluster-config-v1" ConfigMap`)
300-
}
301-
var config ClusterConfig
302-
if err := yaml.Unmarshal([]byte(rawConfig), &config); err != nil {
303-
return nil, fmt.Errorf(`can't deserialize content of "cluster-config-v1" ConfigMap: %w`, err)
304-
}
305-
306-
var cidrs []string
307-
for _, cidr := range config.Networking.MachineNetwork {
308-
cidrs = append(cidrs, cidr.CIDR)
309-
}
310-
if len(cidrs) > 0 {
311-
subnets = append(subnets, flowslatest.SubnetLabel{
312-
Name: "Machines",
313-
CIDRs: cidrs,
314-
})
315-
}
316-
317-
return subnets, nil
318-
}

internal/controller/flp/flp_pipeline_builder_test.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -293,16 +293,10 @@ publish: External`,
293293
},
294294
}
295295

296-
machines, err := readMachineNetworks(&cm)
296+
machines, err := readMachineFromConfig(&cm)
297297
assert.NoError(t, err)
298298

299-
assert.Equal(t,
300-
[]flowslatest.SubnetLabel{
301-
{
302-
Name: "Machines",
303-
CIDRs: []string{"10.0.0.0/16"},
304-
},
305-
}, machines)
299+
assert.Equal(t, []string{"10.0.0.0/16"}, machines)
306300
}
307301

308302
func TestPipelineWithSubnetLabels(t *testing.T) {

internal/controller/networkpolicy/apiserver_endpoint_test.go

Lines changed: 0 additions & 26 deletions
This file was deleted.

internal/controller/networkpolicy/np_controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
flowslatest "github.com/netobserv/network-observability-operator/api/flowcollector/v1beta2"
1313
"github.com/netobserv/network-observability-operator/internal/controller/reconcilers"
14+
"github.com/netobserv/network-observability-operator/internal/pkg/cluster"
1415
"github.com/netobserv/network-observability-operator/internal/pkg/helper"
1516
"github.com/netobserv/network-observability-operator/internal/pkg/manager"
1617
"github.com/netobserv/network-observability-operator/internal/pkg/manager/status"
@@ -80,7 +81,7 @@ func (r *Reconciler) reconcile(ctx context.Context, clh *helper.Client, desired
8081
// Get API server endpoint IPs for network policy
8182
var apiServerIPs []string
8283
if r.mgr.ClusterInfo.IsOpenShift() {
83-
apiServerIPs, err = GetAPIServerEndpointIPs(ctx, r.Client, r.mgr.ClusterInfo)
84+
apiServerIPs, err = cluster.GetAPIServerEndpointIPs(ctx, r.Client, r.mgr.ClusterInfo)
8485
if err != nil {
8586
l.Error(err, "Failed to get API server endpoint IPs")
8687
return fmt.Errorf("cannot determine API server endpoint IPs: %w", err)

0 commit comments

Comments
 (0)