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

     1  package worker_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"time"
     7  
     8  	"code.cloudfoundry.org/garden"
     9  	"github.com/pf-qiu/concourse/v6/atc/metric"
    10  
    11  	"code.cloudfoundry.org/lager/lagertest"
    12  
    13  	"github.com/pf-qiu/concourse/v6/atc"
    14  	"github.com/pf-qiu/concourse/v6/atc/compression/compressionfakes"
    15  	"github.com/pf-qiu/concourse/v6/atc/db"
    16  	"github.com/pf-qiu/concourse/v6/atc/db/lock/lockfakes"
    17  	"github.com/pf-qiu/concourse/v6/atc/runtime"
    18  	"github.com/pf-qiu/concourse/v6/atc/runtime/runtimefakes"
    19  	"github.com/pf-qiu/concourse/v6/atc/worker"
    20  	"github.com/pf-qiu/concourse/v6/atc/worker/workerfakes"
    21  
    22  	. "github.com/onsi/ginkgo"
    23  	. "github.com/onsi/gomega"
    24  )
    25  
    26  var _ = Describe("RunTaskStep", func() {
    27  	var (
    28  		subject    worker.Client
    29  		taskResult worker.TaskResult
    30  		err        error
    31  
    32  		outputBuffer *bytes.Buffer
    33  		ctx          context.Context
    34  
    35  		fakeWorker          *workerfakes.FakeWorker
    36  		fakePool            *workerfakes.FakePool
    37  		fakeTaskProcessSpec runtime.ProcessSpec
    38  		fakeLock            *lockfakes.FakeLock
    39  		fakeProvider        *workerfakes.FakeWorkerProvider
    40  		fakeCompression     *compressionfakes.FakeCompression
    41  		fakeContainerOwner  db.ContainerOwner
    42  		fakeContainerSpec   worker.ContainerSpec
    43  		fakeWorkerSpec      worker.WorkerSpec
    44  		fakeStrategy        *workerfakes.FakeContainerPlacementStrategy
    45  		fakeMetadata        db.ContainerMetadata
    46  		fakeEventDelegate   *runtimefakes.FakeStartingEventDelegate
    47  		fakeLockFactory     *lockfakes.FakeLockFactory
    48  	)
    49  
    50  	Context("assign task when", func() {
    51  		BeforeEach(func() {
    52  			logger = lagertest.NewTestLogger("test")
    53  			outputBuffer = new(bytes.Buffer)
    54  			ctx, _ = context.WithCancel(context.Background())
    55  
    56  			fakePool = new(workerfakes.FakePool)
    57  			fakeProvider = new(workerfakes.FakeWorkerProvider)
    58  			fakeCompression = new(compressionfakes.FakeCompression)
    59  			fakeContainerOwner = containerOwnerDummy()
    60  			fakeContainerSpec = workerContainerDummy()
    61  			fakeWorkerSpec = workerSpecDummy()
    62  			fakeStrategy = new(workerfakes.FakeContainerPlacementStrategy)
    63  			fakeMetadata = containerMetadataDummy()
    64  			fakeTaskProcessSpec = processSpecDummy(outputBuffer)
    65  			fakeEventDelegate = new(runtimefakes.FakeStartingEventDelegate)
    66  			fakeLockFactory = new(lockfakes.FakeLockFactory)
    67  			fakeWorker = fakeWorkerStub()
    68  			fakeLock = new(lockfakes.FakeLock)
    69  
    70  			fakeStrategy.ModifiesActiveTasksReturns(true)
    71  			fakeLockFactory.AcquireReturns(fakeLock, true, nil)
    72  		})
    73  
    74  		JustBeforeEach(func() {
    75  			workerInterval := 250 * time.Millisecond
    76  			workerStatusInterval := 500 * time.Millisecond
    77  
    78  			subject = worker.NewClient(
    79  				fakePool,
    80  				fakeProvider,
    81  				fakeCompression,
    82  				workerInterval,
    83  				workerStatusInterval,
    84  				false,
    85  				15*time.Minute)
    86  		})
    87  
    88  		Context("worker is available", func() {
    89  			BeforeEach(func() {
    90  				fakePool.ContainerInWorkerReturns(false, nil)
    91  				fakePool.FindOrChooseWorkerForContainerReturns(fakeWorker, nil)
    92  			})
    93  
    94  			JustBeforeEach(func() {
    95  				taskResult, err = subject.RunTaskStep(ctx,
    96  					logger,
    97  					fakeContainerOwner,
    98  					fakeContainerSpec,
    99  					fakeWorkerSpec,
   100  					fakeStrategy,
   101  					fakeMetadata,
   102  					fakeTaskProcessSpec,
   103  					fakeEventDelegate,
   104  					fakeLockFactory)
   105  			})
   106  
   107  			It("returns result of container process", func() {
   108  				Expect(err).To(BeNil())
   109  				Expect(taskResult).To(Not(BeNil()))
   110  				Expect(taskResult.ExitStatus).To(BeZero())
   111  			})
   112  
   113  			It("releases lock acquired", func() {
   114  				Expect(fakeLock.ReleaseCallCount()).To(Equal(fakeLockFactory.AcquireCallCount()))
   115  			})
   116  
   117  			It("increases the active task count", func() {
   118  				Expect(fakeWorker.IncreaseActiveTasksCallCount()).To(Equal(1))
   119  				Expect(fakeLock.ReleaseCallCount()).To(Equal(fakeLockFactory.AcquireCallCount()))
   120  			})
   121  
   122  			Context("when the container is already present on the worker", func() {
   123  				BeforeEach(func() {
   124  					fakePool.ContainerInWorkerReturns(true, nil)
   125  				})
   126  
   127  				It("does not increase the active task count", func() {
   128  					Expect(fakeWorker.IncreaseActiveTasksCallCount()).To(Equal(0))
   129  					Expect(fakeLock.ReleaseCallCount()).To(Equal(fakeLockFactory.AcquireCallCount()))
   130  				})
   131  
   132  			})
   133  		})
   134  
   135  		Context("waiting for worker to be available", func() {
   136  			BeforeEach(func() {
   137  				fakePool.FindOrChooseWorkerForContainerReturnsOnCall(0, nil, nil)
   138  				fakePool.FindOrChooseWorkerForContainerReturnsOnCall(1, nil, nil)
   139  				fakePool.FindOrChooseWorkerForContainerReturnsOnCall(2, nil, nil)
   140  				fakePool.FindOrChooseWorkerForContainerReturnsOnCall(3, fakeWorker, nil)
   141  			})
   142  
   143  			JustBeforeEach(func() {
   144  				taskResult, err = subject.RunTaskStep(ctx,
   145  					logger,
   146  					fakeContainerOwner,
   147  					fakeContainerSpec,
   148  					fakeWorkerSpec,
   149  					fakeStrategy,
   150  					fakeMetadata,
   151  					fakeTaskProcessSpec,
   152  					fakeEventDelegate,
   153  					fakeLockFactory)
   154  			})
   155  
   156  			It("returns result of container process", func() {
   157  				Expect(err).To(BeNil())
   158  				Expect(taskResult).To(Not(BeNil()))
   159  				Expect(taskResult.ExitStatus).To(BeZero())
   160  			})
   161  
   162  			It("releases lock properly", func() {
   163  				Expect(fakeLock.ReleaseCallCount()).To(Equal(fakeLockFactory.AcquireCallCount()))
   164  			})
   165  
   166  			It("task waiting metrics is gauged", func() {
   167  				labels := metric.TasksWaitingLabels{
   168  					TeamId:     "123",
   169  					WorkerTags: "step_tags",
   170  					Platform:   "some-platform",
   171  				}
   172  
   173  				Expect(metric.Metrics.TasksWaiting).To(HaveKey(labels))
   174  
   175  				// Verify that when one task is waiting the gauge is increased...
   176  				Eventually(metric.Metrics.TasksWaiting[labels].Max(), 2*time.Second).Should(Equal(float64(1)))
   177  				// and then decreased.
   178  				Eventually(metric.Metrics.TasksWaiting[labels].Max(), 2*time.Second).Should(Equal(float64(0)))
   179  			})
   180  
   181  			It("writes status to output writer", func() {
   182  				output := outputBuffer.String()
   183  				Expect(output).To(ContainSubstring("All workers are busy at the moment, please stand-by.\n"))
   184  				Expect(output).To(ContainSubstring("Found a free worker after waiting"))
   185  			})
   186  		})
   187  	})
   188  })
   189  
   190  func processSpecDummy(outputBuffer *bytes.Buffer) runtime.ProcessSpec {
   191  	return runtime.ProcessSpec{
   192  		Path:         "/some/path",
   193  		Args:         []string{"some", "args"},
   194  		Dir:          "/some/dir",
   195  		StdoutWriter: outputBuffer,
   196  		StderrWriter: new(bytes.Buffer),
   197  	}
   198  }
   199  
   200  func containerMetadataDummy() db.ContainerMetadata {
   201  	return db.ContainerMetadata{
   202  		WorkingDirectory: "some-artifact-root",
   203  		Type:             db.ContainerTypeTask,
   204  		StepName:         "some-step",
   205  	}
   206  }
   207  
   208  func workerContainerDummy() worker.ContainerSpec {
   209  	cpu := uint64(1024)
   210  	memory := uint64(1024)
   211  
   212  	return worker.ContainerSpec{
   213  		TeamID: 123,
   214  		ImageSpec: worker.ImageSpec{
   215  			ImageArtifactSource: new(workerfakes.FakeStreamableArtifactSource),
   216  			Privileged:          false,
   217  		},
   218  		Limits: worker.ContainerLimits{
   219  			CPU:    &cpu,
   220  			Memory: &memory,
   221  		},
   222  		Dir:            "some-artifact-root",
   223  		Env:            []string{"SECURE=secret-task-param"},
   224  		ArtifactByPath: map[string]runtime.Artifact{},
   225  		Inputs:         []worker.InputSource{},
   226  		Outputs:        worker.OutputPaths{},
   227  	}
   228  }
   229  
   230  func workerSpecDummy() worker.WorkerSpec {
   231  	return worker.WorkerSpec{
   232  		TeamID:   123,
   233  		Platform: "some-platform",
   234  		Tags:     []string{"step", "tags"},
   235  	}
   236  }
   237  
   238  func containerOwnerDummy() db.ContainerOwner {
   239  	return db.NewBuildStepContainerOwner(
   240  		1234,
   241  		atc.PlanID("42"),
   242  		123,
   243  	)
   244  }
   245  
   246  func fakeWorkerStub() *workerfakes.FakeWorker {
   247  	fakeContainer := new(workerfakes.FakeContainer)
   248  	fakeContainer.PropertiesReturns(garden.Properties{"concourse:exit-status": "0"}, nil)
   249  
   250  	fakeWorker := new(workerfakes.FakeWorker)
   251  	fakeWorker.NameReturns("some-worker")
   252  	fakeWorker.SatisfiesReturns(true)
   253  	fakeWorker.FindOrCreateContainerReturns(fakeContainer, nil)
   254  
   255  	fakeWorker.IncreaseActiveTasksStub = func() error {
   256  		fakeWorker.ActiveTasksReturns(1, nil)
   257  		return nil
   258  	}
   259  	fakeWorker.DecreaseActiveTasksStub = func() error {
   260  		fakeWorker.ActiveTasksReturns(0, nil)
   261  		return nil
   262  	}
   263  	return fakeWorker
   264  }