Skip to content

Commit

Permalink
fingerprint: add fingerprinting for CNI plugins presense and version
Browse files Browse the repository at this point in the history
This PR adds a fingerprinter to set the attributes
 "cni.plugins" -> "true" or "false" if <cni_path> contains CNI plugins
 "cni.plugins.version" -> version of the CNI plugins if present
  • Loading branch information
shoenig committed Dec 2, 2022
1 parent b23f435 commit e20c7c3
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 14 deletions.
3 changes: 3 additions & 0 deletions .changelog/15452.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
fingerprint: Add fingerprinting for CNI containernetworking plugins
```
8 changes: 5 additions & 3 deletions client/fingerprint/cni.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import (
"strings"

"github.com/containernetworking/cni/libcni"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad/nomad/structs"
)

// CNIFingerprint creates a fingerprint of the CNI configuration(s) on the
// Nomad client.
type CNIFingerprint struct {
StaticFingerprinter
logger log.Logger
logger hclog.Logger
}

func NewCNIFingerprint(logger log.Logger) Fingerprint {
func NewCNIFingerprint(logger hclog.Logger) Fingerprint {
return &CNIFingerprint{logger: logger}
}

Expand Down
23 changes: 12 additions & 11 deletions client/fingerprint/fingerprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,18 @@ var (
// hostFingerprinters contains the host fingerprints which are available for a
// given platform.
hostFingerprinters = map[string]Factory{
"arch": NewArchFingerprint,
"consul": NewConsulFingerprint,
"cni": NewCNIFingerprint,
"cpu": NewCPUFingerprint,
"host": NewHostFingerprint,
"memory": NewMemoryFingerprint,
"network": NewNetworkFingerprint,
"nomad": NewNomadFingerprint,
"signal": NewSignalFingerprint,
"storage": NewStorageFingerprint,
"vault": NewVaultFingerprint,
"arch": NewArchFingerprint,
"consul": NewConsulFingerprint,
"cni": NewCNIFingerprint, // networks
"cpu": NewCPUFingerprint,
"host": NewHostFingerprint,
"memory": NewMemoryFingerprint,
"network": NewNetworkFingerprint,
"nomad": NewNomadFingerprint,
"plugins_cni": NewPluginsCNIFingerprint,
"signal": NewSignalFingerprint,
"storage": NewStorageFingerprint,
"vault": NewVaultFingerprint,
}

// envFingerprinters contains the fingerprints that are environment specific.
Expand Down
67 changes: 67 additions & 0 deletions client/fingerprint/plugins_cni.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package fingerprint

import (
"context"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/hashicorp/go-hclog"
)

const (
defaultCNIPath = "/opt/cni/bin"
cniPluginAttribute = "plugins.cni.version"
)

// PluginsCNIFingerprint creates a fingerprint of the CNI plugins present on the
// CNI plugin path specified for the Nomad client.
type PluginsCNIFingerprint struct {
StaticFingerprinter
logger hclog.Logger
lister func(string) ([]os.DirEntry, error)
}

func NewPluginsCNIFingerprint(logger hclog.Logger) Fingerprint {
return &PluginsCNIFingerprint{
logger: logger.Named("cni_plugins"),
lister: os.ReadDir,
}
}

func (f *PluginsCNIFingerprint) Fingerprint(req *FingerprintRequest, resp *FingerprintResponse) error {
cniPath := req.Config.CNIPath
if cniPath == "" {
cniPath = defaultCNIPath
}

entries, err := f.lister(cniPath)
switch {
case err != nil:
f.logger.Debug("failed to read CNI plugins directory", "cni_path", cniPath, "error", err)
resp.Detected = false
return nil
case len(entries) == 0:
f.logger.Debug("no CNI plugins found", "cni_path", cniPath)
resp.Detected = true
return nil
}

bridgePath := filepath.Join(cniPath, "bridge")
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, bridgePath, "--version")
output, err := cmd.CombinedOutput()
if err != nil {
f.logger.Debug("failed to detect CNI plugins version", "error", err)
return nil
}
tokens := strings.Fields(string(output))
version := tokens[len(tokens)-1]
f.logger.Debug("detected CNI plugins", "version", version)
resp.AddAttribute(cniPluginAttribute, version)
resp.Detected = true
return nil
}
67 changes: 67 additions & 0 deletions client/fingerprint/plugins_cni_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package fingerprint

import (
"os"
"testing"

"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/client/config"
"github.com/hashicorp/nomad/helper/testlog"
"github.com/shoenig/test/must"
)

func TestPluginsCNIFingerprint_Fingerprint_present(t *testing.T) {
ci.Parallel(t)

f := NewPluginsCNIFingerprint(testlog.HCLogger(t))
request := &FingerprintRequest{
Config: &config.Config{
CNIPath: "./test_fixtures/cni",
},
}
response := new(FingerprintResponse)

err := f.Fingerprint(request, response)
must.NoError(t, err)
must.True(t, response.Detected)
must.StrHasPrefix(t, "v1.0.2", response.Attributes[cniPluginAttribute])
}

func TestPluginsCNIFingerprint_Fingerprint_absent(t *testing.T) {
ci.Parallel(t)

f := NewPluginsCNIFingerprint(testlog.HCLogger(t))
request := &FingerprintRequest{
Config: &config.Config{
CNIPath: "/does/not/exist",
},
}
response := new(FingerprintResponse)

err := f.Fingerprint(request, response)
must.NoError(t, err)
must.False(t, response.Detected)
_, exists := response.Attributes[cniPluginAttribute]
must.False(t, exists)
}

func TestPluginsCNIFingerprint_Fingerprint_empty(t *testing.T) {
ci.Parallel(t)

lister := func(string) ([]os.DirEntry, error) {
return nil, nil
}

f := NewPluginsCNIFingerprint(testlog.HCLogger(t))
f.(*PluginsCNIFingerprint).lister = lister
request := &FingerprintRequest{
Config: new(config.Config),
}
response := new(FingerprintResponse)

err := f.Fingerprint(request, response)
must.NoError(t, err)
must.True(t, response.Detected)
_, exists := response.Attributes[cniPluginAttribute]
must.False(t, exists)
}
3 changes: 3 additions & 0 deletions client/fingerprint/test_fixtures/cni/bridge
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

echo "CNI bridge plugin v1.0.2"

0 comments on commit e20c7c3

Please sign in to comment.