bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/pkg/acquisition/acquisition.go (about)

     1  package acquisition
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  
     8  	"bitbucket.org/Aishee/synsec/pkg/csconfig"
     9  	"bitbucket.org/Aishee/synsec/pkg/types"
    10  	"github.com/pkg/errors"
    11  	"github.com/prometheus/client_golang/prometheus"
    12  	log "github.com/sirupsen/logrus"
    13  	"gopkg.in/yaml.v2"
    14  
    15  	tomb "gopkg.in/tomb.v2"
    16  )
    17  
    18  var ReaderHits = prometheus.NewCounterVec(
    19  	prometheus.CounterOpts{
    20  		Name: "cs_reader_hits_total",
    21  		Help: "Total lines where read.",
    22  	},
    23  	[]string{"source"},
    24  )
    25  
    26  /*
    27   current limits :
    28   - The acquisition is not yet modular (cf. traefik/yaegi), but we start with an interface to pave the road for it.
    29   - The configuration item unmarshaled (DataSourceCfg) isn't generic neither yet.
    30   - This changes should be made when we're ready to have acquisition managed by the hub & ccscli
    31   once this change is done, we might go for the following configuration format instead :
    32     ```yaml
    33     ---
    34     type: nginx
    35     source: journald
    36     filter: "PROG=nginx"
    37     ---
    38     type: nginx
    39     source: files
    40     filenames:
    41  	- "/var/log/nginx/*.log"
    42  	```
    43  */
    44  
    45  /* Approach
    46  
    47  We support acquisition in two modes :
    48   - tail mode : we're following a stream of info (tail -f $src). this is used when monitoring live logs
    49   - cat mode : we're reading a file/source one-shot (cat $src), and scenarios will match the timestamp extracted from logs.
    50  
    51  One DataSourceCfg can lead to multiple goroutines, hence the Tombs passing around to allow proper tracking.
    52  tail mode shouldn't return except on errors or when externally killed via tombs.
    53  cat mode will return once source has been exhausted.
    54  
    55  
    56   TBD in current iteration :
    57    - how to deal with "file was not present at startup but might appear later" ?
    58  */
    59  
    60  var TAIL_MODE = "tail"
    61  var CAT_MODE = "cat"
    62  
    63  type DataSourceCfg struct {
    64  	Mode              string            `yaml:"mode,omitempty"` //tail|cat|...
    65  	Filename          string            `yaml:"filename,omitempty"`
    66  	Filenames         []string          `yaml:"filenames,omitempty"`
    67  	JournalctlFilters []string          `yaml:"journalctl_filter,omitempty"`
    68  	Labels            map[string]string `yaml:"labels,omitempty"`
    69  	Profiling         bool              `yaml:"profiling,omitempty"`
    70  }
    71  
    72  type DataSource interface {
    73  	Configure(DataSourceCfg) error
    74  	/*the readers must watch the tomb (especially in tail mode) to know when to shutdown.
    75  	tomb is as well used to trigger general shutdown when a datasource errors */
    76  	StartReading(chan types.Event, *tomb.Tomb) error
    77  	Mode() string //return CAT_MODE or TAIL_MODE
    78  	//Not sure it makes sense to make those funcs part of the interface.
    79  	//While 'cat' and 'tail' are the only two modes we see now, other modes might appear
    80  	//StartTail(chan types.Event, *tomb.Tomb) error
    81  	//StartCat(chan types.Event, *tomb.Tomb) error
    82  }
    83  
    84  func DataSourceConfigure(config DataSourceCfg) (DataSource, error) {
    85  	if config.Mode == "" { /*default mode is tail*/
    86  		config.Mode = TAIL_MODE
    87  	}
    88  
    89  	if len(config.Filename) > 0 || len(config.Filenames) > 0 { /*it's file acquisition*/
    90  
    91  		fileSrc := new(FileSource)
    92  		if err := fileSrc.Configure(config); err != nil {
    93  			return nil, errors.Wrap(err, "configuring file datasource")
    94  		}
    95  		return fileSrc, nil
    96  	} else if len(config.JournalctlFilters) > 0 { /*it's journald acquisition*/
    97  
    98  		journaldSrc := new(JournaldSource)
    99  		if err := journaldSrc.Configure(config); err != nil {
   100  			return nil, errors.Wrap(err, "configuring journald datasource")
   101  		}
   102  		return journaldSrc, nil
   103  	} else {
   104  		return nil, fmt.Errorf("empty filename(s) and journalctl filter, malformed datasource")
   105  	}
   106  }
   107  
   108  func LoadAcquisitionFromFile(config *csconfig.SynsecServiceCfg) ([]DataSource, error) {
   109  
   110  	var sources []DataSource
   111  	var acquisSources = config.AcquisitionFiles
   112  
   113  	for _, acquisFile := range acquisSources {
   114  		log.Infof("loading acquisition file : %s", acquisFile)
   115  		yamlFile, err := os.Open(acquisFile)
   116  		if err != nil {
   117  			return nil, errors.Wrapf(err, "can't open %s", acquisFile)
   118  		}
   119  		dec := yaml.NewDecoder(yamlFile)
   120  		dec.SetStrict(true)
   121  		for {
   122  			sub := DataSourceCfg{}
   123  			err = dec.Decode(&sub)
   124  			if err != nil {
   125  				if err == io.EOF {
   126  					log.Tracef("End of yaml file")
   127  					break
   128  				}
   129  				return nil, errors.Wrap(err, fmt.Sprintf("failed to yaml decode %s", acquisFile))
   130  			}
   131  			src, err := DataSourceConfigure(sub)
   132  			if err != nil {
   133  				log.Warningf("while configuring datasource : %s", err)
   134  				continue
   135  			}
   136  			sources = append(sources, src)
   137  		}
   138  	}
   139  
   140  	return sources, nil
   141  }
   142  
   143  func StartAcquisition(sources []DataSource, output chan types.Event, AcquisTomb *tomb.Tomb) error {
   144  
   145  	for i := 0; i < len(sources); i++ {
   146  		subsrc := sources[i] //ensure its a copy
   147  		log.Debugf("starting one source %d/%d ->> %T", i, len(sources), subsrc)
   148  		AcquisTomb.Go(func() error {
   149  			defer types.CatchPanic("synsec/acquis")
   150  			if err := subsrc.StartReading(output, AcquisTomb); err != nil {
   151  				return err
   152  			}
   153  			return nil
   154  		})
   155  	}
   156  	/*return only when acquisition is over (cat) or never (tail)*/
   157  	err := AcquisTomb.Wait()
   158  	return err
   159  }