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

     1  package runtime_test
     2  
     3  import (
     4  	"errors"
     5  
     6  	"code.cloudfoundry.org/garden"
     7  	"github.com/pf-qiu/concourse/v6/worker/runtime"
     8  	"github.com/pf-qiu/concourse/v6/worker/runtime/libcontainerd/libcontainerdfakes"
     9  	"github.com/pf-qiu/concourse/v6/worker/runtime/runtimefakes"
    10  	"github.com/containerd/containerd"
    11  	"github.com/opencontainers/runtime-spec/specs-go"
    12  	"github.com/stretchr/testify/require"
    13  	"github.com/stretchr/testify/suite"
    14  )
    15  
    16  type ContainerSuite struct {
    17  	suite.Suite
    18  	*require.Assertions
    19  
    20  	container           *runtime.Container
    21  	containerdContainer *libcontainerdfakes.FakeContainer
    22  	containerdProcess   *libcontainerdfakes.FakeProcess
    23  	containerdTask      *libcontainerdfakes.FakeTask
    24  	rootfsManager       *runtimefakes.FakeRootfsManager
    25  	killer              *runtimefakes.FakeKiller
    26  }
    27  
    28  func (s *ContainerSuite) SetupTest() {
    29  	s.containerdContainer = new(libcontainerdfakes.FakeContainer)
    30  	s.containerdProcess = new(libcontainerdfakes.FakeProcess)
    31  	s.containerdTask = new(libcontainerdfakes.FakeTask)
    32  	s.rootfsManager = new(runtimefakes.FakeRootfsManager)
    33  	s.killer = new(runtimefakes.FakeKiller)
    34  
    35  	s.container = runtime.NewContainer(
    36  		s.containerdContainer,
    37  		s.killer,
    38  		s.rootfsManager,
    39  	)
    40  }
    41  
    42  // func (s *ContainerSuite) TestStopWithKillUngracefullyStops() {
    43  // 	err := s.container.Stop(true)
    44  // 	s.NoError(err)
    45  // 	s.Equal(1, s.ungracefulKiller.KillCallCount())
    46  // }
    47  
    48  // func (s *ContainerSuite) TestStopWithKillFailing() {
    49  // 	s.ungracefulKiller.UngracefullyStopReturns(errors.NewGardenBackend("ungraceful-stop-err"))
    50  
    51  // 	err := s.container.Stop(true)
    52  // 	s.Equal(1, s.ungracefulKiller.UngracefullyStopCallCount())
    53  // 	s.EqualError(errors.Unwrap(err), "ungraceful-stop-err")
    54  // }
    55  
    56  // func (s *ContainerSuite) TestStopWithoutKillGracefullyStops() {
    57  // 	err := s.container.Stop(false)
    58  // 	s.NoError(err)
    59  // 	s.Equal(1, s.ungracefulKiller.GracefullyStopCallCount())
    60  // }
    61  
    62  // func (s *ContainerSuite) TestStopWithoutKillFailing() {
    63  // 	s.ungracefulKiller.GracefullyStopReturns(errors.NewGardenBackend("graceful-stop-err"))
    64  
    65  // 	err := s.container.Stop(false)
    66  // 	s.EqualError(errors.Unwrap(err), "graceful-stop-err")
    67  // 	s.Equal(1, s.ungracefulKiller.GracefullyStopCallCount())
    68  // }
    69  
    70  func (s *ContainerSuite) TestRunContainerSpecErr() {
    71  	expectedErr := errors.New("spec-err")
    72  	s.containerdContainer.SpecReturns(nil, expectedErr)
    73  
    74  	_, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{})
    75  	s.True(errors.Is(err, expectedErr))
    76  }
    77  
    78  func (s *ContainerSuite) TestRunWithNonRootCwdSetupCwdFails() {
    79  	s.containerdContainer.SpecReturns(&specs.Spec{
    80  		Process: &specs.Process{},
    81  		Root:    &specs.Root{},
    82  	}, nil)
    83  
    84  	expectedErr := errors.New("setup-cwd-err")
    85  	s.rootfsManager.SetupCwdReturns(expectedErr)
    86  
    87  	_, err := s.container.Run(garden.ProcessSpec{Dir: "/somewhere"}, garden.ProcessIO{})
    88  	s.True(errors.Is(err, expectedErr))
    89  }
    90  
    91  func (s *ContainerSuite) TestRunTaskError() {
    92  	s.containerdContainer.SpecReturns(&specs.Spec{
    93  		Process: &specs.Process{},
    94  		Root:    &specs.Root{},
    95  	}, nil)
    96  
    97  	expectedErr := errors.New("task-err")
    98  	s.containerdContainer.TaskReturns(nil, expectedErr)
    99  
   100  	_, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{})
   101  	s.True(errors.Is(err, expectedErr))
   102  }
   103  
   104  func (s *ContainerSuite) TestRunTaskExecError() {
   105  	s.containerdContainer.SpecReturns(&specs.Spec{
   106  		Process: &specs.Process{},
   107  		Root:    &specs.Root{},
   108  	}, nil)
   109  
   110  	s.containerdContainer.TaskReturns(s.containerdTask, nil)
   111  
   112  	expectedErr := errors.New("exec-err")
   113  	s.containerdTask.ExecReturns(nil, expectedErr)
   114  
   115  	_, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{})
   116  	s.True(errors.Is(err, expectedErr))
   117  }
   118  
   119  func (s *ContainerSuite) TestRunProcWaitError() {
   120  	s.containerdContainer.SpecReturns(&specs.Spec{
   121  		Process: &specs.Process{},
   122  		Root:    &specs.Root{},
   123  	}, nil)
   124  
   125  	s.containerdContainer.TaskReturns(s.containerdTask, nil)
   126  	s.containerdTask.ExecReturns(s.containerdProcess, nil)
   127  
   128  	expectedErr := errors.New("proc-wait-err")
   129  	s.containerdProcess.WaitReturns(nil, expectedErr)
   130  
   131  	_, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{})
   132  	s.True(errors.Is(err, expectedErr))
   133  }
   134  
   135  func (s *ContainerSuite) TestRunProcStartError() {
   136  	s.containerdContainer.SpecReturns(&specs.Spec{
   137  		Process: &specs.Process{},
   138  		Root:    &specs.Root{},
   139  	}, nil)
   140  
   141  	s.containerdContainer.TaskReturns(s.containerdTask, nil)
   142  	s.containerdTask.ExecReturns(s.containerdProcess, nil)
   143  
   144  	expectedErr := errors.New("proc-start-err")
   145  	s.containerdProcess.StartReturns(expectedErr)
   146  
   147  	_, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{})
   148  	s.True(errors.Is(err, expectedErr))
   149  }
   150  
   151  func (s *ContainerSuite) TestRunProcStartErrorExecutableNotFound() {
   152  	s.containerdContainer.SpecReturns(&specs.Spec{
   153  		Process: &specs.Process{},
   154  		Root:    &specs.Root{},
   155  	}, nil)
   156  
   157  	s.containerdContainer.TaskReturns(s.containerdTask, nil)
   158  	s.containerdTask.ExecReturns(s.containerdProcess, nil)
   159  
   160  	exeNotFoundErr := errors.New("OCI runtime exec failed: exec failed: container_linux.go:345: starting container process caused: exec: potato: executable file not found in $PATH")
   161  	s.containerdProcess.StartReturns(exeNotFoundErr)
   162  
   163  	_, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{})
   164  	s.True(errors.Is(err, garden.ExecutableNotFoundError{Message: exeNotFoundErr.Error()}))
   165  }
   166  
   167  func (s *ContainerSuite) TestRunProcCloseIOError() {
   168  	s.containerdContainer.SpecReturns(&specs.Spec{
   169  		Process: &specs.Process{},
   170  		Root:    &specs.Root{},
   171  	}, nil)
   172  
   173  	s.containerdContainer.TaskReturns(s.containerdTask, nil)
   174  	s.containerdTask.ExecReturns(s.containerdProcess, nil)
   175  
   176  	expectedErr := errors.New("proc-closeio-err")
   177  	s.containerdProcess.CloseIOReturns(expectedErr)
   178  
   179  	_, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{})
   180  	s.True(errors.Is(err, expectedErr))
   181  }
   182  
   183  func (s *ContainerSuite) TestRunProcCloseIOWithStdin() {
   184  	s.containerdContainer.SpecReturns(&specs.Spec{
   185  		Process: &specs.Process{},
   186  		Root:    &specs.Root{},
   187  	}, nil)
   188  
   189  	s.containerdContainer.TaskReturns(s.containerdTask, nil)
   190  	s.containerdTask.ExecReturns(s.containerdProcess, nil)
   191  
   192  	_, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{})
   193  	s.NoError(err)
   194  
   195  	s.Equal(1, s.containerdProcess.CloseIOCallCount())
   196  	_, opts := s.containerdProcess.CloseIOArgsForCall(0)
   197  	s.Len(opts, 1)
   198  
   199  	// you can't compare two functions in Go, so, compare its effects (these
   200  	// are functional opts).
   201  	//
   202  	obj := containerd.IOCloseInfo{}
   203  	opts[0](&obj)
   204  
   205  	// we want to make sure that we're passing an opt that sets `Stdin` to
   206  	// true on an `IOCloseInfo`.
   207  	s.True(obj.Stdin)
   208  }
   209  
   210  func (s *ContainerSuite) TestRunWithUserLookupSucceeds() {
   211  	s.containerdContainer.SpecReturns(&specs.Spec{
   212  		Process: &specs.Process{},
   213  		Root:    &specs.Root{},
   214  	}, nil)
   215  
   216  	s.containerdContainer.TaskReturns(s.containerdTask, nil)
   217  	s.containerdTask.ExecReturns(s.containerdProcess, nil)
   218  
   219  	expectedUser := specs.User{UID: 1, GID: 2, Username: "some_user"}
   220  	s.rootfsManager.LookupUserReturns(expectedUser, true, nil)
   221  
   222  	_, err := s.container.Run(garden.ProcessSpec{User: "some_user"}, garden.ProcessIO{})
   223  	s.NoError(err)
   224  
   225  	_, _, procSpec, _ := s.containerdTask.ExecArgsForCall(0)
   226  	s.Equal(expectedUser, procSpec.User)
   227  
   228  	userEnvVarSet := false
   229  	expectedEnvVar := "USER=some_user"
   230  
   231  	for _, envVar := range procSpec.Env {
   232  		if envVar == expectedEnvVar {
   233  			userEnvVarSet = true
   234  			break
   235  		}
   236  	}
   237  	s.True(userEnvVarSet)
   238  }
   239  
   240  func (s *ContainerSuite) TestRunWithUserLookupErrors() {
   241  	s.containerdContainer.SpecReturns(&specs.Spec{
   242  		Process: &specs.Process{},
   243  		Root:    &specs.Root{},
   244  	}, nil)
   245  
   246  	s.containerdContainer.TaskReturns(s.containerdTask, nil)
   247  	s.containerdTask.ExecReturns(s.containerdProcess, nil)
   248  
   249  	expectedErr := errors.New("lookup error")
   250  	s.rootfsManager.LookupUserReturns(specs.User{}, false, expectedErr)
   251  
   252  	_, err := s.container.Run(garden.ProcessSpec{User: "some_user"}, garden.ProcessIO{})
   253  	s.True(errors.Is(err, expectedErr))
   254  }
   255  
   256  func (s *ContainerSuite) TestRunWithUserLookupNotFound() {
   257  	s.containerdContainer.SpecReturns(&specs.Spec{
   258  		Process: &specs.Process{},
   259  		Root:    &specs.Root{},
   260  	}, nil)
   261  
   262  	s.containerdContainer.TaskReturns(s.containerdTask, nil)
   263  	s.containerdTask.ExecReturns(s.containerdProcess, nil)
   264  
   265  	s.rootfsManager.LookupUserReturns(specs.User{}, false, nil)
   266  
   267  	_, err := s.container.Run(garden.ProcessSpec{User: "some_invalid_user"}, garden.ProcessIO{})
   268  	s.True(errors.Is(err, runtime.UserNotFoundError{User: "some_invalid_user"}))
   269  }
   270  
   271  func (s *ContainerSuite) TestSetGraceTimeSetLabelsFails() {
   272  	expectedErr := errors.New("set-label-error")
   273  	s.containerdContainer.SetLabelsReturns(nil, expectedErr)
   274  
   275  	err := s.container.SetGraceTime(1234)
   276  	s.True(errors.Is(err, expectedErr))
   277  }
   278  
   279  func (s *ContainerSuite) TestSetGraceTimeSetLabelsSucceeds() {
   280  	err := s.container.SetGraceTime(1234)
   281  	s.NoError(err)
   282  
   283  	expectedLabelSet := map[string]string{
   284  		"garden.grace-time": "1234",
   285  	}
   286  	_, labelSet := s.containerdContainer.SetLabelsArgsForCall(0)
   287  	s.Equal(expectedLabelSet, labelSet)
   288  }
   289  
   290  func (s *ContainerSuite) TestPropertyGetLabelsFails() {
   291  	expectedErr := errors.New("get-labels-error")
   292  	s.containerdContainer.LabelsReturns(nil, expectedErr)
   293  	_, err := s.container.Property("any")
   294  	s.True(errors.Is(err, expectedErr))
   295  }
   296  
   297  func (s *ContainerSuite) TestPropertyNotFound() {
   298  	s.containerdContainer.LabelsReturns(garden.Properties{}, nil)
   299  	_, err := s.container.Property("any")
   300  	s.Equal(runtime.ErrNotFound("any"), err)
   301  }
   302  
   303  func (s *ContainerSuite) TestPropertyReturnsValue() {
   304  	properties := garden.Properties{
   305  		"any": "some-value",
   306  	}
   307  	s.containerdContainer.LabelsReturns(properties, nil)
   308  	result, err := s.container.Property("any")
   309  	s.NoError(err)
   310  	s.Equal("some-value", result)
   311  }
   312  
   313  func (s *ContainerSuite) TestCurrentCPULimitsGetInfoFails() {
   314  	expectedErr := errors.New("get-spec-error")
   315  	s.containerdContainer.SpecReturns(nil, expectedErr)
   316  	_, err := s.container.CurrentCPULimits()
   317  	s.True(errors.Is(err, expectedErr))
   318  }
   319  
   320  func (s *ContainerSuite) TestCurrentCPULimitsNoLimitSet() {
   321  	s.containerdContainer.SpecReturns(&specs.Spec{
   322  		Linux: &specs.Linux{
   323  			Resources: &specs.LinuxResources{
   324  				CPU: &specs.LinuxCPU{},
   325  			},
   326  		},
   327  	}, nil)
   328  	limits, err := s.container.CurrentCPULimits()
   329  	s.NoError(err)
   330  	s.Equal(garden.CPULimits{}, limits)
   331  }
   332  
   333  func (s *ContainerSuite) TestCurrentCPULimitsReturnsCPUShares() {
   334  	cpuShares := uint64(512)
   335  	s.containerdContainer.SpecReturns(&specs.Spec{
   336  		Linux: &specs.Linux{
   337  			Resources: &specs.LinuxResources{
   338  				CPU: &specs.LinuxCPU{
   339  					Shares: &cpuShares,
   340  				},
   341  			},
   342  		},
   343  	}, nil)
   344  	limits, err := s.container.CurrentCPULimits()
   345  	s.NoError(err)
   346  	s.Equal(garden.CPULimits{Weight: cpuShares}, limits)
   347  }
   348  
   349  func (s *ContainerSuite) TestCurrentMemoryLimitsGetSpecFails() {
   350  	expectedErr := errors.New("get-spec-error")
   351  	s.containerdContainer.SpecReturns(nil, expectedErr)
   352  	_, err := s.container.CurrentMemoryLimits()
   353  	s.True(errors.Is(err, expectedErr))
   354  }
   355  
   356  func (s *ContainerSuite) TestCurrentMemoryLimitsNoLimitSet() {
   357  	s.containerdContainer.SpecReturns(&specs.Spec{
   358  		Linux: &specs.Linux{
   359  			Resources: &specs.LinuxResources{
   360  				Memory: &specs.LinuxMemory{},
   361  			},
   362  		},
   363  	}, nil)
   364  	limits, err := s.container.CurrentMemoryLimits()
   365  	s.NoError(err)
   366  	s.Equal(garden.MemoryLimits{}, limits)
   367  }
   368  
   369  func (s *ContainerSuite) TestCurrentMemoryLimitsReturnsLimit() {
   370  	limitBytes := int64(512)
   371  	s.containerdContainer.SpecReturns(&specs.Spec{
   372  		Linux: &specs.Linux{
   373  			Resources: &specs.LinuxResources{
   374  				Memory: &specs.LinuxMemory{
   375  					Limit: &limitBytes,
   376  				},
   377  			},
   378  		},
   379  	}, nil)
   380  	limits, err := s.container.CurrentMemoryLimits()
   381  	s.NoError(err)
   382  	s.Equal(garden.MemoryLimits{LimitInBytes: uint64(limitBytes)}, limits)
   383  }