open-nomad/client/fingerprint/vault.go
Michael Schurter b554f9344a
fingerprint: lengthen Vault check after seen (#14693)
Extension of #14673

Once Vault is initially fingerprinted, extend the period since changes
should be infrequent and the fingerprint is relatively expensive since
it is contacting a central Vault server.

Also move the period timer reset *after* the fingerprint. This is
similar to #9435 where the idea is to ensure the retry period starts
*after* the operation is attempted. 15s will be the *minimum* time
between fingerprints now instead of the *maximum* time between
fingerprints.

In the case of Vault fingerprinting, the original behavior might cause
the following:

1. Timer is reset to 15s
2. Fingerprint takes 16s
3. Timer has already elapsed so we immediately Fingerprint again

Even if fingerprinting Vault only takes a few seconds, that may very
well be due to excessive load and backing off our fingerprints is
desirable. The new bevahior ensures we always wait at least 15s between
fingerprint attempts and should allow some natural jittering based on
server load and network latency.
2022-09-26 12:14:19 -07:00

90 lines
2.5 KiB
Go

package fingerprint
import (
"fmt"
"strconv"
"strings"
"time"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/helper"
vapi "github.com/hashicorp/vault/api"
)
const (
vaultAvailable = "available"
vaultUnavailable = "unavailable"
)
// VaultFingerprint is used to fingerprint for Vault
type VaultFingerprint struct {
logger log.Logger
client *vapi.Client
lastState string
}
// NewVaultFingerprint is used to create a Vault fingerprint
func NewVaultFingerprint(logger log.Logger) Fingerprint {
return &VaultFingerprint{logger: logger.Named("vault"), lastState: vaultUnavailable}
}
func (f *VaultFingerprint) Fingerprint(req *FingerprintRequest, resp *FingerprintResponse) error {
config := req.Config
if config.VaultConfig == nil || !config.VaultConfig.IsEnabled() {
return nil
}
// Only create the client once to avoid creating too many connections to
// Vault.
if f.client == nil {
vaultConfig, err := config.VaultConfig.ApiConfig()
if err != nil {
return fmt.Errorf("Failed to initialize the Vault client config: %v", err)
}
f.client, err = vapi.NewClient(vaultConfig)
if err != nil {
return fmt.Errorf("Failed to initialize Vault client: %s", err)
}
}
// Connect to vault and parse its information
status, err := f.client.Sys().SealStatus()
if err != nil {
// Print a message indicating that Vault is not available anymore
if f.lastState == vaultAvailable {
f.logger.Info("Vault is unavailable")
}
f.lastState = vaultUnavailable
return nil
}
resp.AddAttribute("vault.accessible", strconv.FormatBool(true))
// We strip the Vault prefix because < 0.6.2 the version looks like:
// status.Version = "Vault v0.6.1"
resp.AddAttribute("vault.version", strings.TrimPrefix(status.Version, "Vault "))
resp.AddAttribute("vault.cluster_id", status.ClusterID)
resp.AddAttribute("vault.cluster_name", status.ClusterName)
// If Vault was previously unavailable print a message to indicate the Agent
// is available now
if f.lastState == vaultUnavailable {
f.logger.Info("Vault is available")
}
f.lastState = vaultAvailable
resp.Detected = true
return nil
}
func (f *VaultFingerprint) Periodic() (bool, time.Duration) {
if f.lastState == vaultAvailable {
// Fingerprint infrequently once Vault is initially discovered with wide
// jitter to avoid thundering herds of fingerprints against central Vault
// servers.
return true, (30 * time.Second) + helper.RandomStagger(90*time.Second)
}
return true, 15 * time.Second
}