/
pod_cidr.go
144 lines (131 loc) · 4.44 KB
/
pod_cidr.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package utils
import (
"encoding/json"
"fmt"
"net"
"os"
"strings"
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
"k8s.io/client-go/kubernetes"
)
const (
podCIDRAnnotation = "kube-router.io/pod-cidr"
)
// GetPodCidrFromCniSpec gets pod CIDR allocated to the node from CNI spec file and returns it
func GetPodCidrFromCniSpec(cniConfFilePath string) (net.IPNet, error) {
var podCidr = net.IPNet{}
var err error
var ipamConfig *allocator.IPAMConfig
if strings.HasSuffix(cniConfFilePath, ".conflist") {
var confList *libcni.NetworkConfigList
confList, err = libcni.ConfListFromFile(cniConfFilePath)
if err != nil {
return net.IPNet{}, fmt.Errorf("failed to load CNI config list file: %s", err.Error())
}
for _, conf := range confList.Plugins {
if conf.Network.IPAM.Type != "" {
ipamConfig, _, err = allocator.LoadIPAMConfig(conf.Bytes, "")
if err != nil {
if err.Error() != "no IP ranges specified" {
return net.IPNet{}, fmt.Errorf("failed to get IPAM details from the CNI conf file: %s", err.Error())
}
}
break
}
}
} else {
netconfig, err := libcni.ConfFromFile(cniConfFilePath)
if err != nil {
return net.IPNet{}, fmt.Errorf("failed to load CNI conf file: %s", err.Error())
}
ipamConfig, _, err = allocator.LoadIPAMConfig(netconfig.Bytes, "")
if err != nil {
// TODO: Handle this error properly in controllers, if no subnet is specified
if err.Error() != "no IP ranges specified" {
return net.IPNet{}, fmt.Errorf("failed to get IPAM details from the CNI conf file: %s", err.Error())
}
return net.IPNet{}, nil
}
}
// TODO: Support multiple subnet definitions in CNI conf
if ipamConfig != nil && len(ipamConfig.Ranges) > 0 {
for _, rangeset := range ipamConfig.Ranges {
for _, item := range rangeset {
if item.Subnet.IP != nil {
podCidr = net.IPNet(item.Subnet)
break
}
}
}
}
return podCidr, nil
}
// InsertPodCidrInCniSpec inserts the pod CIDR allocated to the node by kubernetes controller manager
// and stored it in the CNI specification
func InsertPodCidrInCniSpec(cniConfFilePath string, cidr string) error {
file, err := os.ReadFile(cniConfFilePath)
if err != nil {
return fmt.Errorf("failed to load CNI conf file: %s", err.Error())
}
var config interface{}
if strings.HasSuffix(cniConfFilePath, ".conflist") {
err = json.Unmarshal(file, &config)
if err != nil {
return fmt.Errorf("failed to parse JSON from CNI conf file: %s", err.Error())
}
updatedCidr := false
configMap := config.(map[string]interface{})
for key := range configMap {
if key != "plugins" {
continue
}
// .conflist file has array of plug-in config. Find the one with ipam key
// and insert the CIDR for the node
pluginConfigs := configMap["plugins"].([]interface{})
for _, pluginConfig := range pluginConfigs {
pluginConfigMap := pluginConfig.(map[string]interface{})
if val, ok := pluginConfigMap["ipam"]; ok {
valObj := val.(map[string]interface{})
valObj["subnet"] = cidr
updatedCidr = true
break
}
}
}
if !updatedCidr {
return fmt.Errorf("failed to insert subnet cidr into CNI conf file: %s as CNI file is invalid", cniConfFilePath)
}
} else {
err = json.Unmarshal(file, &config)
if err != nil {
return fmt.Errorf("failed to parse JSON from CNI conf file: %s", err.Error())
}
pluginConfig := config.(map[string]interface{})
pluginConfig["ipam"].(map[string]interface{})["subnet"] = cidr
}
configJSON, _ := json.Marshal(config)
err = os.WriteFile(cniConfFilePath, configJSON, 0644)
if err != nil {
return fmt.Errorf("failed to insert subnet cidr into CNI conf file: %s", err.Error())
}
return nil
}
// GetPodCidrFromNodeSpec reads the pod CIDR allocated to the node from API node object and returns it
func GetPodCidrFromNodeSpec(clientset kubernetes.Interface, hostnameOverride string) (string, error) {
node, err := GetNodeObject(clientset, hostnameOverride)
if err != nil {
return "", fmt.Errorf("Failed to get pod CIDR allocated for the node due to: " + err.Error())
}
if cidr, ok := node.Annotations[podCIDRAnnotation]; ok {
_, _, err = net.ParseCIDR(cidr)
if err != nil {
return "", fmt.Errorf("error parsing pod CIDR in node annotation: %v", err)
}
return cidr, nil
}
if node.Spec.PodCIDR == "" {
return "", fmt.Errorf("node.Spec.PodCIDR not set for node: %v", node.Name)
}
return node.Spec.PodCIDR, nil
}