forked from rancher/agent
-
Notifications
You must be signed in to change notification settings - Fork 0
/
response_unix.go
executable file
·136 lines (122 loc) · 3.43 KB
/
response_unix.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
//+build !windows
package utils
import (
"encoding/json"
"io/ioutil"
"os"
"path"
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types"
"github.com/patrickmn/go-cache"
"github.com/pkg/errors"
"github.com/rancher/agent/utilities/constants"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
)
const (
cniLabels = "io.rancher.cni.network"
linkName = "eth0"
cniStateBaseDir = "/var/lib/rancher/state/cni"
)
func getIP(inspect types.ContainerJSON, c *cache.Cache) (string, error) {
if inspect.Config.Labels[cniLabels] != "" && c != nil {
cacheIP, ok := c.Get(inspect.Config.Labels[constants.UUIDLabel])
if ok && InterfaceToString(cacheIP) == "error" {
c.Delete(inspect.Config.Labels[constants.UUIDLabel])
return "", errors.New("Timeout getting IP address")
} else if ok {
c.Delete(inspect.Config.Labels[constants.UUIDLabel])
return InterfaceToString(cacheIP), nil
}
ip, err := lookUpIP(inspect)
if err != nil {
c.Add(inspect.Config.Labels[constants.UUIDLabel], "error", cache.DefaultExpiration)
return "", err
}
c.Add(inspect.Config.Labels[constants.UUIDLabel], ip, cache.DefaultExpiration)
return ip, nil
}
return inspect.NetworkSettings.IPAddress, nil
}
func lookUpIP(inspect types.ContainerJSON) (string, error) {
// if container is stopped just return empty ip
if inspect.State.Pid == 0 {
return "", nil
}
endTime := time.Now().Add(30 * time.Second)
initTime := 250 * time.Millisecond
maxTime := 2 * time.Second
for {
if ip, cniError := getIPFromStateFile(inspect); ip != "" || cniError != "" {
var err error
if cniError != "" {
err = errors.New(cniError)
}
return ip, err
}
ip, err := getIPForPID(inspect.State.Pid)
if err != nil || ip != "" {
return ip, err
}
logrus.Debugf("Sleeping %v (%v remaining) waiting for IP on %s", initTime, endTime.Sub(time.Now()), inspect.ID)
time.Sleep(initTime)
initTime = initTime * 2
if initTime.Seconds() > maxTime.Seconds() {
initTime = maxTime
}
if time.Now().After(endTime) {
return "", errors.New("Timeout getting IP address")
}
}
}
func getIPFromStateFile(inspect types.ContainerJSON) (string, string) {
if inspect.ID == "" || inspect.State == nil || inspect.State.StartedAt == "" {
return "", ""
}
filename := path.Join(cniStateBaseDir, inspect.ID, inspect.State.StartedAt)
data, err := ioutil.ReadFile(filename)
if err != nil {
if !os.IsNotExist(err) {
logrus.Warnf("Error reading cni state file %v: %v. Falling back to container inspection logic.", filename, err)
}
return "", ""
}
var state cniState
if err := json.Unmarshal(data, &state); err != nil {
logrus.Warnf("Error unmarshalling cni state data %s: %v. Falling back to container inspection logic.", data, err)
return "", ""
}
return state.IP4.IP, state.Error
}
type cniState struct {
Error string
IP4 struct {
IP string
}
}
func getIPForPID(pid int) (string, error) {
nsHandler, err := netns.GetFromPid(pid)
if err != nil {
return "", err
}
defer nsHandler.Close()
handler, err := netlink.NewHandleAt(nsHandler)
if err != nil {
return "", err
}
defer handler.Delete()
link, err := handler.LinkByName(linkName)
if err != nil {
// Don't return error, it's expected this may fail until iface is created
return "", nil
}
addrs, err := handler.AddrList(link, netlink.FAMILY_V4)
if err != nil {
return "", err
}
if len(addrs) > 0 {
return addrs[0].IP.String(), nil
}
return "", nil
}