github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/worker/runtime/spec/spec_test.go (about)

     1  package spec_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	"code.cloudfoundry.org/garden"
     7  	"github.com/pf-qiu/concourse/v6/worker/runtime/spec"
     8  	specs "github.com/opencontainers/runtime-spec/specs-go"
     9  	"github.com/stretchr/testify/require"
    10  	"github.com/stretchr/testify/suite"
    11  )
    12  
    13  var (
    14  	dummyMaxUid uint32 = 0
    15  	dummyMaxGid uint32 = 0
    16  )
    17  
    18  type SpecSuite struct {
    19  	suite.Suite
    20  	*require.Assertions
    21  }
    22  
    23  func uint64Ptr(i uint64) *uint64 { return &i }
    24  func int64Ptr(i int64) *int64 { return &i }
    25  
    26  func (s *SpecSuite) TestContainerSpecValidations() {
    27  	for _, tc := range []struct {
    28  		desc string
    29  		spec garden.ContainerSpec
    30  	}{
    31  		{
    32  			desc: "no handle specified",
    33  			spec: garden.ContainerSpec{},
    34  		},
    35  		{
    36  			desc: "rootfsPath not specified",
    37  			spec: garden.ContainerSpec{
    38  				Handle: "handle",
    39  			},
    40  		},
    41  		{
    42  			desc: "rootfsPath without scheme",
    43  			spec: garden.ContainerSpec{
    44  				Handle:     "handle",
    45  				RootFSPath: "foo",
    46  			},
    47  		},
    48  		{
    49  			desc: "rootfsPath with unknown scheme",
    50  			spec: garden.ContainerSpec{
    51  				Handle:     "handle",
    52  				RootFSPath: "weird://foo",
    53  			},
    54  		},
    55  		{
    56  			desc: "rootfsPath not being absolute",
    57  			spec: garden.ContainerSpec{
    58  				Handle:     "handle",
    59  				RootFSPath: "raw://../not/absolute/at/all",
    60  			},
    61  		},
    62  		{
    63  			desc: "both rootfsPath and image specified",
    64  			spec: garden.ContainerSpec{
    65  				Handle:     "handle",
    66  				RootFSPath: "foo",
    67  				Image:      garden.ImageRef{URI: "bar"},
    68  			},
    69  		},
    70  		{
    71  			desc: "no rootfsPath, but image specified w/out scheme",
    72  			spec: garden.ContainerSpec{
    73  				Handle: "handle",
    74  				Image:  garden.ImageRef{URI: "bar"},
    75  			},
    76  		},
    77  		{
    78  			desc: "no rootfsPath, but image specified w/ unknown scheme",
    79  			spec: garden.ContainerSpec{
    80  				Handle: "handle",
    81  				Image:  garden.ImageRef{URI: "weird://bar"},
    82  			},
    83  		},
    84  	} {
    85  		s.T().Run(tc.desc, func(t *testing.T) {
    86  			_, err := spec.OciSpec(spec.DefaultInitBinPath, tc.spec, dummyMaxUid, dummyMaxGid)
    87  			s.Error(err)
    88  		})
    89  	}
    90  }
    91  
    92  func (s *SpecSuite) TestIDMappings() {
    93  	// TODO
    94  	//
    95  	// ensure that we mutate the right thing
    96  }
    97  
    98  func (s *SpecSuite) TestOciSpecBindMounts() {
    99  	for _, tc := range []struct {
   100  		desc     string
   101  		mounts   []garden.BindMount
   102  		expected []specs.Mount
   103  		succeeds bool
   104  	}{
   105  		{
   106  			desc:     "unknown mode",
   107  			succeeds: false,
   108  			mounts: []garden.BindMount{
   109  				{
   110  					SrcPath: "/a",
   111  					DstPath: "/b",
   112  					Mode:    123,
   113  					Origin:  garden.BindMountOriginHost,
   114  				},
   115  			},
   116  		},
   117  		{
   118  			desc:     "unknown origin",
   119  			succeeds: false,
   120  			mounts: []garden.BindMount{
   121  				{
   122  					SrcPath: "/a",
   123  					DstPath: "/b",
   124  					Mode:    garden.BindMountModeRO,
   125  					Origin:  123,
   126  				},
   127  			},
   128  		},
   129  		{
   130  			desc:     "w/out src",
   131  			succeeds: false,
   132  			mounts: []garden.BindMount{
   133  				{
   134  					DstPath: "/b",
   135  					Mode:    garden.BindMountModeRO,
   136  					Origin:  garden.BindMountOriginHost,
   137  				},
   138  			},
   139  		},
   140  		{
   141  			desc:     "non-absolute src",
   142  			succeeds: false,
   143  			mounts: []garden.BindMount{
   144  				{
   145  					DstPath: "/b",
   146  					Mode:    garden.BindMountModeRO,
   147  					Origin:  garden.BindMountOriginHost,
   148  				},
   149  			},
   150  		},
   151  		{
   152  			desc:     "w/out dest",
   153  			succeeds: false,
   154  			mounts: []garden.BindMount{
   155  				{
   156  					SrcPath: "/a",
   157  					Mode:    garden.BindMountModeRO,
   158  					Origin:  garden.BindMountOriginHost,
   159  				},
   160  			},
   161  		},
   162  		{
   163  			desc:     "non-absolute dest",
   164  			succeeds: false,
   165  			mounts: []garden.BindMount{
   166  				{
   167  					DstPath: "/b",
   168  					Mode:    garden.BindMountModeRO,
   169  					Origin:  garden.BindMountOriginHost,
   170  				},
   171  			},
   172  		},
   173  	} {
   174  		s.T().Run(tc.desc, func(t *testing.T) {
   175  			actual, err := spec.OciSpecBindMounts(tc.mounts)
   176  			if !tc.succeeds {
   177  				s.Error(err)
   178  				return
   179  			}
   180  
   181  			s.NoError(err)
   182  			s.Equal(tc.expected, actual)
   183  		})
   184  	}
   185  }
   186  
   187  func (s *SpecSuite) TestOciNamespaces() {
   188  	for _, tc := range []struct {
   189  		desc       string
   190  		privileged bool
   191  		expected   []specs.LinuxNamespace
   192  	}{
   193  		{
   194  			desc:       "privileged",
   195  			privileged: true,
   196  			expected:   spec.PrivilegedContainerNamespaces,
   197  		},
   198  		{
   199  			desc:       "unprivileged",
   200  			privileged: false,
   201  			expected:   spec.UnprivilegedContainerNamespaces,
   202  		},
   203  	} {
   204  		s.T().Run(tc.desc, func(t *testing.T) {
   205  			s.Equal(tc.expected, spec.OciNamespaces(tc.privileged))
   206  		})
   207  	}
   208  }
   209  
   210  func (s *SpecSuite) TestOciCapabilities() {
   211  	for _, tc := range []struct {
   212  		desc       string
   213  		privileged bool
   214  		expected   specs.LinuxCapabilities
   215  	}{
   216  		{
   217  			desc:       "privileged",
   218  			privileged: true,
   219  			expected:   spec.PrivilegedContainerCapabilities,
   220  		},
   221  		{
   222  			desc:       "unprivileged",
   223  			privileged: false,
   224  			expected:   spec.UnprivilegedContainerCapabilities,
   225  		},
   226  	} {
   227  		s.T().Run(tc.desc, func(t *testing.T) {
   228  			s.Equal(tc.expected, spec.OciCapabilities(tc.privileged))
   229  		})
   230  	}
   231  }
   232  
   233  func (s *SpecSuite) TestOciResourceLimits() {
   234  	for _, tc := range []struct {
   235  		desc     string
   236  		limits   garden.Limits
   237  		expected *specs.LinuxResources
   238  	}{
   239  		{
   240  			desc: "CPU limit in weight",
   241  			limits: garden.Limits{
   242  				CPU: garden.CPULimits{
   243  					Weight: 512,
   244  				},
   245  			},
   246  			expected: &specs.LinuxResources{
   247  				CPU: &specs.LinuxCPU{
   248  					Shares: uint64Ptr(512),
   249  				},
   250  			},
   251  		},
   252  		{
   253  			desc: "CPU limit in shares",
   254  			limits: garden.Limits{
   255  				CPU: garden.CPULimits{
   256  					LimitInShares: 512,
   257  				},
   258  			},
   259  			expected: &specs.LinuxResources{
   260  				CPU: &specs.LinuxCPU{
   261  					Shares: uint64Ptr(512),
   262  				},
   263  			},
   264  		},
   265  		{
   266  			desc: "CPU limit prefers weight",
   267  			limits: garden.Limits{
   268  				CPU: garden.CPULimits{
   269  					LimitInShares: 512,
   270  					Weight:        1024,
   271  				},
   272  			},
   273  			expected: &specs.LinuxResources{
   274  				CPU: &specs.LinuxCPU{
   275  					Shares: uint64Ptr(1024),
   276  				},
   277  			},
   278  		},
   279  		{
   280  			desc: "Memory limit",
   281  			limits: garden.Limits{
   282  				Memory: garden.MemoryLimits{
   283  					LimitInBytes: 10000,
   284  				},
   285  			},
   286  			expected: &specs.LinuxResources{
   287  				Memory: &specs.LinuxMemory{
   288  					Limit: int64Ptr(10000),
   289  					Swap:  int64Ptr(10000),
   290  				},
   291  			},
   292  		},
   293  		{
   294  			desc: "PID limit",
   295  			limits: garden.Limits{
   296  				Pid: garden.PidLimits {
   297  					Max: 1000,
   298  				},
   299  			},
   300  			expected: &specs.LinuxResources{
   301  				Pids: &specs.LinuxPids{
   302  					Limit: 1000,
   303  				},
   304  			},
   305  		},
   306  		{
   307  			desc: "No limits specified",
   308  			limits: garden.Limits{},
   309  			expected: nil,
   310  		},
   311  	} {
   312  		s.T().Run(tc.desc, func(t *testing.T) {
   313  			s.Equal(tc.expected, spec.OciResources(tc.limits))
   314  		})
   315  	}
   316  }
   317  
   318  func (s *SpecSuite) TestOciCgroupsPath() {
   319  	for _, tc := range []struct {
   320  		desc       string
   321  		basePath   string
   322  		handle     string
   323  		privileged bool
   324  		expected   string
   325  	}{
   326  		{
   327  			desc: "not privileged",
   328  			basePath: "garden",
   329  			handle: "1234",
   330  			privileged: false,
   331  			expected: "garden/1234",
   332  		},
   333  		{
   334  			desc: "privileged",
   335  			basePath: "garden",
   336  			handle: "1234",
   337  			privileged: true,
   338  			expected: "",
   339  		},
   340  	} {
   341  		s.T().Run(tc.desc, func(t *testing.T) {
   342  			s.Equal(tc.expected, spec.OciCgroupsPath(tc.basePath, tc.handle, tc.privileged))
   343  		})
   344  	}
   345  }
   346  
   347  func (s *SpecSuite) TestContainerSpec() {
   348  	var minimalContainerSpec = garden.ContainerSpec{
   349  		Handle: "handle", RootFSPath: "raw:///rootfs",
   350  	}
   351  
   352  	for _, tc := range []struct {
   353  		desc  string
   354  		gdn   garden.ContainerSpec
   355  		check func(*specs.Spec)
   356  	}{
   357  		{
   358  			desc: "defaults",
   359  			gdn:  minimalContainerSpec,
   360  			check: func(oci *specs.Spec) {
   361  				s.Equal("/", oci.Process.Cwd)
   362  				s.Equal([]string{"/tmp/gdn-init"}, oci.Process.Args)
   363  				s.Equal(oci.Mounts, spec.AnyContainerMounts(spec.DefaultInitBinPath))
   364  
   365  				s.Equal(minimalContainerSpec.Handle, oci.Hostname)
   366  				s.Equal(spec.AnyContainerDevices, oci.Linux.Resources.Devices)
   367  			},
   368  		},
   369  		{
   370  			desc: "default devices privileged",
   371  			gdn:  garden.ContainerSpec{
   372  				Handle: "handle", RootFSPath: "raw:///rootfs",
   373  				Privileged: true,
   374  			},
   375  			check: func(oci *specs.Spec) {
   376  				s.Equal(append(spec.PrivilegedOnlyDevices, spec.AnyContainerDevices...), oci.Linux.Resources.Devices)
   377  			},
   378  		},
   379  		{
   380  			desc: "env + default path",
   381  			gdn: garden.ContainerSpec{
   382  				Handle: "handle", RootFSPath: "raw:///rootfs",
   383  				Env: []string{"foo=bar"},
   384  			},
   385  			check: func(oci *specs.Spec) {
   386  				s.Equal([]string{"foo=bar", spec.Path}, oci.Process.Env)
   387  			},
   388  		},
   389  		{
   390  			desc: "env + default root path",
   391  			gdn: garden.ContainerSpec{
   392  				Handle: "handle", RootFSPath: "raw:///rootfs",
   393  				Env:        []string{"foo=bar"},
   394  				Privileged: true,
   395  			},
   396  			check: func(oci *specs.Spec) {
   397  				s.Equal([]string{"foo=bar", spec.SuperuserPath}, oci.Process.Env)
   398  			},
   399  		},
   400  		{
   401  			desc: "env with path already configured",
   402  			gdn: garden.ContainerSpec{
   403  				Handle: "handle", RootFSPath: "raw:///rootfs",
   404  				Env: []string{"foo=bar", "PATH=/somewhere"},
   405  			},
   406  			check: func(oci *specs.Spec) {
   407  				s.Equal([]string{"foo=bar", "PATH=/somewhere"}, oci.Process.Env)
   408  			},
   409  		},
   410  		{
   411  			desc: "mounts",
   412  			gdn: garden.ContainerSpec{
   413  				Handle: "handle", RootFSPath: "raw:///rootfs",
   414  				BindMounts: []garden.BindMount{
   415  					{ // ro mount
   416  						SrcPath: "/a",
   417  						DstPath: "/b",
   418  						Mode:    garden.BindMountModeRO,
   419  						Origin:  garden.BindMountOriginHost,
   420  					},
   421  					{ // rw mount
   422  						SrcPath: "/a",
   423  						DstPath: "/b",
   424  						Mode:    garden.BindMountModeRW,
   425  						Origin:  garden.BindMountOriginHost,
   426  					},
   427  				},
   428  			},
   429  			check: func(oci *specs.Spec) {
   430  				s.Contains(oci.Mounts, specs.Mount{
   431  					Source:      "/a",
   432  					Destination: "/b",
   433  					Type:        "bind",
   434  					Options:     []string{"bind", "ro"},
   435  				})
   436  				s.Contains(oci.Mounts, specs.Mount{
   437  					Source:      "/a",
   438  					Destination: "/b",
   439  					Type:        "bind",
   440  					Options:     []string{"bind", "rw"},
   441  				})
   442  			},
   443  		},
   444  		{
   445  			desc: "seccomp is not empty for unprivileged",
   446  			gdn: garden.ContainerSpec{
   447  				Handle: "handle", RootFSPath: "raw:///rootfs",
   448  				Privileged: false,
   449  			},
   450  			check: func(oci *specs.Spec) {
   451  				s.NotEmpty(oci.Linux.Seccomp)
   452  			},
   453  		},
   454  		{
   455  			desc: "seccomp is empty for privileged",
   456  			gdn: garden.ContainerSpec{
   457  				Handle: "handle", RootFSPath: "raw:///rootfs",
   458  				Privileged: true,
   459  			},
   460  			check: func(oci *specs.Spec) {
   461  				s.Empty(oci.Linux.Seccomp)
   462  			},
   463  		},
   464  		{
   465  			desc: "limits",
   466  			gdn: garden.ContainerSpec{
   467  				Handle: "handle", RootFSPath: "raw:///rootfs",
   468  				Limits: garden.Limits{
   469  					CPU: garden.CPULimits{
   470  						Weight: 512,
   471  					},
   472  					Memory: garden.MemoryLimits{
   473  						LimitInBytes: 10000,
   474  					},
   475  					Pid: garden.PidLimits{
   476  						Max: 1000,
   477  					},
   478  				},
   479  			},
   480  			check: func(oci *specs.Spec) {
   481  				s.NotNil(oci.Linux.Resources.CPU)
   482  				s.Equal(uint64Ptr(512), oci.Linux.Resources.CPU.Shares)
   483  				s.NotNil(oci.Linux.Resources.Memory)
   484  				s.Equal(int64Ptr(10000), oci.Linux.Resources.Memory.Limit)
   485  				s.NotNil(oci.Linux.Resources.Pids)
   486  				s.Equal(int64(1000), oci.Linux.Resources.Pids.Limit)
   487  
   488  				s.NotNil(oci.Linux.Resources.Devices)
   489  			},
   490  		},
   491  		{
   492  			desc: "cgroups path",
   493  			gdn: garden.ContainerSpec{
   494  				Handle: "handle", RootFSPath: "raw:///rootfs",
   495  				Privileged: false,
   496  			},
   497  			check: func(oci *specs.Spec) {
   498  				s.Equal("garden/handle", oci.Linux.CgroupsPath)
   499  			},
   500  		},
   501  	} {
   502  		s.T().Run(tc.desc, func(t *testing.T) {
   503  			actual, err := spec.OciSpec(spec.DefaultInitBinPath, tc.gdn, dummyMaxUid, dummyMaxGid)
   504  			s.NoError(err)
   505  
   506  			tc.check(actual)
   507  		})
   508  	}
   509  }