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 }