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

     1  package syslog
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"time"
     7  
     8  	"code.cloudfoundry.org/lager"
     9  	"code.cloudfoundry.org/lager/lagerctx"
    10  	"github.com/pf-qiu/concourse/v6/atc/db"
    11  	"github.com/pf-qiu/concourse/v6/atc/event"
    12  )
    13  
    14  //go:generate counterfeiter . Drainer
    15  
    16  type Drainer interface {
    17  	Run(context.Context) error
    18  }
    19  
    20  type drainer struct {
    21  	hostname     string
    22  	transport    string
    23  	address      string
    24  	caCerts      []string
    25  	buildFactory db.BuildFactory
    26  }
    27  
    28  func NewDrainer(transport string, address string, hostname string, caCerts []string, buildFactory db.BuildFactory) Drainer {
    29  	return &drainer{
    30  		hostname:     hostname,
    31  		transport:    transport,
    32  		address:      address,
    33  		buildFactory: buildFactory,
    34  		caCerts:      caCerts,
    35  	}
    36  }
    37  
    38  func (d *drainer) Run(ctx context.Context) error {
    39  	logger := lagerctx.FromContext(ctx).Session("syslog")
    40  
    41  	builds, err := d.buildFactory.GetDrainableBuilds()
    42  	if err != nil {
    43  		logger.Error("failed-to-get-drainable-builds", err)
    44  		return err
    45  	}
    46  
    47  	if len(builds) > 0 {
    48  		syslog, err := Dial(d.transport, d.address, d.caCerts)
    49  		if err != nil {
    50  			logger.Error("failed-to-connect", err)
    51  			return err
    52  		}
    53  
    54  		// ignore any errors coming from syslog.Close()
    55  		defer db.Close(syslog)
    56  
    57  		for _, build := range builds {
    58  			err := d.drainBuild(logger, build, syslog)
    59  			if err != nil {
    60  				return err
    61  			}
    62  		}
    63  	}
    64  	return nil
    65  }
    66  
    67  func (d *drainer) drainBuild(logger lager.Logger, build db.Build, syslog *Syslog) error {
    68  	logger = logger.Session("drain-build", build.LagerData())
    69  
    70  	events, err := build.Events(0)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	// ignore any errors coming from events.Close()
    76  	defer db.Close(events)
    77  
    78  	for {
    79  		ev, err := events.Next()
    80  		if err != nil {
    81  			if err == db.ErrEndOfBuildEventStream {
    82  				break
    83  			}
    84  			logger.Error("failed-to-get-next-event", err)
    85  			return err
    86  		}
    87  
    88  		if ev.Event == event.EventTypeLog {
    89  			var log event.Log
    90  
    91  			err := json.Unmarshal(*ev.Data, &log)
    92  			if err != nil {
    93  				logger.Error("failed-to-unmarshal", err)
    94  				return err
    95  			}
    96  
    97  			err = syslog.Write(
    98  				d.hostname,
    99  				build.SyslogTag(log.Origin.ID),
   100  				time.Unix(log.Time, 0),
   101  				log.Payload,
   102  			)
   103  			if err != nil {
   104  				logger.Error("failed-to-write-to-server", err)
   105  				return err
   106  			}
   107  		}
   108  	}
   109  
   110  	err = build.SetDrained(true)
   111  	if err != nil {
   112  		logger.Error("failed-to-update-status", err)
   113  		return err
   114  	}
   115  
   116  	return nil
   117  }