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

     1  package worker
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"sync"
     7  	"time"
     8  
     9  	"code.cloudfoundry.org/garden"
    10  	"code.cloudfoundry.org/lager"
    11  	"code.cloudfoundry.org/lager/lagerctx"
    12  	"github.com/chenbh/concourse/v6/atc/worker/gclient"
    13  )
    14  
    15  // ContainerSweeper is an ifrit.Runner that periodically reports and
    16  // garbage-collects a worker's containers
    17  type ContainerSweeper struct {
    18  	logger       lager.Logger
    19  	interval     time.Duration
    20  	tsaClient    TSAClient
    21  	gardenClient gclient.Client
    22  	maxInFlight  uint16
    23  }
    24  
    25  func NewContainerSweeper(
    26  	logger lager.Logger,
    27  	sweepInterval time.Duration,
    28  	tsaClient TSAClient,
    29  	gardenClient gclient.Client,
    30  	maxInFlight uint16,
    31  ) *ContainerSweeper {
    32  	return &ContainerSweeper{
    33  		logger:       logger,
    34  		interval:     sweepInterval,
    35  		tsaClient:    tsaClient,
    36  		gardenClient: gardenClient,
    37  		maxInFlight:  maxInFlight,
    38  	}
    39  }
    40  
    41  func (sweeper *ContainerSweeper) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
    42  	timer := time.NewTicker(sweeper.interval)
    43  
    44  	close(ready)
    45  
    46  	for {
    47  		select {
    48  		case <-timer.C:
    49  			sweeper.sweep(sweeper.logger.Session("tick"))
    50  
    51  		case sig := <-signals:
    52  			sweeper.logger.Info("sweep-cancelled-by-signal", lager.Data{"signal": sig})
    53  			return nil
    54  		}
    55  	}
    56  }
    57  
    58  func (sweeper *ContainerSweeper) sweep(logger lager.Logger) {
    59  	ctx := lagerctx.NewContext(context.Background(), logger)
    60  
    61  	containers, err := sweeper.gardenClient.Containers(garden.Properties{})
    62  	if err != nil {
    63  		logger.Error("failed-to-list-containers", err)
    64  	} else {
    65  		handles := []string{}
    66  		for _, container := range containers {
    67  			handles = append(handles, container.Handle())
    68  		}
    69  
    70  		err := sweeper.tsaClient.ReportContainers(ctx, handles)
    71  		if err != nil {
    72  			logger.Error("failed-to-report-containers", err)
    73  		}
    74  	}
    75  
    76  	containerHandles, err := sweeper.tsaClient.ContainersToDestroy(ctx)
    77  	if err != nil {
    78  		logger.Error("failed-to-get-containers-to-destroy", err)
    79  	} else {
    80  		var wg sync.WaitGroup
    81  		maxInFlight := make(chan int, sweeper.maxInFlight)
    82  
    83  		for _, handle := range containerHandles {
    84  			maxInFlight <- 1
    85  			wg.Add(1)
    86  
    87  			go func(handle string) {
    88  				err := sweeper.gardenClient.Destroy(handle)
    89  				if err != nil {
    90  					logger.WithData(lager.Data{"handle": handle}).Error("failed-to-destroy-container", err)
    91  				}
    92  				<-maxInFlight
    93  				wg.Done()
    94  			}(handle)
    95  		}
    96  		wg.Wait()
    97  	}
    98  }