Skip to content

Commit 1927cae

Browse files
Merge pull request #18355 from danwinship/sdn-out-of-process
Automatic merge from submit-queue (batch tested with PRs 18376, 18355). Move pod-namespace calls out of process As discussed in #15991, we need to move all operations in the pod's network namespace out of process, due to a golang issue that allows setns() calls in a locked thread to leak into other threads, causing random lossage as operations intended for the main network namespace end up running in other namespaces instead. (This is fixed in golang 1.10 but we need a fix before then.) Fixes #15991 Fixes #14385 Fixes #13108 Fixes #18317
2 parents 7a6d8cd + dd7a754 commit 1927cae

File tree

11 files changed

+421
-289
lines changed

11 files changed

+421
-289
lines changed

pkg/network/node/cniserver/cniserver.go

+62-15
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"net"
1010
"net/http"
1111
"os"
12-
"path"
12+
"path/filepath"
1313
"strings"
1414

1515
"github.com/golang/glog"
@@ -44,9 +44,21 @@ import (
4444
// removed and re-created with 0700 permissions each time openshift-node is
4545
// started.
4646

47-
// Default CNIServer unix domain socket path which the OpenShift SDN CNI
48-
// plugin uses to talk to the CNIServer
49-
const CNIServerSocketPath string = "/var/run/openshift-sdn/cni-server.sock"
47+
// Default directory for CNIServer runtime files
48+
const CNIServerRunDir string = "/var/run/openshift-sdn"
49+
50+
// CNIServer socket name, and default full path
51+
const CNIServerSocketName string = "cni-server.sock"
52+
const CNIServerSocketPath string = CNIServerRunDir + "/" + CNIServerSocketName
53+
54+
// Config file containing MTU, and default full path
55+
const CNIServerConfigFileName string = "config.json"
56+
const CNIServerConfigFilePath string = CNIServerRunDir + "/" + CNIServerConfigFileName
57+
58+
// Server-to-plugin config data
59+
type Config struct {
60+
MTU uint32 `json:"mtu"`
61+
}
5062

5163
// Explicit type for CNI commands the server handles
5264
type CNICommand string
@@ -76,6 +88,8 @@ type PodRequest struct {
7688
SandboxID string
7789
// kernel network namespace path
7890
Netns string
91+
// for an ADD request, the host side of the created veth
92+
HostVeth string
7993
// Channel for returning the operation result to the CNIServer
8094
Result chan *PodResult
8195
}
@@ -95,19 +109,20 @@ type cniRequestFunc func(request *PodRequest) ([]byte, error)
95109
type CNIServer struct {
96110
http.Server
97111
requestFunc cniRequestFunc
98-
path string
112+
rundir string
113+
config *Config
99114
}
100115

101-
// Create and return a new CNIServer object which will listen on the given
102-
// socket path
103-
func NewCNIServer(socketPath string) *CNIServer {
116+
// Create and return a new CNIServer object which will listen on a socket in the given path
117+
func NewCNIServer(rundir string, config *Config) *CNIServer {
104118
router := mux.NewRouter()
105119

106120
s := &CNIServer{
107121
Server: http.Server{
108122
Handler: router,
109123
},
110-
path: socketPath,
124+
rundir: rundir,
125+
config: config,
111126
}
112127
router.NotFoundHandler = http.HandlerFunc(http.NotFound)
113128
router.HandleFunc("/", s.handleCNIRequest).Methods("POST")
@@ -125,25 +140,36 @@ func (s *CNIServer) Start(requestFunc cniRequestFunc) error {
125140
s.requestFunc = requestFunc
126141

127142
// Remove and re-create the socket directory with root-only permissions
128-
dirName := path.Dir(s.path)
129-
if err := os.RemoveAll(s.path); err != nil && !os.IsNotExist(err) {
143+
if err := os.RemoveAll(s.rundir); err != nil && !os.IsNotExist(err) {
130144
utilruntime.HandleError(fmt.Errorf("failed to remove old pod info socket: %v", err))
131145
}
132-
if err := os.RemoveAll(dirName); err != nil && !os.IsNotExist(err) {
146+
if err := os.RemoveAll(s.rundir); err != nil && !os.IsNotExist(err) {
133147
utilruntime.HandleError(fmt.Errorf("failed to remove contents of socket directory: %v", err))
134148
}
135-
if err := os.MkdirAll(dirName, 0700); err != nil {
149+
if err := os.MkdirAll(s.rundir, 0700); err != nil {
136150
return fmt.Errorf("failed to create pod info socket directory: %v", err)
137151
}
138152

153+
// Write config file
154+
config, err := json.Marshal(s.config)
155+
if err != nil {
156+
return fmt.Errorf("could not marshal config data: %v", err)
157+
}
158+
configPath := filepath.Join(s.rundir, CNIServerConfigFileName)
159+
err = ioutil.WriteFile(configPath, config, os.FileMode(0444))
160+
if err != nil {
161+
return fmt.Errorf("could not write config file %q: %v", configPath, err)
162+
}
163+
139164
// On Linux the socket is created with the permissions of the directory
140165
// it is in, so as long as the directory is root-only we can avoid
141166
// racy umask manipulation.
142-
l, err := net.Listen("unix", s.path)
167+
socketPath := filepath.Join(s.rundir, CNIServerSocketName)
168+
l, err := net.Listen("unix", socketPath)
143169
if err != nil {
144170
return fmt.Errorf("failed to listen on pod info socket: %v", err)
145171
}
146-
if err := os.Chmod(s.path, 0600); err != nil {
172+
if err := os.Chmod(socketPath, 0600); err != nil {
147173
l.Close()
148174
return fmt.Errorf("failed to set pod info socket mode: %v", err)
149175
}
@@ -157,6 +183,22 @@ func (s *CNIServer) Start(requestFunc cniRequestFunc) error {
157183
return nil
158184
}
159185

186+
func ReadConfig(configPath string) (*Config, error) {
187+
bytes, err := ioutil.ReadFile(configPath)
188+
if err != nil {
189+
if os.IsNotExist(err) {
190+
return nil, fmt.Errorf("OpenShift SDN network process is not (yet?) available")
191+
} else {
192+
return nil, fmt.Errorf("could not read config file %q: %v", configPath, err)
193+
}
194+
}
195+
var config Config
196+
if err = json.Unmarshal(bytes, &config); err != nil {
197+
return nil, fmt.Errorf("could not parse config file %q: %v", configPath, err)
198+
}
199+
return &config, nil
200+
}
201+
160202
// Split the "CNI_ARGS" environment variable's value into a map. CNI_ARGS
161203
// contains arbitrary key/value pairs separated by ';' and is for runtime or
162204
// plugin specific uses. Kubernetes passes the pod namespace and name in
@@ -204,6 +246,11 @@ func cniRequestToPodRequest(r *http.Request) (*PodRequest, error) {
204246
return nil, fmt.Errorf("missing CNI_NETNS")
205247
}
206248

249+
req.HostVeth, ok = cr.Env["OSDN_HOSTVETH"]
250+
if !ok && req.Command == CNI_ADD {
251+
return nil, fmt.Errorf("missing OSDN_HOSTVETH")
252+
}
253+
207254
cniArgs, err := gatherCNIArgs(cr.Env)
208255
if err != nil {
209256
return nil, err

pkg/network/node/cniserver/cniserver_test.go

+22-3
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,17 @@ func TestCNIServer(t *testing.T) {
6060
t.Fatalf("failed to create temp directory: %v", err)
6161
}
6262
defer os.RemoveAll(tmpDir)
63+
socketPath := filepath.Join(tmpDir, CNIServerSocketName)
6364

64-
path := filepath.Join(tmpDir, "cni-server.sock")
65-
s := NewCNIServer(path)
65+
s := NewCNIServer(tmpDir, &Config{MTU: 1500})
6666
if err := s.Start(serverHandleCNI); err != nil {
6767
t.Fatalf("error starting CNI server: %v", err)
6868
}
6969

7070
client := &http.Client{
7171
Transport: &http.Transport{
7272
Dial: func(proto, addr string) (net.Conn, error) {
73-
return net.Dial("unix", path)
73+
return net.Dial("unix", socketPath)
7474
},
7575
},
7676
}
@@ -102,6 +102,7 @@ func TestCNIServer(t *testing.T) {
102102
"CNI_CONTAINERID": "adsfadsfasfdasdfasf",
103103
"CNI_NETNS": "/path/to/something",
104104
"CNI_ARGS": "K8S_POD_NAMESPACE=awesome-namespace;K8S_POD_NAME=awesome-name",
105+
"OSDN_HOSTVETH": "vethABC",
105106
},
106107
Config: []byte("{\"cniVersion\": \"0.1.0\",\"name\": \"openshift-sdn\",\"type\": \"openshift-sdn\"}"),
107108
},
@@ -143,6 +144,7 @@ func TestCNIServer(t *testing.T) {
143144
"CNI_COMMAND": string(CNI_ADD),
144145
"CNI_CONTAINERID": "adsfadsfasfdasdfasf",
145146
"CNI_NETNS": "/path/to/something",
147+
"OSDN_HOSTVETH": "vethABC",
146148
},
147149
Config: []byte("{\"cniVersion\": \"0.1.0\",\"name\": \"openshift-sdn\",\"type\": \"openshift-sdn\"}"),
148150
},
@@ -157,6 +159,7 @@ func TestCNIServer(t *testing.T) {
157159
"CNI_COMMAND": string(CNI_ADD),
158160
"CNI_CONTAINERID": "adsfadsfasfdasdfasf",
159161
"CNI_ARGS": "K8S_POD_NAMESPACE=awesome-namespace;K8S_POD_NAME=awesome-name",
162+
"OSDN_HOSTVETH": "vethABC",
160163
},
161164
Config: []byte("{\"cniVersion\": \"0.1.0\",\"name\": \"openshift-sdn\",\"type\": \"openshift-sdn\"}"),
162165
},
@@ -171,12 +174,28 @@ func TestCNIServer(t *testing.T) {
171174
"CNI_CONTAINERID": "adsfadsfasfdasdfasf",
172175
"CNI_NETNS": "/path/to/something",
173176
"CNI_ARGS": "K8S_POD_NAMESPACE=awesome-namespace;K8S_POD_NAME=awesome-name",
177+
"OSDN_HOSTVETH": "vethABC",
174178
},
175179
Config: []byte("{\"cniVersion\": \"0.1.0\",\"name\": \"openshift-sdn\",\"type\": \"openshift-sdn\"}"),
176180
},
177181
result: nil,
178182
errorPrefix: "unexpected or missing CNI_COMMAND",
179183
},
184+
// Missing OSDN_HOSTVETH
185+
{
186+
name: "ARGS4",
187+
request: &CNIRequest{
188+
Env: map[string]string{
189+
"CNI_COMMAND": string(CNI_ADD),
190+
"CNI_CONTAINERID": "adsfadsfasfdasdfasf",
191+
"CNI_NETNS": "/path/to/something",
192+
"CNI_ARGS": "K8S_POD_NAMESPACE=awesome-namespace;K8S_POD_NAME=awesome-name",
193+
},
194+
Config: []byte("{\"cniVersion\": \"0.1.0\",\"name\": \"openshift-sdn\",\"type\": \"openshift-sdn\"}"),
195+
},
196+
result: nil,
197+
errorPrefix: "missing OSDN_HOSTVETH",
198+
},
180199
}
181200

182201
for _, tc := range testcases {

pkg/network/node/node.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ func (node *OsdnNode) Start() error {
347347
}
348348

349349
glog.V(2).Infof("Starting openshift-sdn pod manager")
350-
if err := node.podManager.Start(cniserver.CNIServerSocketPath, node.localSubnetCIDR, node.networkInfo.ClusterNetworks); err != nil {
350+
if err := node.podManager.Start(cniserver.CNIServerRunDir, node.localSubnetCIDR, node.networkInfo.ClusterNetworks); err != nil {
351351
return err
352352
}
353353

0 commit comments

Comments
 (0)