Files
nomad/client/fingerprint/dynamic_host_volumes.go
Daniel Bennett 99c25fc635 dhv: mkdir plugin parameters: uid,guid,mode (#25533)
also remove Error logs from client rpc and promote plugin Debug logs to Error (since they have more info in them)
2025-03-28 10:13:13 -05:00

121 lines
3.2 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package fingerprint
import (
"context"
"os"
"strings"
"sync"
"time"
"github.com/hashicorp/go-hclog"
hvm "github.com/hashicorp/nomad/client/hostvolumemanager"
"github.com/hashicorp/nomad/helper"
)
func NewPluginsHostVolumeFingerprint(logger hclog.Logger) Fingerprint {
return &DynamicHostVolumePluginFingerprint{
logger: logger.Named("host_volume_plugins"),
}
}
var _ ReloadableFingerprint = &DynamicHostVolumePluginFingerprint{}
type DynamicHostVolumePluginFingerprint struct {
logger hclog.Logger
}
func (h *DynamicHostVolumePluginFingerprint) Reload() {
// host volume plugins are re-detected on agent reload
}
func (h *DynamicHostVolumePluginFingerprint) Fingerprint(request *FingerprintRequest, response *FingerprintResponse) error {
// always add "mkdir" plugin
h.logger.Debug("detected plugin built-in",
"plugin_id", hvm.HostVolumePluginMkdirID, "version", hvm.HostVolumePluginMkdirVersion)
defer response.AddAttribute("plugins.host_volume."+hvm.HostVolumePluginMkdirID+".version", hvm.HostVolumePluginMkdirVersion)
response.Detected = true
// this config value will be empty in -dev mode
pluginDir := request.Config.HostVolumePluginDir
if pluginDir == "" {
return nil
}
plugins, err := GetHostVolumePluginVersions(h.logger, pluginDir, request.Node.NodePool)
if err != nil {
if os.IsNotExist(err) {
h.logger.Debug("plugin dir does not exist", "dir", pluginDir)
} else {
h.logger.Warn("error finding plugins", "dir", pluginDir, "error", err)
}
return nil // don't halt agent start
}
// if this was a reload, wipe what was there before
for k := range request.Node.Attributes {
if strings.HasPrefix(k, "plugins.host_volume.") {
response.RemoveAttribute(k)
}
}
// set the attribute(s)
for plugin, version := range plugins {
h.logger.Debug("detected plugin", "plugin_id", plugin, "version", version)
response.AddAttribute("plugins.host_volume."+plugin+".version", version)
}
return nil
}
func (h *DynamicHostVolumePluginFingerprint) Periodic() (bool, time.Duration) {
return false, 0
}
// GetHostVolumePluginVersions finds all the executable files on disk that
// respond to a `fingerprint` call. The return map's keys are plugin IDs,
// and the values are version strings.
func GetHostVolumePluginVersions(log hclog.Logger, pluginDir, nodePool string) (map[string]string, error) {
files, err := helper.FindExecutableFiles(pluginDir)
if err != nil {
return nil, err
}
plugins := make(map[string]string)
mut := sync.Mutex{}
var wg sync.WaitGroup
for file := range files {
wg.Add(1)
go func(file string) {
defer wg.Done()
// really should take way less than a second
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
log := log.With("plugin_id", file)
p, err := hvm.NewHostVolumePluginExternal(log, pluginDir, file, "", nodePool)
if err != nil {
log.Warn("error getting plugin", "error", err)
return
}
fprint, err := p.Fingerprint(ctx)
if err != nil {
// Fingerprint logs the error
return
}
mut.Lock()
plugins[file] = fprint.Version.String()
mut.Unlock()
}(file)
}
wg.Wait()
return plugins, nil
}