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 }