github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/netutil/netutil_test.go (about) 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 package netutil 18 19 import ( 20 "bytes" 21 "fmt" 22 "net" 23 "os" 24 "path/filepath" 25 "strconv" 26 "testing" 27 "text/template" 28 29 ncdefaults "github.com/containerd/nerdctl/v2/pkg/defaults" 30 "github.com/containerd/nerdctl/v2/pkg/labels" 31 "github.com/containerd/nerdctl/v2/pkg/testutil" 32 33 "gotest.tools/v3/assert" 34 ) 35 36 const preExistingNetworkConfigTemplate = ` 37 { 38 "cniVersion": "0.2.0", 39 "name": "{{ .network_name }}", 40 "type": "nat", 41 "master": "Ethernet", 42 "ipam": { 43 "subnet": "{{ .subnet }}", 44 "routes": [ 45 { 46 "GW": "{{ .gateway }}" 47 } 48 ] 49 }, 50 "capabilities": { 51 "portMappings": true, 52 "dns": true 53 } 54 } 55 ` 56 57 func TestParseIPAMRange(t *testing.T) { 58 t.Parallel() 59 type testCase struct { 60 subnet string 61 gateway string 62 iprange string 63 expected *IPAMRange 64 err string 65 } 66 testCases := []testCase{ 67 { 68 subnet: "10.1.100.0/24", 69 expected: &IPAMRange{ 70 Subnet: "10.1.100.0/24", 71 Gateway: "10.1.100.1", 72 }, 73 }, 74 { 75 subnet: "10.1.100.0/24", 76 gateway: "10.1.10.100", 77 err: "no matching subnet", 78 }, 79 { 80 subnet: "10.1.100.0/24", 81 gateway: "10.1.100.100", 82 expected: &IPAMRange{ 83 Subnet: "10.1.100.0/24", 84 Gateway: "10.1.100.100", 85 }, 86 }, 87 { 88 subnet: "10.1.100.0/23", 89 gateway: "10.1.102.1", 90 err: "no matching subnet", 91 }, 92 { 93 subnet: "10.1.0.0/16", 94 iprange: "10.10.10.0/24", 95 err: "no matching subnet", 96 }, 97 { 98 subnet: "10.1.0.0/16", 99 iprange: "10.1.100.0/24", 100 expected: &IPAMRange{ 101 Subnet: "10.1.0.0/16", 102 Gateway: "10.1.0.1", 103 IPRange: "10.1.100.0/24", 104 RangeStart: "10.1.100.1", 105 RangeEnd: "10.1.100.255", 106 }, 107 }, 108 { 109 subnet: "10.1.100.0/23", 110 iprange: "10.1.100.0/25", 111 expected: &IPAMRange{ 112 Subnet: "10.1.100.0/23", 113 Gateway: "10.1.100.1", 114 IPRange: "10.1.100.0/25", 115 RangeStart: "10.1.100.1", 116 RangeEnd: "10.1.100.127", 117 }, 118 }, 119 } 120 for _, tc := range testCases { 121 _, subnet, _ := net.ParseCIDR(tc.subnet) 122 got, err := parseIPAMRange(subnet, tc.gateway, tc.iprange) 123 if tc.err != "" { 124 assert.ErrorContains(t, err, tc.err) 125 } else { 126 assert.NilError(t, err) 127 assert.Equal(t, *tc.expected, *got) 128 } 129 } 130 } 131 132 // Tests whether nerdctl properly creates the default network when required. 133 // Note that this test will require a CNI driver bearing the same name as 134 // the type of the default network. (denoted by netutil.DefaultNetworkName, 135 // which is used as both the name of the default network and its Driver) 136 func testDefaultNetworkCreation(t *testing.T) { 137 // To prevent subnet collisions when attempting to recreate the default network 138 // in the isolated CNI config dir we'll be using, we must first delete 139 // the network in the default CNI config dir. 140 defaultCniEnv := CNIEnv{ 141 Path: ncdefaults.CNIPath(), 142 NetconfPath: ncdefaults.CNINetConfPath(), 143 } 144 defaultNet, err := defaultCniEnv.GetDefaultNetworkConfig() 145 assert.NilError(t, err) 146 if defaultNet != nil { 147 assert.NilError(t, defaultCniEnv.RemoveNetwork(defaultNet)) 148 } 149 150 // We create a tempdir for the CNI conf path to ensure an empty env for this test. 151 cniConfTestDir := t.TempDir() 152 cniEnv := CNIEnv{ 153 Path: ncdefaults.CNIPath(), 154 NetconfPath: cniConfTestDir, 155 } 156 // Ensure no default network config is not present. 157 defaultNetConf, err := cniEnv.GetDefaultNetworkConfig() 158 assert.NilError(t, err) 159 assert.Assert(t, defaultNetConf == nil) 160 161 // Attempt to create the default network. 162 err = cniEnv.ensureDefaultNetworkConfig() 163 assert.NilError(t, err) 164 165 // Ensure no default network config is present now. 166 defaultNetConf, err = cniEnv.GetDefaultNetworkConfig() 167 assert.NilError(t, err) 168 assert.Assert(t, defaultNetConf != nil) 169 170 // Check network config file present. 171 stat, err := os.Stat(defaultNetConf.File) 172 assert.NilError(t, err) 173 firstConfigModTime := stat.ModTime() 174 175 // Check default network label present. 176 assert.Assert(t, defaultNetConf.NerdctlLabels != nil) 177 lstr, ok := (*defaultNetConf.NerdctlLabels)[labels.NerdctlDefaultNetwork] 178 assert.Assert(t, ok) 179 boolv, err := strconv.ParseBool(lstr) 180 assert.NilError(t, err) 181 assert.Assert(t, boolv) 182 183 // Ensure network isn't created twice or accidentally re-created. 184 err = cniEnv.ensureDefaultNetworkConfig() 185 assert.NilError(t, err) 186 187 // Check for any other network config files. 188 files := []os.FileInfo{} 189 walkF := func(p string, info os.FileInfo, err error) error { 190 files = append(files, info) 191 return nil 192 } 193 err = filepath.Walk(cniConfTestDir, walkF) 194 assert.NilError(t, err) 195 assert.Assert(t, len(files) == 2) // files[0] is the entry for '.' 196 assert.Assert(t, filepath.Join(cniConfTestDir, files[1].Name()) == defaultNetConf.File) 197 assert.Assert(t, firstConfigModTime == files[1].ModTime()) 198 } 199 200 // Tests whether nerdctl skips the creation of the default network if a 201 // network bearing the default network name already exists in a 202 // non-nerdctl-managed network config file. 203 func TestNetworkWithDefaultNameAlreadyExists(t *testing.T) { 204 // We create a tempdir for the CNI conf path to ensure an empty env for this test. 205 cniConfTestDir := t.TempDir() 206 cniEnv := CNIEnv{ 207 Path: t.TempDir(), // irrelevant for this test 208 NetconfPath: cniConfTestDir, 209 } 210 211 // Ensure no default network config is not present. 212 defaultNetConf, err := cniEnv.GetDefaultNetworkConfig() 213 assert.NilError(t, err) 214 assert.Assert(t, defaultNetConf == nil) 215 216 // Manually define and write a network config file. 217 values := map[string]string{ 218 "network_name": DefaultNetworkName, 219 "subnet": "10.7.1.1/24", 220 "gateway": "10.7.1.1", 221 } 222 tpl, err := template.New("test").Parse(preExistingNetworkConfigTemplate) 223 assert.NilError(t, err) 224 buf := &bytes.Buffer{} 225 assert.NilError(t, tpl.ExecuteTemplate(buf, "test", values)) 226 227 // Filename is irrelevant as long as it's not nerdctl's. 228 testConfFile := filepath.Join(cniConfTestDir, fmt.Sprintf("%s.conf", testutil.Identifier(t))) 229 err = os.WriteFile(testConfFile, buf.Bytes(), 0600) 230 assert.NilError(t, err) 231 232 // Check network is detected. 233 netConfs, err := cniEnv.NetworkList() 234 assert.NilError(t, err) 235 assert.Assert(t, len(netConfs) > 0) 236 237 var listedDefaultNetConf *NetworkConfig 238 for _, netConf := range netConfs { 239 if netConf.Name == DefaultNetworkName { 240 listedDefaultNetConf = netConf 241 break 242 } 243 } 244 assert.Assert(t, listedDefaultNetConf != nil) 245 246 defaultNetConf, err = cniEnv.GetDefaultNetworkConfig() 247 assert.NilError(t, err) 248 assert.Assert(t, defaultNetConf != nil) 249 assert.Assert(t, defaultNetConf.File == testConfFile) 250 251 err = cniEnv.ensureDefaultNetworkConfig() 252 assert.NilError(t, err) 253 254 netConfs, err = cniEnv.NetworkList() 255 assert.NilError(t, err) 256 defaultNamedNetworksFileDefinitions := []string{} 257 for _, netConf := range netConfs { 258 if netConf.Name == DefaultNetworkName { 259 defaultNamedNetworksFileDefinitions = append(defaultNamedNetworksFileDefinitions, netConf.File) 260 } 261 } 262 assert.Assert(t, len(defaultNamedNetworksFileDefinitions) == 1) 263 assert.Assert(t, defaultNamedNetworksFileDefinitions[0] == testConfFile) 264 }