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.
This commit is contained in:
Seth Hoenig 2023-02-13 14:55:56 -06:00 committed by GitHub
parent 1f7f96cf38
commit 490c902c62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 19 deletions

View File

@ -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()

View File

@ -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)

View File

@ -0,0 +1,3 @@
#!/bin/sh
echo "Custom v9.9.9 Plugin"