github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/topgun/both/worker_landing_test.go (about)

     1  package topgun_test
     2  
     3  import (
     4  	"os"
     5  	"regexp"
     6  	"strings"
     7  	"time"
     8  
     9  	. "github.com/pf-qiu/concourse/v6/topgun/common"
    10  	_ "github.com/lib/pq"
    11  	. "github.com/onsi/ginkgo"
    12  	. "github.com/onsi/gomega"
    13  	"github.com/onsi/gomega/gbytes"
    14  	"github.com/onsi/gomega/gexec"
    15  )
    16  
    17  var _ = Describe("Worker landing", func() {
    18  	landWorker := func() (string, BoshInstance) {
    19  		workerToLand := FlyTable("workers")[0]["name"]
    20  
    21  		// the bosh release ensures the first guid segment matches the first guid
    22  		// segment of the instance ID, so that they can be correlated
    23  		guidSegments := strings.Split(workerToLand, "-")
    24  		prefix := guidSegments[0]
    25  
    26  		var instance BoshInstance
    27  		for _, i := range JobInstances("worker") {
    28  			if strings.HasPrefix(i.ID, prefix) {
    29  				instance = i
    30  				break
    31  			}
    32  		}
    33  
    34  		Expect(instance.ID).ToNot(BeEmpty(), "should have found a corresponding bosh instance")
    35  
    36  		// unmonitor worker, otherwise monit will just restart it once it's landed
    37  		Bosh("ssh", instance.Name, "-c", "sudo /var/vcap/bosh/bin/monit unmonitor worker")
    38  
    39  		// land worker via fly; this will cause the worker process to exit
    40  		Fly.Run("land-worker", "-w", workerToLand)
    41  
    42  		return workerToLand, instance
    43  	}
    44  
    45  	startLandedWorker := func(instance BoshInstance) {
    46  		Bosh("ssh", instance.Name, "-c", "sudo /var/vcap/bosh/bin/monit monitor worker")
    47  		Bosh("ssh", instance.Name, "-c", "sudo /var/vcap/bosh/bin/monit start worker")
    48  	}
    49  
    50  	Context("with two workers available", func() {
    51  		BeforeEach(func() {
    52  			Deploy(
    53  				"deployments/concourse.yml",
    54  				"-o", "operations/worker-instances.yml",
    55  				"-v", "worker_instances=2",
    56  			)
    57  		})
    58  
    59  		Describe("landing the worker", func() {
    60  			var landingWorkerName string
    61  			var landingWorkerInstance BoshInstance
    62  
    63  			JustBeforeEach(func() {
    64  				landingWorkerName, landingWorkerInstance = landWorker()
    65  			})
    66  
    67  			AfterEach(func() {
    68  				startLandedWorker(landingWorkerInstance)
    69  			})
    70  
    71  			Context("while in landing or landed state", func() {
    72  				It("is not used for new workloads", func() {
    73  					for i := 0; i < 10; i++ {
    74  						Fly.Run("execute", "-c", "tasks/tiny.yml")
    75  						usedWorkers := WorkersWithContainers()
    76  						Expect(usedWorkers).To(HaveLen(1))
    77  						Expect(usedWorkers).ToNot(ContainElement(landingWorkerName))
    78  					}
    79  				})
    80  
    81  				It("can be pruned", func() {
    82  					Fly.Run("prune-worker", "-w", landingWorkerName)
    83  					WaitForWorkersToBeRunning(1)
    84  				})
    85  			})
    86  		})
    87  	})
    88  
    89  	describeLandingTheWorker := func() {
    90  		Describe("landing the worker", func() {
    91  			var landingWorkerName string
    92  			var landingWorkerInstance BoshInstance
    93  
    94  			JustBeforeEach(func() {
    95  				landingWorkerName, landingWorkerInstance = landWorker()
    96  			})
    97  
    98  			AfterEach(func() {
    99  				startLandedWorker(landingWorkerInstance)
   100  			})
   101  
   102  			Context("with volumes and containers present", func() {
   103  				var preservedContainerID string
   104  
   105  				BeforeEach(func() {
   106  					By("setting pipeline that creates volumes for image")
   107  					Fly.Run("set-pipeline", "-n", "-c", "pipelines/get-task.yml", "-p", "topgun")
   108  
   109  					By("unpausing the pipeline")
   110  					Fly.Run("unpause-pipeline", "-p", "topgun")
   111  
   112  					By("triggering a job")
   113  					buildSession := Fly.Start("trigger-job", "-w", "-j", "topgun/simple-job")
   114  					Eventually(buildSession).Should(gbytes.Say("mirroring self image"))
   115  					<-buildSession.Exited
   116  					Expect(buildSession.ExitCode()).To(Equal(0))
   117  
   118  					By("getting identifier for check container")
   119  					hijackSession := Fly.Start("hijack", "-c", "topgun/tick-tock", "--", "hostname")
   120  					<-hijackSession.Exited
   121  					Expect(buildSession.ExitCode()).To(Equal(0))
   122  
   123  					preservedContainerID = string(hijackSession.Out.Contents())
   124  				})
   125  
   126  				It("keeps volumes and containers after restart", func() {
   127  					By("starting the worker back up")
   128  					WaitForLandedWorker()
   129  					startLandedWorker(landingWorkerInstance)
   130  					WaitForWorkersToBeRunning(1)
   131  
   132  					By("retaining cached image resource in second job build")
   133  					buildSession := Fly.Start("trigger-job", "-w", "-j", "topgun/simple-job")
   134  					<-buildSession.Exited
   135  					Expect(buildSession).NotTo(gbytes.Say("mirroring self image"))
   136  					Expect(buildSession.ExitCode()).To(Equal(0))
   137  
   138  					By("retaining check containers")
   139  					hijackSession := Fly.Start("hijack", "-c", "topgun/tick-tock", "--", "hostname")
   140  					<-hijackSession.Exited
   141  					Expect(buildSession.ExitCode()).To(Equal(0))
   142  
   143  					currentContainerID := string(hijackSession.Out.Contents())
   144  					Expect(currentContainerID).To(Equal(preservedContainerID))
   145  				})
   146  			})
   147  
   148  			Context("with an interruptible build in-flight", func() {
   149  				var buildSession *gexec.Session
   150  
   151  				BeforeEach(func() {
   152  					By("setting pipeline that has an infinite but interruptible job")
   153  					Fly.Run("set-pipeline", "-n", "-c", "pipelines/interruptible.yml", "-p", "topgun")
   154  
   155  					By("unpausing the pipeline")
   156  					Fly.Run("unpause-pipeline", "-p", "topgun")
   157  
   158  					By("triggering a job")
   159  					buildSession = Fly.Start("trigger-job", "-w", "-j", "topgun/interruptible-job")
   160  					Eventually(buildSession).Should(gbytes.Say("waiting forever"))
   161  				})
   162  
   163  				It("does not wait for the build", func() {
   164  					By("landing without the drain timeout kicking in")
   165  					WaitForLandedWorker()
   166  				})
   167  			})
   168  
   169  			Context("with uninterruptible build in-flight", func() {
   170  				var buildSession *gexec.Session
   171  				var buildID string
   172  
   173  				BeforeEach(func() {
   174  					buildSession = Fly.Start("execute", "-c", "tasks/wait.yml")
   175  					Eventually(buildSession).Should(gbytes.Say("executing build"))
   176  
   177  					buildRegex := regexp.MustCompile(`executing build (\d+)`)
   178  					matches := buildRegex.FindSubmatch(buildSession.Out.Contents())
   179  					buildID = string(matches[1])
   180  
   181  					Eventually(buildSession).Should(gbytes.Say("waiting for /tmp/stop-waiting"))
   182  				})
   183  
   184  				AfterEach(func() {
   185  					buildSession.Signal(os.Interrupt)
   186  					<-buildSession.Exited
   187  				})
   188  
   189  				It("waits for the build", func() {
   190  					Consistently(func() string {
   191  						return WorkerState(landingWorkerName)
   192  					}, 5*time.Minute).Should(Equal("landing"))
   193  				})
   194  
   195  				It("finishes landing once the build is done", func() {
   196  					By("hijacking the build to tell it to finish")
   197  					Eventually(func() int {
   198  						hijackSession := Fly.Start(
   199  							"hijack",
   200  							"-b", buildID,
   201  							"-s", "one-off", "--",
   202  							"touch", "/tmp/stop-waiting",
   203  						)
   204  						<-hijackSession.Exited
   205  						return hijackSession.ExitCode()
   206  					}).Should(Equal(0))
   207  
   208  					By("waiting for the build to exit")
   209  					Eventually(buildSession).Should(gbytes.Say("done"))
   210  					<-buildSession.Exited
   211  					Expect(buildSession.ExitCode()).To(Equal(0))
   212  
   213  					By("successfully landing")
   214  					WaitForLandedWorker()
   215  				})
   216  			})
   217  		})
   218  	}
   219  
   220  	Context("with one worker", func() {
   221  		BeforeEach(func() {
   222  			Deploy("deployments/concourse.yml")
   223  			WaitForRunningWorker()
   224  		})
   225  
   226  		describeLandingTheWorker()
   227  	})
   228  
   229  	//TODO: Un-pend this Context when team workers can run check containers
   230  	// see: - https://github.com/pf-qiu/concourse/v6/issues/2910
   231  	//      - https://github.com/pf-qiu/concourse/v6/issues/2951
   232  	XContext("with a single team worker", func() {
   233  		BeforeEach(func() {
   234  			Deploy(
   235  				"deployments/concourse.yml",
   236  				"-o", "operations/worker-instances.yml",
   237  				"-v", "worker_instances=0",
   238  			)
   239  
   240  			Fly.Run("set-team", "--non-interactive", "-n", "team-a", "--local-user", AtcUsername)
   241  
   242  			Deploy(
   243  				"deployments/concourse.yml",
   244  				"-o", "operations/worker-team.yml",
   245  			)
   246  
   247  			Fly.Run("login", "-c", AtcExternalURL, "-n", "team-a", "-u", AtcUsername, "-p", AtcPassword)
   248  
   249  			// wait for the team's worker to arrive now that team exists
   250  			WaitForRunningWorker()
   251  		})
   252  
   253  		describeLandingTheWorker()
   254  	})
   255  })