github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/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  	reloadableFps map[string]fingerprint.ReloadableFingerprint
    28  
    29  	logger log.Logger
    30  }
    31  
    32  // NewFingerprintManager is a constructor that creates and returns an instance
    33  // of FingerprintManager
    34  func NewFingerprintManager(
    35  	singletonLoader loader.PluginCatalog,
    36  	getConfig func() *config.Config,
    37  	node *structs.Node,
    38  	shutdownCh chan struct{},
    39  	updateNodeAttributes func(*fingerprint.FingerprintResponse) *structs.Node,
    40  	logger log.Logger) *FingerprintManager {
    41  
    42  	return &FingerprintManager{
    43  		singletonLoader:      singletonLoader,
    44  		getConfig:            getConfig,
    45  		updateNodeAttributes: updateNodeAttributes,
    46  		node:                 node,
    47  		shutdownCh:           shutdownCh,
    48  		logger:               logger.Named("fingerprint_mgr"),
    49  		reloadableFps:        make(map[string]fingerprint.ReloadableFingerprint),
    50  	}
    51  }
    52  
    53  // setNode updates the current client node
    54  func (fm *FingerprintManager) setNode(node *structs.Node) {
    55  	fm.nodeLock.Lock()
    56  	defer fm.nodeLock.Unlock()
    57  	fm.node = node
    58  }
    59  
    60  // getNode returns the current client node
    61  func (fm *FingerprintManager) getNode() *structs.Node {
    62  	fm.nodeLock.Lock()
    63  	defer fm.nodeLock.Unlock()
    64  	return fm.node
    65  }
    66  
    67  // Run starts the process of fingerprinting the node. It does an initial pass,
    68  // identifying allowlisted and denylisted fingerprints/drivers. Then, for
    69  // those which require periotic checking, it starts a periodic process for
    70  // each.
    71  func (fp *FingerprintManager) Run() error {
    72  	// First, set up all fingerprints
    73  	cfg := fp.getConfig()
    74  	// COMPAT(1.0) using inclusive language, whitelist is kept for backward compatibility.
    75  	allowlistFingerprints := cfg.ReadStringListToMap("fingerprint.allowlist", "fingerprint.whitelist")
    76  	allowlistFingerprintsEnabled := len(allowlistFingerprints) > 0
    77  	// COMPAT(1.0) using inclusive language, blacklist is kept for backward compatibility.
    78  	denylistFingerprints := cfg.ReadStringListToMap("fingerprint.denylist", "fingerprint.blacklist")
    79  
    80  	fp.logger.Debug("built-in fingerprints", "fingerprinters", fingerprint.BuiltinFingerprints())
    81  
    82  	var availableFingerprints []string
    83  	var skippedFingerprints []string
    84  	for _, name := range fingerprint.BuiltinFingerprints() {
    85  		// Skip modules that are not in the allowlist if it is enabled.
    86  		if _, ok := allowlistFingerprints[name]; allowlistFingerprintsEnabled && !ok {
    87  			skippedFingerprints = append(skippedFingerprints, name)
    88  			continue
    89  		}
    90  		// Skip modules that are in the denylist
    91  		if _, ok := denylistFingerprints[name]; ok {
    92  			skippedFingerprints = append(skippedFingerprints, name)
    93  			continue
    94  		}
    95  
    96  		availableFingerprints = append(availableFingerprints, name)
    97  	}
    98  
    99  	if err := fp.setupFingerprinters(availableFingerprints); err != nil {
   100  		return err
   101  	}
   102  
   103  	if len(skippedFingerprints) != 0 {
   104  		fp.logger.Debug("fingerprint modules skipped due to allow/denylist",
   105  			"skipped_fingerprinters", skippedFingerprints)
   106  	}
   107  
   108  	return nil
   109  }
   110  
   111  // Reload will reload any registered ReloadableFingerprinters and immediately call Fingerprint
   112  func (fm *FingerprintManager) Reload() {
   113  	for name, fp := range fm.reloadableFps {
   114  		fm.logger.Info("reloading fingerprinter", "fingerprinter", name)
   115  		fp.Reload()
   116  		if _, err := fm.fingerprint(name, fp); err != nil {
   117  			fm.logger.Warn("error fingerprinting after reload", "fingerprinter", name, "error", err)
   118  		}
   119  	}
   120  }
   121  
   122  // setupFingerprints is used to fingerprint the node to see if these attributes are
   123  // supported
   124  func (fm *FingerprintManager) setupFingerprinters(fingerprints []string) error {
   125  	var appliedFingerprints []string
   126  
   127  	for _, name := range fingerprints {
   128  		f, err := fingerprint.NewFingerprint(name, fm.logger)
   129  
   130  		if err != nil {
   131  			fm.logger.Error("error fingerprinting", "error", err, "fingerprinter", name)
   132  			return err
   133  		}
   134  
   135  		detected, err := fm.fingerprint(name, f)
   136  		if err != nil {
   137  			return err
   138  		}
   139  
   140  		// log the fingerprinters which have been applied
   141  		if detected {
   142  			appliedFingerprints = append(appliedFingerprints, name)
   143  		}
   144  
   145  		p, period := f.Periodic()
   146  		if p {
   147  			go fm.runFingerprint(f, period, name)
   148  		}
   149  
   150  		if rfp, ok := f.(fingerprint.ReloadableFingerprint); ok {
   151  			fm.reloadableFps[name] = rfp
   152  		}
   153  	}
   154  
   155  	fm.logger.Debug("detected fingerprints", "node_attrs", appliedFingerprints)
   156  	return nil
   157  }
   158  
   159  // runFingerprint runs each fingerprinter individually on an ongoing basis
   160  func (fm *FingerprintManager) runFingerprint(f fingerprint.Fingerprint, period time.Duration, name string) {
   161  	fm.logger.Debug("fingerprinting periodically", "fingerprinter", name, "period", period)
   162  
   163  	timer := time.NewTimer(period)
   164  	defer timer.Stop()
   165  
   166  	for {
   167  		select {
   168  		case <-timer.C:
   169  			timer.Reset(period)
   170  
   171  			_, err := fm.fingerprint(name, f)
   172  			if err != nil {
   173  				fm.logger.Debug("error periodic fingerprinting", "error", err, "fingerprinter", name)
   174  				continue
   175  			}
   176  
   177  		case <-fm.shutdownCh:
   178  			return
   179  		}
   180  	}
   181  }
   182  
   183  // fingerprint does an initial fingerprint of the client. If the fingerprinter
   184  // is meant to be run continuously, a process is launched to perform this
   185  // fingerprint on an ongoing basis in the background.
   186  func (fm *FingerprintManager) fingerprint(name string, f fingerprint.Fingerprint) (bool, error) {
   187  	var response fingerprint.FingerprintResponse
   188  
   189  	fm.nodeLock.Lock()
   190  	request := &fingerprint.FingerprintRequest{Config: fm.getConfig(), Node: fm.node}
   191  	err := f.Fingerprint(request, &response)
   192  	fm.nodeLock.Unlock()
   193  
   194  	if err != nil {
   195  		return false, err
   196  	}
   197  
   198  	if node := fm.updateNodeAttributes(&response); node != nil {
   199  		fm.setNode(node)
   200  	}
   201  
   202  	return response.Detected, nil
   203  }