2018-01-24 13:01:37 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
2018-02-12 23:15:15 +00:00
|
|
|
"sync"
|
2018-01-24 13:01:37 +00:00
|
|
|
"time"
|
|
|
|
|
2018-09-16 00:48:59 +00:00
|
|
|
log "github.com/hashicorp/go-hclog"
|
2018-01-24 13:01:37 +00:00
|
|
|
"github.com/hashicorp/nomad/client/config"
|
|
|
|
"github.com/hashicorp/nomad/client/fingerprint"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
2019-01-15 00:50:05 +00:00
|
|
|
"github.com/hashicorp/nomad/pluginutils/loader"
|
2018-10-11 00:08:57 +00:00
|
|
|
)
|
|
|
|
|
2018-01-24 13:01:37 +00:00
|
|
|
// FingerprintManager runs a client fingerprinters on a continuous basis, and
|
|
|
|
// updates the client when the node has changed
|
|
|
|
type FingerprintManager struct {
|
2018-10-04 19:08:20 +00:00
|
|
|
singletonLoader loader.PluginCatalog
|
|
|
|
getConfig func() *config.Config
|
|
|
|
node *structs.Node
|
|
|
|
nodeLock sync.Mutex
|
|
|
|
shutdownCh chan struct{}
|
2018-02-05 17:20:07 +00:00
|
|
|
|
2018-01-25 16:30:15 +00:00
|
|
|
// updateNodeAttributes is a callback to the client to update the state of its
|
2018-02-05 17:20:07 +00:00
|
|
|
// associated node
|
2018-12-01 16:10:39 +00:00
|
|
|
updateNodeAttributes func(*fingerprint.FingerprintResponse) *structs.Node
|
2018-01-25 16:30:15 +00:00
|
|
|
|
2018-11-28 03:42:22 +00:00
|
|
|
logger log.Logger
|
2018-01-24 13:01:37 +00:00
|
|
|
}
|
|
|
|
|
2018-02-05 23:02:52 +00:00
|
|
|
// NewFingerprintManager is a constructor that creates and returns an instance
|
|
|
|
// of FingerprintManager
|
2018-10-04 19:08:20 +00:00
|
|
|
func NewFingerprintManager(
|
|
|
|
singletonLoader loader.PluginCatalog,
|
|
|
|
getConfig func() *config.Config,
|
2018-02-05 23:02:52 +00:00
|
|
|
node *structs.Node,
|
|
|
|
shutdownCh chan struct{},
|
2018-12-01 16:10:39 +00:00
|
|
|
updateNodeAttributes func(*fingerprint.FingerprintResponse) *structs.Node,
|
2018-09-16 00:48:59 +00:00
|
|
|
logger log.Logger) *FingerprintManager {
|
2018-08-29 22:05:03 +00:00
|
|
|
|
2018-02-05 23:02:52 +00:00
|
|
|
return &FingerprintManager{
|
2018-10-04 19:08:20 +00:00
|
|
|
singletonLoader: singletonLoader,
|
2018-01-25 16:30:15 +00:00
|
|
|
getConfig: getConfig,
|
|
|
|
updateNodeAttributes: updateNodeAttributes,
|
|
|
|
node: node,
|
|
|
|
shutdownCh: shutdownCh,
|
2018-09-16 00:48:59 +00:00
|
|
|
logger: logger.Named("fingerprint_mgr"),
|
2018-02-05 23:02:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-10 17:52:43 +00:00
|
|
|
// setNode updates the current client node
|
|
|
|
func (fm *FingerprintManager) setNode(node *structs.Node) {
|
|
|
|
fm.nodeLock.Lock()
|
|
|
|
defer fm.nodeLock.Unlock()
|
|
|
|
fm.node = node
|
|
|
|
}
|
|
|
|
|
2018-04-16 22:07:08 +00:00
|
|
|
// getNode returns the current client node
|
|
|
|
func (fm *FingerprintManager) getNode() *structs.Node {
|
|
|
|
fm.nodeLock.Lock()
|
|
|
|
defer fm.nodeLock.Unlock()
|
|
|
|
return fm.node
|
|
|
|
}
|
|
|
|
|
2018-03-19 16:50:18 +00:00
|
|
|
// Run starts the process of fingerprinting the node. It does an initial pass,
|
|
|
|
// identifying whitelisted and blacklisted fingerprints/drivers. Then, for
|
|
|
|
// those which require periotic checking, it starts a periodic process for
|
|
|
|
// each.
|
|
|
|
func (fp *FingerprintManager) Run() error {
|
|
|
|
// First, set up all fingerprints
|
|
|
|
cfg := fp.getConfig()
|
|
|
|
whitelistFingerprints := cfg.ReadStringListToMap("fingerprint.whitelist")
|
|
|
|
whitelistFingerprintsEnabled := len(whitelistFingerprints) > 0
|
|
|
|
blacklistFingerprints := cfg.ReadStringListToMap("fingerprint.blacklist")
|
2018-01-24 13:01:37 +00:00
|
|
|
|
2018-08-29 22:05:03 +00:00
|
|
|
fp.logger.Debug("built-in fingerprints", "fingerprinters", fingerprint.BuiltinFingerprints())
|
2018-03-09 17:28:01 +00:00
|
|
|
|
2018-03-19 16:50:18 +00:00
|
|
|
var availableFingerprints []string
|
|
|
|
var skippedFingerprints []string
|
|
|
|
for _, name := range fingerprint.BuiltinFingerprints() {
|
|
|
|
// Skip modules that are not in the whitelist if it is enabled.
|
|
|
|
if _, ok := whitelistFingerprints[name]; whitelistFingerprintsEnabled && !ok {
|
|
|
|
skippedFingerprints = append(skippedFingerprints, name)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Skip modules that are in the blacklist
|
|
|
|
if _, ok := blacklistFingerprints[name]; ok {
|
|
|
|
skippedFingerprints = append(skippedFingerprints, name)
|
|
|
|
continue
|
|
|
|
}
|
2018-03-09 17:28:01 +00:00
|
|
|
|
2018-03-19 16:50:18 +00:00
|
|
|
availableFingerprints = append(availableFingerprints, name)
|
|
|
|
}
|
2018-01-24 13:01:37 +00:00
|
|
|
|
2018-03-19 16:50:18 +00:00
|
|
|
if err := fp.setupFingerprinters(availableFingerprints); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(skippedFingerprints) != 0 {
|
2018-08-29 22:05:03 +00:00
|
|
|
fp.logger.Debug("fingerprint modules skipped due to white/blacklist",
|
|
|
|
"skipped_fingerprinters", skippedFingerprints)
|
2018-03-19 16:50:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// setupFingerprints is used to fingerprint the node to see if these attributes are
|
|
|
|
// supported
|
|
|
|
func (fm *FingerprintManager) setupFingerprinters(fingerprints []string) error {
|
|
|
|
var appliedFingerprints []string
|
|
|
|
|
|
|
|
for _, name := range fingerprints {
|
2018-09-16 00:48:59 +00:00
|
|
|
f, err := fingerprint.NewFingerprint(name, fm.logger)
|
2018-03-19 16:50:18 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2018-08-29 22:05:03 +00:00
|
|
|
fm.logger.Error("error fingerprinting", "error", err, "fingerprinter", name)
|
2018-03-19 16:50:18 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
detected, err := fm.fingerprint(name, f)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// log the fingerprinters which have been applied
|
|
|
|
if detected {
|
|
|
|
appliedFingerprints = append(appliedFingerprints, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
p, period := f.Periodic()
|
|
|
|
if p {
|
|
|
|
go fm.runFingerprint(f, period, name)
|
2018-01-24 13:01:37 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-19 16:50:18 +00:00
|
|
|
|
2018-08-29 22:05:03 +00:00
|
|
|
fm.logger.Debug("detected fingerprints", "node_attrs", appliedFingerprints)
|
2018-03-19 16:50:18 +00:00
|
|
|
return nil
|
2018-01-24 13:01:37 +00:00
|
|
|
}
|
|
|
|
|
2018-03-19 16:50:18 +00:00
|
|
|
// runFingerprint runs each fingerprinter individually on an ongoing basis
|
|
|
|
func (fm *FingerprintManager) runFingerprint(f fingerprint.Fingerprint, period time.Duration, name string) {
|
2018-08-29 22:05:03 +00:00
|
|
|
fm.logger.Debug("fingerprinting periodically", "fingerprinter", name, "period", period)
|
2018-03-19 16:50:18 +00:00
|
|
|
|
|
|
|
timer := time.NewTimer(period)
|
|
|
|
defer timer.Stop()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-timer.C:
|
|
|
|
timer.Reset(period)
|
|
|
|
|
|
|
|
_, err := fm.fingerprint(name, f)
|
|
|
|
if err != nil {
|
2018-08-29 22:05:03 +00:00
|
|
|
fm.logger.Debug("error periodic fingerprinting", "error", err, "fingerprinter", name)
|
2018-03-19 16:50:18 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
case <-fm.shutdownCh:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// fingerprint does an initial fingerprint of the client. If the fingerprinter
|
|
|
|
// is meant to be run continuously, a process is launched to perform this
|
|
|
|
// fingerprint on an ongoing basis in the background.
|
|
|
|
func (fm *FingerprintManager) fingerprint(name string, f fingerprint.Fingerprint) (bool, error) {
|
2018-12-01 16:10:39 +00:00
|
|
|
var response fingerprint.FingerprintResponse
|
2018-03-19 16:50:18 +00:00
|
|
|
|
|
|
|
fm.nodeLock.Lock()
|
2018-12-01 16:10:39 +00:00
|
|
|
request := &fingerprint.FingerprintRequest{Config: fm.getConfig(), Node: fm.node}
|
2018-03-19 16:50:18 +00:00
|
|
|
err := f.Fingerprint(request, &response)
|
|
|
|
fm.nodeLock.Unlock()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if node := fm.updateNodeAttributes(&response); node != nil {
|
2018-04-10 17:52:43 +00:00
|
|
|
fm.setNode(node)
|
2018-03-19 16:50:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return response.Detected, nil
|
|
|
|
}
|