Skip to content

Commit 49aa5ab

Browse files
committed
Add integration test and update go version
* Use real CNI plugins to test Setup/Remove API. * Update go version from 1.16.x to 1.17.x in CI. * Remove coverage Signed-off-by: Wei Fu <fuweid89@gmail.com>
1 parent e559bd8 commit 49aa5ab

File tree

7 files changed

+600
-5
lines changed

7 files changed

+600
-5
lines changed

.github/workflows/ci.yml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
steps:
1717
- uses: actions/setup-go@v2
1818
with:
19-
go-version: 1.16.x
19+
go-version: 1.17.x
2020

2121
- name: Set env
2222
shell: bash
@@ -33,14 +33,18 @@ jobs:
3333
with:
3434
working-directory: src/github.com/containerd/go-cni
3535

36+
- uses: containerd/project-checks@v1
37+
with:
38+
working-directory: src/github.com/containerd/go-cni/integration
39+
3640
linters:
3741
name: Linters
3842
runs-on: ${{ matrix.os }}
3943
timeout-minutes: 10
4044

4145
strategy:
4246
matrix:
43-
go-version: [1.16.x]
47+
go-version: [1.17.x]
4448
os: [ubuntu-18.04]
4549

4650
steps:
@@ -71,7 +75,7 @@ jobs:
7175

7276
- uses: actions/setup-go@v2
7377
with:
74-
go-version: 1.16.x
78+
go-version: 1.17.x
7579

