From 082066765898ea572cf8b028d546bae2930eb148 Mon Sep 17 00:00:00 2001 From: erezrokah Date: Thu, 8 Oct 2026 13:35:32 +0300 Subject: [PATCH] fix: Fall back to architecture specific path for missing local plugin path --- managedplugin/binary.go | 19 +++++++++-- managedplugin/binary_test.go | 62 ++++++++++++++++++++++++++++++++++++ managedplugin/plugin.go | 1 + 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/managedplugin/binary.go b/managedplugin/binary.go index 6617a29..c33bca8 100644 --- a/managedplugin/binary.go +++ b/managedplugin/binary.go @@ -53,12 +53,27 @@ var peMachines = map[string]uint16{ // path is the pre-namespacing location, which is read but never written. func pluginCachePaths(directory, kind, org, name, version string) (canonical, legacy string) { base := filepath.Join(directory, "plugins", kind, org, name, version) - target := runtime.GOOS + "_" + runtime.GOARCH - canonical = WithBinarySuffix(filepath.Join(base, target, "plugin")) + canonical = WithBinarySuffix(filepath.Join(base, hostTarget(), "plugin")) legacy = WithBinarySuffix(filepath.Join(base, "plugin")) return canonical, legacy } +func hostTarget() string { + return runtime.GOOS + "_" + runtime.GOARCH +} + +func resolveLocalPluginPath(logger zerolog.Logger, path string) string { + if _, err := os.Stat(path); !os.IsNotExist(err) { + return path + } + targetPath := filepath.Join(filepath.Dir(path), hostTarget(), filepath.Base(path)) + if err := validateBinary(targetPath); err != nil { + return path + } + logger.Warn().Str("path", path).Str("resolved_path", targetPath).Msg("local plugin path does not exist, using architecture specific path from the plugins cache") + return targetPath +} + // resolveCachedPlugin reports which of the cache locations holds a binary that // is usable on this host, preferring the canonical one. A legacy binary is // reused only when it was built for the architecture we are running on, so diff --git a/managedplugin/binary_test.go b/managedplugin/binary_test.go index 9244957..35668b7 100644 --- a/managedplugin/binary_test.go +++ b/managedplugin/binary_test.go @@ -211,3 +211,65 @@ func TestResolveCachedPluginIsolatesArchitectures(t *testing.T) { t.Fatalf("expected another architecture's cache to be left intact, got %v", err) } } + +func TestResolveLocalPluginPath(t *testing.T) { + hostArch := buildFixture(t, runtime.GOARCH) + wrongArch := buildFixture(t, otherArch()) + + install := func(t *testing.T, src, dest string) { + t.Helper() + body, err := os.ReadFile(src) + if err != nil { + t.Fatal(err) + } + if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(dest, body, 0744); err != nil { + t.Fatal(err) + } + } + + for _, tc := range []struct { + name string + configured string + target string + wantResolved func(configured, target string) string + }{ + { + name: "configured path exists", + configured: hostArch, + target: hostArch, + wantResolved: func(configured, _ string) string { return configured }, + }, + { + name: "configured path missing falls back to architecture specific path", + target: hostArch, + wantResolved: func(_, target string) string { return target }, + }, + { + name: "architecture specific path with other arch is ignored", + target: wrongArch, + wantResolved: func(configured, _ string) string { return configured }, + }, + { + name: "no binary keeps configured path", + wantResolved: func(configured, _ string) string { return configured }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, configured := pluginCachePaths(t.TempDir(), "source", "cloudquery", "aws", "v34.9.0") + target := filepath.Join(filepath.Dir(configured), runtime.GOOS+"_"+runtime.GOARCH, filepath.Base(configured)) + if tc.configured != "" { + install(t, tc.configured, configured) + } + if tc.target != "" { + install(t, tc.target, target) + } + + if got, want := resolveLocalPluginPath(zerolog.Nop(), configured), tc.wantResolved(configured, target); got != want { + t.Errorf("got %q, want %q", got, want) + } + }) + } +} diff --git a/managedplugin/plugin.go b/managedplugin/plugin.go index 0ce7814..5baa8a2 100644 --- a/managedplugin/plugin.go +++ b/managedplugin/plugin.go @@ -181,6 +181,7 @@ func (c *Client) downloadPlugin(ctx context.Context, typ PluginType) (AssetSourc case RegistryGrpc: return AssetSourceUnknown, nil // GRPC plugins are not downloaded case RegistryLocal: + c.config.Path = resolveLocalPluginPath(c.logger, c.config.Path) return AssetSourceUnknown, validateLocalExecPath(c.config.Path) case RegistryGithub: pathSplit := strings.Split(c.config.Path, "/")