From c41ccc6cd38e572a5be32b3d34e540d4c1f7fe12 Mon Sep 17 00:00:00 2001 From: Seth Hoenig Date: Mon, 13 Feb 2023 14:55:56 -0600 Subject: [PATCH] cni: handle multi-path cni_path when fingerprinting plugins (#16163) This PR fixes the CNI plugin fingerprinter to take into account the fact that the cni_path config can be a multi-path (e.g. `/foo:/bar:/baz`). Accumulate plugins from each of the possible path elements. If scanning any of the named directory fails, the fingerprinter fails. Fixes #16083 No CL/BP - has not shipped yet. --- client/fingerprint/plugins_cni.go | 42 ++++++++++--------- client/fingerprint/plugins_cni_test.go | 22 ++++++++++ client/fingerprint/test_fixtures/cni2/custom2 | 3 ++ 3 files changed, 48 insertions(+), 19 deletions(-) create mode 100755 client/fingerprint/test_fixtures/cni2/custom2 diff --git a/client/fingerprint/plugins_cni.go b/client/fingerprint/plugins_cni.go index e1eb89d3e..8100521c7 100644 --- a/client/fingerprint/plugins_cni.go +++ b/client/fingerprint/plugins_cni.go @@ -40,24 +40,28 @@ func (f *PluginsCNIFingerprint) Fingerprint(req *FingerprintRequest, resp *Finge return nil } - // list the cni_path directory - entries, err := f.lister(cniPath) - switch { - case err != nil: - f.logger.Warn("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 - } + // cniPath could be a multi-path, e.g. /opt/cni/bin:/custom/cni/bin + cniPathList := filepath.SplitList(cniPath) + for _, cniPath = range cniPathList { + // list the cni_path directory + entries, err := f.lister(cniPath) + switch { + case err != nil: + f.logger.Warn("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 + } - // for each file in cni_path, detect executables and try to get their version - for _, entry := range entries { - v, ok := f.detectOne(cniPath, entry) - if ok { - resp.AddAttribute(f.attribute(entry.Name()), v) + // for each file in cni_path, detect executables and try to get their version + for _, entry := range entries { + v, ok := f.detectOnePlugin(cniPath, entry) + if ok { + resp.AddAttribute(f.attribute(entry.Name()), v) + } } } @@ -70,7 +74,7 @@ func (f *PluginsCNIFingerprint) attribute(filename string) string { return fmt.Sprintf("%s.%s", cniPluginAttribute, filename) } -func (f *PluginsCNIFingerprint) detectOne(cniPath string, entry os.DirEntry) (string, bool) { +func (f *PluginsCNIFingerprint) detectOnePlugin(pluginPath string, entry os.DirEntry) (string, bool) { fi, err := entry.Info() if err != nil { f.logger.Debug("failed to read cni directory entry", "error", err) @@ -82,7 +86,7 @@ func (f *PluginsCNIFingerprint) detectOne(cniPath string, entry os.DirEntry) (st return "", false // not executable } - exePath := filepath.Join(cniPath, fi.Name()) + exePath := filepath.Join(pluginPath, fi.Name()) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() diff --git a/client/fingerprint/plugins_cni_test.go b/client/fingerprint/plugins_cni_test.go index 4a03baec0..95e1b0e23 100644 --- a/client/fingerprint/plugins_cni_test.go +++ b/client/fingerprint/plugins_cni_test.go @@ -30,6 +30,28 @@ func TestPluginsCNIFingerprint_Fingerprint_present(t *testing.T) { must.Eq(t, "v1.0.2", response.Attributes[attrBridge]) } +func TestPluginsCNIFingerprint_Fingerprint_multi(t *testing.T) { + ci.Parallel(t) + + f := NewPluginsCNIFingerprint(testlog.HCLogger(t)) + request := &FingerprintRequest{ + Config: &config.Config{ + CNIPath: "./test_fixtures/cni:./test_fixtures/cni2", + }, + } + response := new(FingerprintResponse) + + err := f.Fingerprint(request, response) + must.NoError(t, err) + must.True(t, response.Detected) + attrCustom := f.(*PluginsCNIFingerprint).attribute("custom") + attrBridge := f.(*PluginsCNIFingerprint).attribute("bridge") + attrCustom2 := f.(*PluginsCNIFingerprint).attribute("custom2") + must.Eq(t, "v1.2.3", response.Attributes[attrCustom]) + must.Eq(t, "v1.0.2", response.Attributes[attrBridge]) + must.Eq(t, "v9.9.9", response.Attributes[attrCustom2]) +} + func TestPluginsCNIFingerprint_Fingerprint_absent(t *testing.T) { ci.Parallel(t) diff --git a/client/fingerprint/test_fixtures/cni2/custom2 b/client/fingerprint/test_fixtures/cni2/custom2 new file mode 100755 index 000000000..5632e3a9c --- /dev/null +++ b/client/fingerprint/test_fixtures/cni2/custom2 @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Custom v9.9.9 Plugin"