1package fingerprint
2
3import (
4	"fmt"
5	"sort"
6	"time"
7
8	log "github.com/hashicorp/go-hclog"
9	cstructs "github.com/hashicorp/nomad/client/structs"
10)
11
12// EmptyDuration is to be used by fingerprinters that are not periodic.
13const (
14	EmptyDuration = time.Duration(0)
15
16	// TightenNetworkTimeoutsConfig is a config key that can be used during
17	// tests to tighten the timeouts for fingerprinters that make network calls.
18	TightenNetworkTimeoutsConfig = "test.tighten_network_timeouts"
19)
20
21func init() {
22
23	// Initialize the list of available fingerprinters per platform.  Each
24	// platform defines its own list of available fingerprinters.
25	initPlatformFingerprints(hostFingerprinters)
26}
27
28var (
29	// hostFingerprinters contains the host fingerprints which are available for a
30	// given platform.
31	hostFingerprinters = map[string]Factory{
32		"arch":    NewArchFingerprint,
33		"consul":  NewConsulFingerprint,
34		"cni":     NewCNIFingerprint,
35		"cpu":     NewCPUFingerprint,
36		"host":    NewHostFingerprint,
37		"memory":  NewMemoryFingerprint,
38		"network": NewNetworkFingerprint,
39		"nomad":   NewNomadFingerprint,
40		"signal":  NewSignalFingerprint,
41		"storage": NewStorageFingerprint,
42		"vault":   NewVaultFingerprint,
43	}
44
45	// envFingerprinters contains the fingerprints that are environment specific.
46	// This should run after the host fingerprinters as they may override specific
47	// node resources with more detailed information.
48	envFingerprinters = map[string]Factory{
49		"env_aws":   NewEnvAWSFingerprint,
50		"env_gce":   NewEnvGCEFingerprint,
51		"env_azure": NewEnvAzureFingerprint,
52	}
53)
54
55// BuiltinFingerprints is a slice containing the key names of all registered
56// fingerprints available. The order of this slice should be preserved when
57// fingerprinting.
58func BuiltinFingerprints() []string {
59	fingerprints := make([]string, 0, len(hostFingerprinters))
60	for k := range hostFingerprinters {
61		fingerprints = append(fingerprints, k)
62	}
63	sort.Strings(fingerprints)
64	for k := range envFingerprinters {
65		fingerprints = append(fingerprints, k)
66	}
67	return fingerprints
68}
69
70// NewFingerprint is used to instantiate and return a new fingerprint
71// given the name and a logger
72func NewFingerprint(name string, logger log.Logger) (Fingerprint, error) {
73	// Lookup the factory function
74	factory, ok := hostFingerprinters[name]
75	if !ok {
76		factory, ok = envFingerprinters[name]
77		if !ok {
78			return nil, fmt.Errorf("unknown fingerprint '%s'", name)
79		}
80	}
81
82	// Instantiate the fingerprint
83	f := factory(logger)
84	return f, nil
85}
86
87// Factory is used to instantiate a new Fingerprint
88type Factory func(log.Logger) Fingerprint
89
90// HealthCheck is used for doing periodic health checks. On a given time
91// interfal, a health check will be called by the fingerprint manager of the
92// node.
93type HealthCheck interface {
94	// Check is used to update properties of the node on the status of the health
95	// check
96	HealthCheck(*cstructs.HealthCheckRequest, *cstructs.HealthCheckResponse) error
97
98	// GetHealthCheckInterval is a mechanism for the health checker to indicate that
99	// it should be run periodically. The return value is a boolean indicating
100	// whether it should be done periodically, and the time interval at which
101	// this check should happen.
102	GetHealthCheckInterval(*cstructs.HealthCheckIntervalRequest, *cstructs.HealthCheckIntervalResponse) error
103}
104
105// Fingerprint is used for doing "fingerprinting" of the
106// host to automatically determine attributes, resources,
107// and metadata about it. Each of these is a heuristic, and
108// many of them can be applied on a particular host.
109type Fingerprint interface {
110	// Fingerprint is used to update properties of the Node,
111	// and returns a diff of updated node attributes and a potential error.
112	Fingerprint(*FingerprintRequest, *FingerprintResponse) error
113
114	// Periodic is a mechanism for the fingerprinter to indicate that it should
115	// be run periodically. The return value is a boolean indicating if it
116	// should be periodic, and if true, a duration.
117	Periodic() (bool, time.Duration)
118}
119
120// ReloadableFingerprint can be implemented if the fingerprinter needs to be run during client reload.
121// If implemented, the client will call Reload during client reload then immediately Fingerprint
122type ReloadableFingerprint interface {
123	Fingerprint
124	Reload()
125}
126
127// StaticFingerprinter can be embedded in a struct that has a Fingerprint method
128// to make it non-periodic.
129type StaticFingerprinter struct{}
130
131func (s *StaticFingerprinter) Periodic() (bool, time.Duration) {
132	return false, EmptyDuration
133}
134