github.com/chenbh/concourse/v6@v6.4.2/worker/container_sweeper_test.go (about)

     1  package worker_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"os"
     8  	"time"
     9  
    10  	"code.cloudfoundry.org/lager/lagertest"
    11  	"github.com/chenbh/concourse/v6/atc/worker/gclient"
    12  	"github.com/chenbh/concourse/v6/worker"
    13  	"github.com/chenbh/concourse/v6/worker/workerfakes"
    14  
    15  	. "github.com/onsi/ginkgo"
    16  	. "github.com/onsi/gomega"
    17  	"github.com/onsi/gomega/gbytes"
    18  	"github.com/onsi/gomega/ghttp"
    19  )
    20  
    21  var _ = Describe("Container Sweeper", func() {
    22  	const (
    23  		sweepInterval              = 1 * time.Second
    24  		maxInFlight                = uint16(1)
    25  		gardenClientTimeoutRequest = 5 * time.Millisecond
    26  	)
    27  
    28  	var (
    29  		garden *ghttp.Server
    30  
    31  		testLogger = lagertest.NewTestLogger("container-sweeper")
    32  
    33  		fakeTSAClient workerfakes.FakeTSAClient
    34  		gardenClient  gclient.Client
    35  
    36  		sweeper *worker.ContainerSweeper
    37  
    38  		osSignal chan os.Signal
    39  		exited   chan struct{}
    40  	)
    41  
    42  	BeforeEach(func() {
    43  		garden = ghttp.NewServer()
    44  
    45  		osSignal = make(chan os.Signal)
    46  		exited = make(chan struct{})
    47  
    48  		gardenAddr := fmt.Sprintf("http://%s", garden.Addr())
    49  		gardenClient = gclient.BasicGardenClientWithRequestTimeout(testLogger, gardenClientTimeoutRequest, gardenAddr)
    50  
    51  		fakeTSAClient = workerfakes.FakeTSAClient{}
    52  		fakeTSAClient.ReportContainersReturns(nil)
    53  
    54  		sweeper = worker.NewContainerSweeper(testLogger, sweepInterval, &fakeTSAClient, gardenClient, maxInFlight)
    55  
    56  	})
    57  
    58  	JustBeforeEach(func() {
    59  		go func() {
    60  			_ = sweeper.Run(osSignal, make(chan struct{}))
    61  			close(exited)
    62  		}()
    63  	})
    64  
    65  	AfterEach(func() {
    66  		close(osSignal)
    67  		<-exited
    68  		garden.Close()
    69  	})
    70  
    71  	Context("when garden doesn't respond on DELETE", func() {
    72  		var (
    73  			gardenContext context.Context
    74  			gardenCancel  context.CancelFunc
    75  		)
    76  
    77  		BeforeEach(func() {
    78  			gardenContext, gardenCancel = context.WithCancel(context.Background())
    79  			garden.AppendHandlers(
    80  				ghttp.CombineHandlers(
    81  					ghttp.VerifyRequest("GET", "/containers"),
    82  					ghttp.RespondWithJSONEncoded(200, []map[string]string{}),
    83  				),
    84  				ghttp.CombineHandlers(
    85  					ghttp.VerifyRequest("DELETE", "/containers/some-handle-1"),
    86  					func(w http.ResponseWriter, r *http.Request) {
    87  						<-gardenContext.Done()
    88  					},
    89  				),
    90  				ghttp.CombineHandlers(
    91  					ghttp.VerifyRequest("DELETE", "/containers/some-handle-2"),
    92  					func(w http.ResponseWriter, r *http.Request) {
    93  						<-gardenContext.Done()
    94  					},
    95  				),
    96  				ghttp.CombineHandlers(
    97  					ghttp.VerifyRequest("GET", "/containers"),
    98  					ghttp.RespondWithJSONEncoded(200, []map[string]string{}),
    99  				),
   100  				ghttp.CombineHandlers(
   101  					ghttp.VerifyRequest("DELETE", "/containers/some-handle-3"),
   102  					func(w http.ResponseWriter, r *http.Request) {
   103  						<-gardenContext.Done()
   104  					},
   105  				),
   106  				ghttp.CombineHandlers(
   107  					ghttp.VerifyRequest("DELETE", "/containers/some-handle-4"),
   108  					func(w http.ResponseWriter, r *http.Request) {
   109  						<-gardenContext.Done()
   110  					},
   111  				),
   112  			)
   113  			// First GC Tick
   114  			fakeTSAClient.ContainersToDestroyReturnsOnCall(0, []string{"some-handle-1", "some-handle-2"}, nil)
   115  			// Second GC Tick
   116  			fakeTSAClient.ContainersToDestroyReturnsOnCall(1, []string{"some-handle-3", "some-handle-4"}, nil)
   117  
   118  			garden.AllowUnhandledRequests = true
   119  
   120  		})
   121  		AfterEach(func() {
   122  			gardenCancel()
   123  		})
   124  
   125  		It("request to garden times out eventually", func() {
   126  			Eventually(testLogger.Buffer()).Should(gbytes.Say("failed-to-destroy-container\".*\\(Client.Timeout exceeded while awaiting headers\\)"))
   127  		})
   128  		It("sweeper continues ticking and GC'ing", func() {
   129  			// ensure all 4 DELETEs are issues over 2 successive ticks
   130  			Eventually(func() []string {
   131  				// Gather all containers deleted
   132  				var deleteRequestPaths []string
   133  				for _, req := range garden.ReceivedRequests() {
   134  					if req.Method == http.MethodDelete {
   135  						deleteRequestPaths = append(deleteRequestPaths, req.RequestURI)
   136  					}
   137  				}
   138  				return deleteRequestPaths
   139  			}).Should(ConsistOf(
   140  				"/containers/some-handle-1",
   141  				"/containers/some-handle-2",
   142  				"/containers/some-handle-3",
   143  				"/containers/some-handle-4"))
   144  
   145  			// Check calls to TSA for containers to destroy > 1
   146  			Expect(fakeTSAClient.ContainersToDestroyCallCount()).To(BeNumerically(">=", 2))
   147  		})
   148  	})
   149  
   150  })