1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package utils
16
17 import (
18 "encoding/json"
19 "fmt"
20 "regexp"
21 "strings"
22
23 cnitypes "github.com/containernetworking/cni/pkg/types"
24 "github.com/containernetworking/cni/pkg/types/current"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26
27 v1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
28
29 corev1 "k8s.io/api/core/v1"
30 "k8s.io/client-go/kubernetes"
31 "k8s.io/client-go/util/retry"
32 )
33
34
35 func convertDNS(dns cnitypes.DNS) *v1.DNS {
36 var v1dns v1.DNS
37
38 v1dns.Nameservers = append([]string{}, dns.Nameservers...)
39 v1dns.Domain = dns.Domain
40 v1dns.Search = append([]string{}, dns.Search...)
41 v1dns.Options = append([]string{}, dns.Options...)
42
43 return &v1dns
44 }
45
46
47 func SetNetworkStatus(client kubernetes.Interface, pod *corev1.Pod, statuses []v1.NetworkStatus) error {
48 if client == nil {
49 return fmt.Errorf("no client set")
50 }
51
52 if pod == nil {
53 return fmt.Errorf("no pod set")
54 }
55
56 var networkStatus []string
57 if statuses != nil {
58 for _, status := range statuses {
59 data, err := json.MarshalIndent(status, "", " ")
60 if err != nil {
61 return fmt.Errorf("SetNetworkStatus: error with Marshal Indent: %v", err)
62 }
63 networkStatus = append(networkStatus, string(data))
64 }
65 }
66
67 _, err := setPodNetworkStatus(client, pod, fmt.Sprintf("[%s]", strings.Join(networkStatus, ",")))
68 if err != nil {
69 return fmt.Errorf("SetNetworkStatus: failed to update the pod %s in out of cluster comm: %v", pod.Name, err)
70 }
71 return nil
72 }
73
74 func setPodNetworkStatus(client kubernetes.Interface, pod *corev1.Pod, networkstatus string) (*corev1.Pod, error) {
75 if len(pod.Annotations) == 0 {
76 pod.Annotations = make(map[string]string)
77 }
78
79 coreClient := client.CoreV1()
80
81 pod.Annotations[v1.NetworkStatusAnnot] = networkstatus
82 pod = pod.DeepCopy()
83 var err error
84
85 if resultErr := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
86 if err != nil {
87
88 pod, err = coreClient.Pods(pod.Namespace).Get(pod.Name, metav1.GetOptions{})
89 if err != nil {
90 return err
91 }
92 }
93
94 pod, err = coreClient.Pods(pod.Namespace).UpdateStatus(pod)
95 return err
96 }); resultErr != nil {
97 return nil, fmt.Errorf("status update failed for pod %s/%s: %v", pod.Namespace, pod.Name, resultErr)
98 }
99 return pod, nil
100 }
101
102
103 func GetNetworkStatus(pod *corev1.Pod) ([]v1.NetworkStatus, error) {
104 if pod == nil {
105 return nil, fmt.Errorf("cannot find pod")
106 }
107 if pod.Annotations == nil {
108 return nil, fmt.Errorf("cannot find pod annotation")
109 }
110
111 netStatusesJson, ok := pod.Annotations[v1.NetworkStatusAnnot]
112 if !ok {
113 return nil, fmt.Errorf("cannot find network status")
114 }
115
116 var netStatuses []v1.NetworkStatus
117 err := json.Unmarshal([]byte(netStatusesJson), &netStatuses)
118
119 return netStatuses, err
120 }
121
122
123 func CreateNetworkStatus(r cnitypes.Result, networkName string, defaultNetwork bool) (*v1.NetworkStatus, error) {
124 netStatus := &v1.NetworkStatus{}
125 netStatus.Name = networkName
126 netStatus.Default = defaultNetwork
127
128
129 result, err := current.NewResultFromResult(r)
130 if err != nil {
131 return netStatus, fmt.Errorf("error convert the type.Result to current.Result: %v", err)
132 }
133
134 for _, ifs := range result.Interfaces {
135
136 if ifs.Sandbox != "" {
137 netStatus.Interface = ifs.Name
138 netStatus.Mac = ifs.Mac
139 }
140 }
141
142 for _, ipconfig := range result.IPs {
143 if ipconfig.Version == "4" && ipconfig.Address.IP.To4() != nil {
144 netStatus.IPs = append(netStatus.IPs, ipconfig.Address.IP.String())
145 }
146
147 if ipconfig.Version == "6" && ipconfig.Address.IP.To16() != nil {
148 netStatus.IPs = append(netStatus.IPs, ipconfig.Address.IP.String())
149 }
150 }
151
152 v1dns := convertDNS(result.DNS)
153 netStatus.DNS = *v1dns
154
155 return netStatus, nil
156 }
157
158
159 func ParsePodNetworkAnnotation(pod *corev1.Pod) ([]*v1.NetworkSelectionElement, error) {
160 netAnnot := pod.Annotations[v1.NetworkAttachmentAnnot]
161 defaultNamespace := pod.Namespace
162
163 if len(netAnnot) == 0 {
164 return nil, &v1.NoK8sNetworkError{Message: "no kubernetes network found"}
165 }
166
167 networks, err := ParseNetworkAnnotation(netAnnot, defaultNamespace)
168 if err != nil {
169 return nil, err
170 }
171 return networks, nil
172 }
173
174
175 func ParseNetworkAnnotation(podNetworks, defaultNamespace string) ([]*v1.NetworkSelectionElement, error) {
176 var networks []*v1.NetworkSelectionElement
177
178 if podNetworks == "" {
179 return nil, fmt.Errorf("parsePodNetworkAnnotation: pod annotation not having \"network\" as key")
180 }
181
182 if strings.IndexAny(podNetworks, "[{\"") >= 0 {
183 if err := json.Unmarshal([]byte(podNetworks), &networks); err != nil {
184 return nil, fmt.Errorf("parsePodNetworkAnnotation: failed to parse pod Network Attachment Selection Annotation JSON format: %v", err)
185 }
186 } else {
187
188 for _, item := range strings.Split(podNetworks, ",") {
189
190 item = strings.TrimSpace(item)
191
192
193 netNsName, networkName, netIfName, err := parsePodNetworkObjectText(item)
194 if err != nil {
195 return nil, fmt.Errorf("parsePodNetworkAnnotation: %v", err)
196 }
197
198 networks = append(networks, &v1.NetworkSelectionElement{
199 Name: networkName,
200 Namespace: netNsName,
201 InterfaceRequest: netIfName,
202 })
203 }
204 }
205
206 for _, net := range networks {
207 if net.Namespace == "" {
208 net.Namespace = defaultNamespace
209 }
210 }
211
212 return networks, nil
213 }
214
215
216
217 func parsePodNetworkObjectText(podnetwork string) (string, string, string, error) {
218 var netNsName string
219 var netIfName string
220 var networkName string
221
222 slashItems := strings.Split(podnetwork, "/")
223 if len(slashItems) == 2 {
224 netNsName = strings.TrimSpace(slashItems[0])
225 networkName = slashItems[1]
226 } else if len(slashItems) == 1 {
227 networkName = slashItems[0]
228 } else {
229 return "", "", "", fmt.Errorf("Invalid network object (failed at '/')")
230 }
231
232 atItems := strings.Split(networkName, "@")
233 networkName = strings.TrimSpace(atItems[0])
234 if len(atItems) == 2 {
235 netIfName = strings.TrimSpace(atItems[1])
236 } else if len(atItems) != 1 {
237 return "", "", "", fmt.Errorf("Invalid network object (failed at '@')")
238 }
239
240
241
242
243
244
245 allItems := []string{netNsName, networkName, netIfName}
246 for i := range allItems {
247 matched, _ := regexp.MatchString("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$", allItems[i])
248 if !matched && len([]rune(allItems[i])) > 0 {
249 return "", "", "", fmt.Errorf(fmt.Sprintf("Failed to parse: one or more items did not match comma-delimited format (must consist of lower case alphanumeric characters). Must start and end with an alphanumeric character), mismatch @ '%v'", allItems[i]))
250 }
251 }
252
253 return netNsName, networkName, netIfName, nil
254 }
255
View as plain text