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  }