diff --git a/pkg/cni/cni_linux.go b/pkg/cni/cni_linux.go index da64849c..08175122 100644 --- a/pkg/cni/cni_linux.go +++ b/pkg/cni/cni_linux.go @@ -11,6 +11,7 @@ import ( "log/slog" "os" "path/filepath" + "runtime" ) //go:embed all:embed @@ -79,21 +80,26 @@ func (m *Manager) Extract() error { return nil } +// findTarball selects the CNI plugins archive matching this binary's arch. +// The macOS build cross-compiles ephemerd-linux for arm64, but the embed dir +// can hold BOTH cni-plugins-linux-amd64 and -arm64 (the amd64 one lingers +// from the CI cache). `//go:embed all:embed` then bundles both, so a naive +// "first entry" pick returns amd64 and the arm64 Linux VM fails every CNI +// exec with "exec format error". Select by runtime.GOARCH instead. func (m *Manager) findTarball() (string, error) { entries, err := cniFS.ReadDir("embed") if err != nil { return "", fmt.Errorf("reading embedded files: %w", err) } - + names := make([]string, 0, len(entries)) for _, e := range entries { - name := e.Name() - if name == ".gitkeep" { - continue - } - return filepath.Join("embed", name), nil + names = append(names, e.Name()) } - - return "", fmt.Errorf("no CNI plugins archive found in embedded files (did you run 'make download-cni'?)") + name, err := selectCNITarball(names, runtime.GOARCH) + if err != nil { + return "", err + } + return filepath.Join("embed", name), nil } func extractTarGz(r io.Reader, dest string) error { diff --git a/pkg/cni/select.go b/pkg/cni/select.go new file mode 100644 index 00000000..ccd452ca --- /dev/null +++ b/pkg/cni/select.go @@ -0,0 +1,30 @@ +package cni + +import ( + "fmt" + "strings" +) + +// selectCNITarball picks the CNI plugins archive matching goarch from the +// embed listing. The macOS build cross-compiles ephemerd-linux for arm64 but +// the embed dir can hold BOTH cni-plugins-linux-amd64 and -arm64 (the amd64 +// one lingers from the CI cache). `//go:embed all:embed` then bundles both, +// so a naive "first entry" pick returns amd64 and the arm64 Linux VM fails +// every CNI exec with "exec format error". Select by arch instead. +func selectCNITarball(names []string, goarch string) (string, error) { + want := "cni-plugins-linux-" + goarch + "-" + var found []string + for _, name := range names { + if name == ".gitkeep" { + continue + } + found = append(found, name) + if strings.HasPrefix(name, want) { + return name, nil + } + } + if len(found) == 0 { + return "", fmt.Errorf("no CNI plugins archive found in embedded files (did you run the download targets?)") + } + return "", fmt.Errorf("no CNI plugins archive for linux/%s in embedded files (have: %s)", goarch, strings.Join(found, ", ")) +} diff --git a/pkg/cni/select_test.go b/pkg/cni/select_test.go new file mode 100644 index 00000000..9be86be6 --- /dev/null +++ b/pkg/cni/select_test.go @@ -0,0 +1,39 @@ +package cni + +import "testing" + +func TestSelectCNITarball(t *testing.T) { + both := []string{"cni-plugins-linux-amd64-v1.6.2.tgz", "cni-plugins-linux-arm64-v1.6.2.tgz"} + + tests := []struct { + name string + names []string + goarch string + want string + wantErr bool + }{ + {"arm64 picks arm64 from both", both, "arm64", "cni-plugins-linux-arm64-v1.6.2.tgz", false}, + {"amd64 picks amd64 from both", both, "amd64", "cni-plugins-linux-amd64-v1.6.2.tgz", false}, + {"amd64 alone", both[:1], "amd64", "cni-plugins-linux-amd64-v1.6.2.tgz", false}, + {"skips gitkeep", []string{".gitkeep", "cni-plugins-linux-arm64-v1.6.2.tgz"}, "arm64", "cni-plugins-linux-arm64-v1.6.2.tgz", false}, + {"no match errors", both[:1], "arm64", "", true}, + {"empty errors", []string{".gitkeep"}, "arm64", "", true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := selectCNITarball(tt.names, tt.goarch) + if tt.wantErr { + if err == nil { + t.Fatalf("expected error, got %q", got) + } + return + } + if err != nil { + t.Fatal(err) + } + if got != tt.want { + t.Errorf("got %q, want %q", got, tt.want) + } + }) + } +}