github.com/bigcommerce/nomad@v0.9.3-bc/client/fingerprint_manager.go (about)

     1  package client
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	log "github.com/hashicorp/go-hclog"
     8  	"github.com/hashicorp/nomad/client/config"
     9  	"github.com/hashicorp/nomad/client/fingerprint"
    10  	"github.com/hashicorp/nomad/helper/pluginutils/loader"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  )
    13  
    14  // FingerprintManager runs a client fingerprinters on a continuous basis, and
    15  // updates the client when the node has changed
    16  type FingerprintManager struct {
    17  	singletonLoader loader.PluginCatalog
    18  	getConfig       func() *config.Config
    19  	node            *structs.Node
    20  	nodeLock        sync.Mutex
    21  	shutdownCh      chan struct{}
    22  
    23  	// updateNodeAttributes is a callback to the client to update the state of its
    24  	// associated node
    25  	updateNodeAttributes func(*fingerprint.FingerprintResponse) *structs.Node
    26  
    27  	logger log.Logger
    28  }
    29  
    30  // NewFingerprintManager is a constructor that creates and returns an instance
    31  // of FingerprintManager
    32  func NewFingerprintManager(
    33  	singletonLoader loader.PluginCatalog,
    34  	getConfig func() *config.Config,
    35  	node *structs.Node,
    36  	shutdownCh chan struct{},
    37  	updateNodeAttributes func(*fingerprint.FingerprintResponse) *structs.Node,
    38  	logger log.Logger) *FingerprintManager {
    39  
    40  	return &FingerprintManager{
    41  		singletonLoader:      singletonLoader,
    42  		getConfig:            getConfig,
    43  		updateNodeAttributes: updateNodeAttributes,
    44  		node:                 node,
    45  		shutdownCh:           shutdownCh,
    46  		logger:               logger.Named("fingerprint_mgr"),
    47  	}
    48  }
    49  
    50  // setNode updates the current client node
    51  func (fm *FingerprintManager) setNode(node *structs.Node) {
    52  	fm.nodeLock.Lock()
    53  	defer fm.nodeLock.Unlock()
    54  	fm.node = node
    55  }
    56  
    57  // getNode returns the current client node
    58  func (fm *FingerprintManager) getNode() *structs.Node {
    59  	fm.nodeLock.Lock()
    60  	defer fm.nodeLock.Unlock()
    61  	return fm.node
    62  }
    63  
    64  // Run starts the process of fingerprinting the node. It does an initial pass,
    65  // identifying whitelisted and blacklisted fingerprints/drivers. Then, for
    66  // those which require periotic checking, it starts a periodic process for
    67  // each.
    68  func (fp *FingerprintManager) Run() error {
    69  	// First, set up all fingerprints
    70  	cfg := fp.getConfig()
    71  	whitelistFingerprints := cfg.ReadStringListToMap("fingerprint.whitelist")
    72  	whitelistFingerprintsEnabled := len(whitelistFingerprints) > 0
    73  	blacklistFingerprints := cfg.ReadStringListToMap("fingerprint.blacklist")
    74  
    75  	fp.logger.Debug("built-in fingerprints", "fingerprinters", fingerprint.BuiltinFingerprints())
    76  
    77  	var availableFingerprints []string
    78  	var skippedFingerprints []string
    79  	for _, name := range fingerprint.BuiltinFingerprints() {
    80  		// Skip modules that are not in the whitelist if it is enabled.
    81  		if _, ok := whitelistFingerprints[name]; whitelistFingerprintsEnabled && !ok {
    82  			skippedFingerprints = append(skippedFingerprints, name)
    83  			continue
    84  		}
    85  		// Skip modules that are in the blacklist
    86  		if _, ok := blacklistFingerprints[name]; ok {
    87  			skippedFingerprints = append(skippedFingerprints, name)
    88  			continue
    89  		}
    90  
    91  		availableFingerprints = append(availableFingerprints, name)
    92  	}
    93  
    94  	if err := fp.setupFingerprinters(availableFingerprints); err != nil {
    95  		return err
    96  	}
    97  
    98  	if len(skippedFingerprints) != 0 {
    99  		fp.logger.Debug("fingerprint modules skipped due to white/blacklist",
   100  			"skipped_fingerprinters", skippedFingerprints)
   101  	}
   102  
   103  	return nil
   104  }
   105  
   106  // setupFingerprints is used to fingerprint the node to see if these attributes are
   107  // supported
   108  func (fm *FingerprintManager) setupFingerprinters(fingerprints []string) error {
   109  	var appliedFingerprints []string
   110  
   111  	for _, name := range fingerprints {
   112  		f, err := fingerprint.NewFingerprint(name, fm.logger)
   113  
   114  		if err != nil {
   115  			fm.logger.Error("error fingerprinting", "error", err, "fingerprinter", name)
   116  			return err
   117  		}
   118  
   119  		detected, err := fm.fingerprint(name, f)
   120  		if err != nil {
   121  			return err
   122  		}
   123  
   124  		// log the fingerprinters which have been applied
   125  		if detected {
   126  			appliedFingerprints = append(appliedFingerprints, name)
   127  		}
   128  
   129  		p, period := f.Periodic()
   130  		if p {
   131  			go fm.runFingerprint(f, period, name)
   132  		}
   133  	}
   134  
   135  	fm.logger.Debug("detected fingerprints", "node_attrs", appliedFingerprints)
   136  	return nil
   137  }
   138  
   139  // runFingerprint runs each fingerprinter individually on an ongoing basis
   140  func (fm *FingerprintManager) runFingerprint(f fingerprint.Fingerprint, period time.Duration, name string) {
   141  	fm.logger.Debug("fingerprinting periodically", "fingerprinter", name, "period", period)
   142  
   143  	timer := time.NewTimer(period)
   144  	defer timer.Stop()
   145  
   146  	for {
   147  		select {
   148  		case <-timer.C:
   149  			timer.Reset(period)
   150  
   151  			_, err := fm.fingerprint(name, f)
   152  			if err != nil {
   153  				fm.logger.Debug("error periodic fingerprinting", "error", err, "fingerprinter", name)
   154  				continue
   155  			}
   156  
   157  		case <-fm.shutdownCh:
   158  			return
   159  		}
   160  	}
   161  }
   162  
   163  // fingerprint does an initial fingerprint of the client. If the fingerprinter
   164  // is meant to be run continuously, a process is launched to perform this
   165  // fingerprint on an ongoing basis in the background.
   166  func (fm *FingerprintManager) fingerprint(name string, f fingerprint.Fingerprint) (bool, error) {
   167  	var response fingerprint.FingerprintResponse
   168  
   169  	fm.nodeLock.Lock()
   170  	request := &fingerprint.FingerprintRequest{Config: fm.getConfig(), Node: fm.node}
   171  	err := f.Fingerprint(request, &response)
   172  	fm.nodeLock.Unlock()
   173  
   174  	if err != nil {
   175  		return false, err
   176  	}
   177  
   178  	if node := fm.updateNodeAttributes(&response); node != nil {
   179  		fm.setNode(node)
   180  	}
   181  
   182  	return response.Detected, nil
   183  }