github.com/containerd/nerdctl@v1.7.7/pkg/inspecttypes/dockercompat/dockercompat_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 dockercompat 18 19 import ( 20 "net" 21 "os" 22 "path/filepath" 23 "runtime" 24 "testing" 25 26 "github.com/docker/go-connections/nat" 27 "github.com/opencontainers/runtime-spec/specs-go" 28 "gotest.tools/v3/assert" 29 30 "github.com/containerd/containerd" 31 "github.com/containerd/containerd/containers" 32 33 "github.com/containerd/nerdctl/pkg/inspecttypes/native" 34 ) 35 36 func TestContainerFromNative(t *testing.T) { 37 tempStateDir, err := os.MkdirTemp(t.TempDir(), "rw") 38 if err != nil { 39 t.Fatal(err) 40 } 41 os.WriteFile(filepath.Join(tempStateDir, "resolv.conf"), []byte(""), 0644) 42 defer os.RemoveAll(tempStateDir) 43 44 testcase := []struct { 45 name string 46 n *native.Container 47 expected *Container 48 }{ 49 // nerdctl container, mount /mnt/foo:/mnt/foo:rw,rslave; ResolvConfPath; hostname 50 { 51 name: "container from nerdctl", 52 n: &native.Container{ 53 Container: containers.Container{ 54 Labels: map[string]string{ 55 "nerdctl/mounts": "[{\"Type\":\"bind\",\"Source\":\"/mnt/foo\",\"Destination\":\"/mnt/foo\",\"Mode\":\"rshared,rw\",\"RW\":true,\"Propagation\":\"rshared\"}]", 56 "nerdctl/state-dir": tempStateDir, 57 "nerdctl/hostname": "host1", 58 }, 59 }, 60 Spec: &specs.Spec{}, 61 Process: &native.Process{ 62 Pid: 10000, 63 Status: containerd.Status{ 64 Status: "running", 65 }, 66 }, 67 }, 68 expected: &Container{ 69 Created: "0001-01-01T00:00:00Z", 70 Platform: runtime.GOOS, 71 ResolvConfPath: tempStateDir + "/resolv.conf", 72 State: &ContainerState{ 73 Status: "running", 74 Running: true, 75 Pid: 10000, 76 FinishedAt: "0001-01-01T00:00:00Z", 77 }, 78 Mounts: []MountPoint{ 79 { 80 Type: "bind", 81 Source: "/mnt/foo", 82 Destination: "/mnt/foo", 83 Mode: "rshared,rw", 84 RW: true, 85 Propagation: "rshared", 86 }, 87 }, 88 Config: &Config{ 89 Labels: map[string]string{ 90 "nerdctl/mounts": "[{\"Type\":\"bind\",\"Source\":\"/mnt/foo\",\"Destination\":\"/mnt/foo\",\"Mode\":\"rshared,rw\",\"RW\":true,\"Propagation\":\"rshared\"}]", 91 "nerdctl/state-dir": tempStateDir, 92 "nerdctl/hostname": "host1", 93 }, 94 Hostname: "host1", 95 }, 96 NetworkSettings: &NetworkSettings{ 97 Ports: &nat.PortMap{}, 98 Networks: map[string]*NetworkEndpointSettings{}, 99 }, 100 }, 101 }, 102 // cri container, mount /mnt/foo:/mnt/foo:rw,rslave; mount resolv.conf and hostname; internal sysfs mount 103 { 104 name: "container from cri", 105 n: &native.Container{ 106 Container: containers.Container{}, 107 Spec: &specs.Spec{ 108 Mounts: []specs.Mount{ 109 { 110 Destination: "/etc/resolv.conf", 111 Type: "bind", 112 Source: "/mock-sandbox-dir/resolv.conf", 113 Options: []string{"rbind", "rprivate", "rw"}, 114 }, 115 { 116 Destination: "/etc/hostname", 117 Type: "bind", 118 Source: "/mock-sandbox-dir/hostname", 119 Options: []string{"rbind", "rprivate", "rw"}, 120 }, 121 { 122 Destination: "/mnt/foo", 123 Type: "bind", 124 Source: "/mnt/foo", 125 Options: []string{"rbind", "rslave", "rw"}, 126 }, 127 { 128 Destination: "/sys", 129 Type: "sysfs", 130 Source: "sysfs", 131 Options: []string{"nosuid", "noexec", "nodev", "ro"}, 132 }, 133 }, 134 }, 135 Process: &native.Process{ 136 Pid: 10000, 137 Status: containerd.Status{ 138 Status: "running", 139 }, 140 }, 141 }, 142 expected: &Container{ 143 Created: "0001-01-01T00:00:00Z", 144 Platform: runtime.GOOS, 145 ResolvConfPath: "", 146 HostnamePath: "", 147 State: &ContainerState{ 148 Status: "running", 149 Running: true, 150 Pid: 10000, 151 FinishedAt: "0001-01-01T00:00:00Z", 152 }, 153 Config: &Config{}, 154 NetworkSettings: &NetworkSettings{ 155 Ports: &nat.PortMap{}, 156 Networks: map[string]*NetworkEndpointSettings{}, 157 }, 158 }, 159 }, 160 // ctr container, mount /mnt/foo:/mnt/foo:rw,rslave; internal sysfs mount; hostname 161 { 162 name: "container from ctr", 163 n: &native.Container{ 164 Container: containers.Container{}, 165 Spec: &specs.Spec{ 166 Hostname: "", 167 Mounts: []specs.Mount{ 168 { 169 Destination: "/mnt/foo", 170 Type: "bind", 171 Source: "/mnt/foo", 172 Options: []string{"rbind", "rslave", "rw"}, 173 }, 174 { 175 Destination: "/sys", 176 Type: "sysfs", 177 Source: "sysfs", 178 Options: []string{"nosuid", "noexec", "nodev", "ro"}, 179 }, 180 }, 181 }, 182 Process: &native.Process{ 183 Pid: 10000, 184 Status: containerd.Status{ 185 Status: "running", 186 }, 187 }, 188 }, 189 expected: &Container{ 190 Created: "0001-01-01T00:00:00Z", 191 Platform: runtime.GOOS, 192 State: &ContainerState{ 193 Status: "running", 194 Running: true, 195 Pid: 10000, 196 FinishedAt: "0001-01-01T00:00:00Z", 197 }, 198 Config: &Config{ 199 Hostname: "", 200 }, 201 NetworkSettings: &NetworkSettings{ 202 Ports: &nat.PortMap{}, 203 Networks: map[string]*NetworkEndpointSettings{}, 204 }, 205 }, 206 }, 207 } 208 209 for _, tc := range testcase { 210 t.Run(tc.name, func(tt *testing.T) { 211 d, _ := ContainerFromNative(tc.n) 212 assert.DeepEqual(tt, d, tc.expected) 213 }) 214 } 215 } 216 217 func TestNetworkSettingsFromNative(t *testing.T) { 218 tempStateDir, err := os.MkdirTemp(t.TempDir(), "rw") 219 if err != nil { 220 t.Fatal(err) 221 } 222 os.WriteFile(filepath.Join(tempStateDir, "resolv.conf"), []byte(""), 0644) 223 defer os.RemoveAll(tempStateDir) 224 225 testcase := []struct { 226 name string 227 n *native.NetNS 228 s *specs.Spec 229 expected *NetworkSettings 230 }{ 231 // Given null native.NetNS, Return initialized NetworkSettings 232 // UseCase: Inspect a Stopped Container 233 { 234 name: "Given Null NetNS, Return initialized NetworkSettings", 235 n: nil, 236 s: &specs.Spec{}, 237 expected: &NetworkSettings{ 238 Ports: &nat.PortMap{}, 239 Networks: map[string]*NetworkEndpointSettings{}, 240 }, 241 }, 242 // Given native.NetNS with single Interface with Port Annotations, Return populated NetworkSettings 243 // UseCase: Inspect a Running Container with published ports 244 { 245 name: "Given NetNS with single Interface with Port Annotation, Return populated NetworkSettings", 246 n: &native.NetNS{ 247 Interfaces: []native.NetInterface{ 248 { 249 Interface: net.Interface{ 250 Index: 1, 251 MTU: 1500, 252 Name: "eth0.100", 253 Flags: net.FlagUp, 254 }, 255 HardwareAddr: "xx:xx:xx:xx:xx:xx", 256 Flags: []string{}, 257 Addrs: []string{"10.0.4.30/24"}, 258 }, 259 }, 260 }, 261 s: &specs.Spec{ 262 Annotations: map[string]string{ 263 "nerdctl/ports": "[{\"HostPort\":8075,\"ContainerPort\":77,\"Protocol\":\"tcp\",\"HostIP\":\"127.0.0.1\"}]", 264 }, 265 }, 266 expected: &NetworkSettings{ 267 Ports: &nat.PortMap{ 268 nat.Port("77/tcp"): []nat.PortBinding{ 269 { 270 HostIP: "127.0.0.1", 271 HostPort: "8075", 272 }, 273 }, 274 }, 275 Networks: map[string]*NetworkEndpointSettings{ 276 "unknown-eth0.100": { 277 IPAddress: "10.0.4.30", 278 IPPrefixLen: 24, 279 MacAddress: "xx:xx:xx:xx:xx:xx", 280 }, 281 }, 282 }, 283 }, 284 // Given native.NetNS with single Interface without Port Annotations, Return valid NetworkSettings w/ empty Ports 285 // UseCase: Inspect a Running Container without published ports 286 { 287 name: "Given NetNS with single Interface without Port Annotations, Return valid NetworkSettings w/ empty Ports", 288 n: &native.NetNS{ 289 Interfaces: []native.NetInterface{ 290 { 291 Interface: net.Interface{ 292 Index: 1, 293 MTU: 1500, 294 Name: "eth0.100", 295 Flags: net.FlagUp, 296 }, 297 HardwareAddr: "xx:xx:xx:xx:xx:xx", 298 Flags: []string{}, 299 Addrs: []string{"10.0.4.30/24"}, 300 }, 301 }, 302 }, 303 s: &specs.Spec{ 304 Annotations: map[string]string{}, 305 }, 306 expected: &NetworkSettings{ 307 Ports: &nat.PortMap{}, 308 Networks: map[string]*NetworkEndpointSettings{ 309 "unknown-eth0.100": { 310 IPAddress: "10.0.4.30", 311 IPPrefixLen: 24, 312 MacAddress: "xx:xx:xx:xx:xx:xx", 313 }, 314 }, 315 }, 316 }, 317 } 318 319 for _, tc := range testcase { 320 t.Run(tc.name, func(tt *testing.T) { 321 d, _ := networkSettingsFromNative(tc.n, tc.s) 322 assert.DeepEqual(tt, d, tc.expected) 323 }) 324 } 325 }