7680
- name: Set env
7781
shell: bash
@@ -80,6 +84,9 @@ jobs:
8084
echo "${{ github.workspace }}/bin" >> $GITHUB_PATH
8185
8286
- run: |
83-
go test -v -race -covermode=atomic -coverprofile=coverage.txt ./...
84-
bash <(curl -s https://codecov.io/bash)
87+
bash -x script/install-cni
88+
make clean V=1
89+
make bin/integration.test V=1
90+
make test V=1
91+
sudo make integration V=1
8592
working-directory: src/github.com/containerd/go-cni

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/bin/
2+
coverage.txt
3+
profile.out

Makefile

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright The containerd Authors.
2+
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
TESTFLAGS_PARALLEL ?= 8
16+
17+
EXTRA_TESTFLAGS ?=
18+
19+
# quiet or not
20+
ifeq ($(V),1)
21+
Q =
22+
else
23+
Q = @
24+
endif
25+
26+
.PHONY: test integration clean help
27+
28+
help: ## this help
29+
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort
30+
31+
test: ## run tests, except integration tests and tests that require root
32+
$(Q)go test -v -race $(EXTRA_TESTFLAGS) -count=1 ./...
33+
34+
integration: ## run integration test
35+
$(Q)bin/integration.test -test.v -test.count=1 -test.root $(EXTRA_TESTFLAGS) -test.parallel $(TESTFLAGS_PARALLEL)
36+
37+
FORCE:
38+
39+
bin/integration.test: FORCE ## build integration test binary into bin
40+
$(Q)cd ./integration && go test -race -c . -o ../bin/integration.test
41+
42+
clean: ## clean up binaries
43+
$(Q)rm -rf bin/
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Copyright 2018 CNI authors
18+
//
19+
// Licensed under the Apache License, Version 2.0 (the "License");
20+
// you may not use this file except in compliance with the License.
21+
// You may obtain a copy of the License at
22+
//
23+
// http://www.apache.org/licenses/LICENSE-2.0
24+
//
25+
// Unless required by applicable law or agreed to in writing, software
26+
//
27+
// distributed under the License is distributed on an "AS IS" BASIS,
28+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29+
// See the License for the specific language governing permissions and
30+
// limitations under the License.
31+
32+
package integration
33+
34+
import (
35+
"context"
36+
"crypto/rand"
37+
"fmt"
38+
"io/ioutil"
39+
"os"
40+
"path"
41+
"runtime"
42+
"sync"
43+
"syscall"
44+
"testing"
45+
46+
"github.com/containerd/continuity/fs"
47+
"github.com/containerd/continuity/testutil"
48+
"github.com/containerd/go-cni"
49+
"github.com/stretchr/testify/assert"
50+
)
51+
52+
var (
53+
baseNetNSDir = "/var/run/netns/"
54+
55+
defaultCNIPluginDir = "/opt/cni/bin/"
56+
57+
cniBridgePluginCfg = `
58+
{
59+
"cniVersion": "1.0.0",
60+
"name": "gocni-test",
61+
"plugins": [
62+
{
63+
"type":"bridge",
64+
"bridge":"gocni-test0",
65+
"isGateway":true,
66+
"ipMasq":true,
67+
"promiscMode":true,
68+
"ipam":{
69+
"type":"host-local",
70+
"ranges":[
71+
[{
72+
"subnet":"10.88.0.0/16"
73+
}],
74+
[{
75+
"subnet":"2001:4860:4860::/64"
76+
}]
77+
],
78+
"routes":[
79+
{"dst":"0.0.0.0/0"},
80+
{"dst":"::/0"}
81+
]
82+
}
83+
},
84+
{
85+
"type":"portmap",
86+
"capabilities":{
87+
"portMappings":true
88+
}
89+
}
90+
]
91+
}
92+
`
93+
)
94+
95+
// TestBasicSetupAndRemove tests the cni.Setup/Remove with real bridge and
96+
// loopback CNI plugins.
97+
//
98+
// NOTE:
99+
//
100+
// 1. It required that the both bridge and loopback CNI plugins are installed
101+
// in /opt/cni/bin.
102+
//
103+
// 2. Since #76 enables parallel mode, we should enable -race option for this.
104+
func TestBasicSetupAndRemove(t *testing.T) {
105+
testutil.RequiresRoot(t)
106+
107+
// setup config dir
108+
tmpPluginConfDir, err := os.MkdirTemp("", t.Name()+"-conf")
109+
assert.NoError(t, err, "create temp dir for plugin conf dir")
110+
defer os.RemoveAll(tmpPluginConfDir)
111+
112+
assert.NoError(t,
113+
ioutil.WriteFile(
114+
path.Join(tmpPluginConfDir, "10-gocni-test-net.conflist"),
115+
[]byte(cniBridgePluginCfg),
116+
0600,
117+
),
118+
"init cni config",
119+
)
120+
121+
// copy plugins from /opt/cni/bin
122+
tmpPluginDir, err := os.MkdirTemp("", t.Name()+"-bin")
123+
assert.NoError(t, err, "create temp dir for plugin bin dir")
124+
defer os.RemoveAll(tmpPluginDir)
125+
126+
assert.NoError(t,
127+
fs.CopyDir(tmpPluginDir, defaultCNIPluginDir),
128+
"copy %v into %v", defaultCNIPluginDir, tmpPluginDir)
129+
130+
nsPath, done, err := createNetNS()
131+
assert.NoError(t, err, "create temp netns")
132+
defer func() {
133+
assert.NoError(t, done(), "cleanup temp netns")
134+
}()
135+
136+
defaultIfName := "eth0"
137+
ctx := context.Background()
138+
id := t.Name()
139+
140+
for idx, opts := range [][]cni.Opt{
141+
// Use default plugin dir
142+
{
143+
cni.WithMinNetworkCount(2),
144+
cni.WithPluginConfDir(tmpPluginConfDir),
145+
},
146+
// Use customize plugin dir
147+
{
148+
cni.WithMinNetworkCount(2),
149+
cni.WithPluginConfDir(tmpPluginConfDir),
150+
cni.WithPluginDir([]string{
151+
tmpPluginDir,
152+
}),
153+
},
154+
} {
155+
l, err := cni.New(opts...)
156+
assert.NoError(t, err, "[%v] initialize cni library", idx)
157+
158+
assert.NoError(t,
159+
l.Load(cni.WithLoNetwork, cni.WithDefaultConf),
160+
"[%v] load cni configuration", idx,
161+
)
162+
163+
// Setup network
164+
result, err := l.Setup(ctx, id, nsPath)
165+
assert.NoError(t, err, "[%v] setup network for namespace %v", idx, nsPath)
166+
167+
ip := result.Interfaces[defaultIfName].IPConfigs[0].IP.String()
168+
t.Logf("[%v] ip is %v", idx, ip)
169+
170+
assert.NoError(t,
171+
l.Remove(ctx, id, nsPath),
172+
"[%v] teardown network for namespace %v", idx, nsPath,
173+
)
174+
}
175+
}
176+
177+
// createNetNS returns temp netns path.
178+
//
179+
// NOTE: It is based on https://github.com/containernetworking/plugins/blob/v1.0.1/pkg/testutils/netns_linux.go.
180+
// That can prevent from introducing unnessary dependencies in go.mod.
181+
func createNetNS() (_ string, _ func() error, retErr error) {
182+
b := make([]byte, 16)
183+
if _, err := rand.Reader.Read(b); err != nil {
184+
return "", nil, fmt.Errorf("failed to generate random netns name: %w", err)
185+
}
186+
187+
// Create the directory for mounting network namespaces
188+
// This needs to be a shared mountpoint in case it is mounted in to
189+
// other namespaces (containers)
190+
if err := os.MkdirAll(baseNetNSDir, 0755); err != nil {
191+
return "", nil, fmt.Errorf("failed to init base netns dir %s: %v", baseNetNSDir, err)
192+
}
193+
194+
// create an empty file at the mount point
195+
nsName := fmt.Sprintf("gocni-test-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
196+
nsPath := path.Join(baseNetNSDir, nsName)
197+
mountPointFd, err := os.Create(nsPath)
198+
if err != nil {
199+
return "", nil, fmt.Errorf("failed to create temp nspath %s: %v", nsPath, err)
200+
}
201+
mountPointFd.Close()
202+
203+
defer func() {
204+
if retErr != nil {
205+
_ = os.RemoveAll(nsPath)
206+
}
207+
}()
208+
209+
var wg sync.WaitGroup
210+
wg.Add(1)
211+
212+
// do namespace work in a dedicated goroutine, so that we can safely
213+
// Lock/Unlock OSThread without upsetting the lock/unlock state of
214+
// the caller of this function
215+
go (func() {
216+
defer wg.Done()
217+
218+
// Don't unlock. By not unlocking, golang will kill the OS thread
219+
// when the goroutine is done (>= go1.10). Since <= go1.10 has
220+
// been deprecated, we don't need to get current net ns and
221+
// reset.
222+
runtime.LockOSThread()
223+
224+
// create a new netns on the current thread
225+
if err = syscall.Unshare(syscall.CLONE_NEWNET); err != nil {
226+
return
227+
}
228+
229+
// bind mount the netns from the current thread (from /proc) onto the
230+
// mount point. This causes the namespace to persist, even when there
231+
// are no threads in the ns.
232+
err = syscall.Mount(getCurrentThreadNetNSPath(), nsPath, "none", syscall.MS_BIND, "")
233+
if err != nil {
234+
err = fmt.Errorf("failed to bind mount ns at %s: %w", nsPath, err)
235+
}
236+
})()
237+
wg.Wait()
238+
239+
if err != nil {
240+
return "", nil, fmt.Errorf("failed to create net namespace: %w", err)
241+
}
242+
243+
return nsPath, func() error {
244+
if err := syscall.Unmount(nsPath, 0); err != nil {
245+
return fmt.Errorf("failed to unmount netns: at %s: %v", nsPath, err)
246+
}
247+
248+
if err := os.Remove(nsPath); err != nil {
249+
return fmt.Errorf("failed to remove nspath %s: %v", nsPath, err)
250+
}
251+
return nil
252+
}, nil
253+
}
254+
255+
// getCurrentThreadNetNSPath copied from pkg/ns
256+
//
257+
// NOTE: It is from https://github.com/containernetworking/plugins/blob/v1.0.1/pkg/testutils/netns_linux.go.
258+
func getCurrentThreadNetNSPath() string {
259+
// /proc/self/ns/net returns the namespace of the main thread, not
260+
// of whatever thread this goroutine is running on. Make sure we
261+
// use the thread's net namespace since the thread is switching around
262+
return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), syscall.Gettid())
263+
}

integration/go.mod

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module github.com/containerd/go-cni/integration
2+
3+
go 1.17
4+
5+
require (
6+
github.com/containerd/continuity v0.2.2
7+
github.com/containerd/go-cni v0.0.0-00010101000000-000000000000
8+
github.com/stretchr/testify v1.7.0
9+
)
10+
11+
require (
12+
github.com/Microsoft/go-winio v0.5.1 // indirect
13+
github.com/containernetworking/cni v1.0.1 // indirect
14+
github.com/davecgh/go-spew v1.1.1 // indirect
15+
github.com/pmezard/go-difflib v1.0.0 // indirect
16+
github.com/sirupsen/logrus v1.7.0 // indirect
17+
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a // indirect
18+
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
19+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
20+
)
21+
22+
replace github.com/containerd/go-cni => ../

0 commit comments

Comments
 (0)