-
Notifications
You must be signed in to change notification settings - Fork 0
/
pod.go
159 lines (142 loc) · 4.18 KB
/
pod.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package pod
import (
"encoding/json"
"fmt"
"os"
"path"
"time"
"github.com/andreaskaris/veth-ethtool/pkg/helpers"
"github.com/vishvananda/netlink"
"k8s.io/apimachinery/pkg/util/errors"
"k8s.io/klog"
)
// Metadata holds a pod's metadata.
type Metadata struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
}
// Pod is a pod object.
type Pod struct {
ID string `json:"id"`
Metadata Metadata `json:"metadata"`
}
// PodList is a list of Pods.
type PodList struct {
Items []Pod `json:"items"`
}
type Namespace struct {
Type string `json:"type"`
Path string `json:"path"`
}
type PodInspect struct {
Info struct {
RuntimeSpec struct {
Linux struct {
Namespaces []Namespace `json:"namespaces"`
} `json:"linux"`
} `json:"runtimeSpec"`
} `json:"info"`
}
// GetNetns returns the netns (only the name, without the full path) of this pod.
func (p PodInspect) GetNetns() string {
for _, ns := range p.Info.RuntimeSpec.Linux.Namespaces {
if ns.Type == helpers.TypeNetwork {
return path.Base(ns.Path)
}
}
return ""
}
// ListNetnsIDs returns the NetNS IDs for each namespace listed inside dir.
func ListNetnsIDs(dir string) (map[string]int, error) {
return listNetnsIDs(dir)
}
// GetOwnerOfLink returns the Pod for the provided link.
func GetOwnerOfLink(link netlink.Link) (*Pod, error) {
// Return if this is not a veth.
if link.Type() != helpers.TypeVeth {
return nil, fmt.Errorf("provided link is not a veth")
}
// List all namespaces with their IDs.
netnsIDs, err := ListNetnsIDs(helpers.GetNetNSLocation())
if err != nil {
return nil, fmt.Errorf("failed to list all netns IDs, netnsIds: %+v, err: %q", netnsIDs, err)
}
// Then, find the name of the namespace with the ID that's shown in
// link.Attrs().NetNsID.
linkName := link.Attrs().Name
var linkNetnsName string
linkNetnsID := link.Attrs().NetNsID
for name, id := range netnsIDs {
if id == linkNetnsID {
linkNetnsName = name
break
}
}
if linkNetnsName == "" {
return nil, fmt.Errorf("could not find namespace name for link; link name: %s, link NetnsID: %d, netnsIDs: %+v",
linkName, linkNetnsID, netnsIDs)
}
klog.V(2).Infof("Found netns %q for link %q", linkNetnsName, linkName)
// TODO: race condition, link is there before pod is listed -_-
time.Sleep(2 * time.Second)
pod, err := findPodForNetns(linkNetnsName)
if err != nil {
return nil, err
}
return pod, nil
}
// findPodForNetns lists all pods with `crictl pods`, then inspects all pods with `crictl inspectp`. It looks for the
// pod with netns within the list of .info.runtimeSpec.linux.namespaces.
func findPodForNetns(netns string) (*Pod, error) {
var podList PodList
out, err := crictl("pods", "-o", "json")
if err != nil {
return nil, fmt.Errorf("listing pods failed, out: %q, err: %q", out, err)
}
if err := json.Unmarshal(out, &podList); err != nil {
return nil, err
}
for _, pod := range podList.Items {
pod := pod
out, err := crictl("inspectp", "-o", "json", pod.ID)
if err != nil {
return nil, err
}
var podInspect PodInspect
if err := json.Unmarshal(out, &podInspect); err != nil {
return nil, err
}
if podInspect.GetNetns() == netns {
return &pod, nil
}
}
return nil, fmt.Errorf("could not find a pod for netns %q", netns)
}
var crictl = func(parameters ...string) ([]byte, error) {
return helpers.RunCommand("crictl", parameters...)
}
// listNetnsIDs holds the implementation for ListNetnsIDs. It can easily be swapped our for a fake implementation.
var listNetnsIDs = func(dir string) (map[string]int, error) {
nsIDMap := make(map[string]int)
entries, err := os.ReadDir(dir)
if err != nil {
return nil, fmt.Errorf("could not open directory %q err: %q", dir, err)
}
var errs []error
for _, e := range entries {
p := path.Join(dir, e.Name())
f, err := os.Open(p)
if err != nil {
errs = append(errs, fmt.Errorf("could not open file %q for reading, err: %q", p, err))
continue
}
id, err := netlink.GetNetNsIdByFd(int(f.Fd()))
if err != nil {
errs = append(errs, fmt.Errorf("issue running netlink.GetNetNsIdByFd, file: %q, err: %q", p, err))
continue
}
baseName := path.Base(f.Name())
nsIDMap[baseName] = id
}
return nsIDMap, errors.NewAggregate(errs)
}