From 48d4e623c12791d6ecdd3b138f0b59e5971d621b Mon Sep 17 00:00:00 2001 From: dantedelucia Date: Fri, 15 Aug 2025 02:53:26 -0700 Subject: [PATCH] Add support for bulk package overrides This allows you to specify a directory that contains local packages you wish to use, instead of the packages that are available from the apt repositories. These can be provided per SVMKit component, alongside the existing package override mechanism. --- pkg/runner/deb/packageconfig.go | 60 +++++++++++++++++- pkg/runner/deb/packageconfig_test.go | 91 +++++++++++++++++++++++++++- 2 files changed, 147 insertions(+), 4 deletions(-) diff --git a/pkg/runner/deb/packageconfig.go b/pkg/runner/deb/packageconfig.go index b96599bf..8d266714 100644 --- a/pkg/runner/deb/packageconfig.go +++ b/pkg/runner/deb/packageconfig.go @@ -2,12 +2,15 @@ package deb import ( "fmt" + "os" + "path/filepath" "strings" ) type PackageConfig struct { - Override *[]Package `pulumi:"override,optional"` - Additional *[]string `pulumi:"additional,optional"` + OverrideDir *string `pulumi:"overrideDir,optional"` + Override *[]Package `pulumi:"override,optional"` + Additional *[]string `pulumi:"additional,optional"` } func (p *PackageConfig) UpdatePackageGroup(g *PackageGroup) error { @@ -15,6 +18,24 @@ func (p *PackageConfig) UpdatePackageGroup(g *PackageGroup) error { g.Add(Package{}.MakePackages(*p.Additional...)...) } + if p.OverrideDir != nil { + localDebs, err := getOverrideDirPackages(*p.OverrideDir) + if err != nil { + return err + } + + overrides := make([]Package, 0, len(g.packages)) + for _, pkg := range g.packages { + if localDeb, ok := localDebs[pkg.Name]; ok { + overrides = append(overrides, Package{ + Name: pkg.Name, + LocalPath: &localDeb, + }) + } + } + g.Add(overrides...) + } + if p.Override != nil { unknownPackages := []string{} @@ -33,3 +54,38 @@ func (p *PackageConfig) UpdatePackageGroup(g *PackageGroup) error { return nil } + +// Assume unique packages in the override dir. Multiple package +// versions will result in an error +func getOverrideDirPackages(dir string) (map[string]string, error) { + info, err := os.Stat(dir) + if err != nil { + if os.IsNotExist(err) { + return nil, fmt.Errorf("directory %q does not exist", dir) + } + return nil, err + } + if !info.IsDir() { + return nil, fmt.Errorf("%q exists but is not a directory", dir) + } + + files, err := filepath.Glob(filepath.Join(dir, "*.deb")) + if err != nil { + return nil, err + } + localDebs := make(map[string]string, len(files)) + for _, p := range files { + base := filepath.Base(p) + parts := strings.Split(base, "_") + if len(parts) < 3 { + return nil, fmt.Errorf("%q invalid debian package name", base) + } + name := strings.Join(parts[:len(parts)-2], "_") + if _, ok := localDebs[name]; ok { + return nil, fmt.Errorf("%q duplicate package name", base) + } else { + localDebs[name] = p + } + } + return localDebs, nil +} diff --git a/pkg/runner/deb/packageconfig_test.go b/pkg/runner/deb/packageconfig_test.go index bae76fad..da747e83 100644 --- a/pkg/runner/deb/packageconfig_test.go +++ b/pkg/runner/deb/packageconfig_test.go @@ -1,9 +1,11 @@ package deb import ( - "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "os" + "path/filepath" + "testing" ) func TestBasicPackageConfig0(t *testing.T) { @@ -67,3 +69,88 @@ func TestBasicPackageConfigErr0(t *testing.T) { assert.Equal(t, []string{"testpkg", "anotherpkg", "newpkg/dev"}, g.Args()) } } + +func TestPackageConfigBadOverrideDir(t *testing.T) { + base := t.TempDir() + overrideDir := filepath.Join(base, "overrides") + overrideDirNone := filepath.Join(base, "NoSuchDir") + overrideFile := filepath.Join(base, "file.txt") + + require.NoError(t, os.Mkdir(overrideDir, 0755)) + require.NoError(t, os.WriteFile(overrideFile, nil, 0644)) + + g := Package{}.MakePackageGroup("testpkg", "anotherpkg") + + assert.Equal(t, []string{"testpkg", "anotherpkg"}, g.Args()) + + { + c := PackageConfig{ + OverrideDir: &overrideDirNone, + } + + assert.ErrorContains(t, c.UpdatePackageGroup(g), "does not exist") + } + + { + c := PackageConfig{ + OverrideDir: &overrideFile, + } + + assert.ErrorContains(t, c.UpdatePackageGroup(g), "exists but is not a directory") + } + + { + c := PackageConfig{ + OverrideDir: &overrideDir, + } + + assert.NoError(t, c.UpdatePackageGroup(g)) + } +} + +func mkdeb(t *testing.T, dir, fname string) string { + t.Helper() + path := filepath.Join(dir, fname) + require.NoError(t, os.WriteFile(path, nil, 0644)) + return path +} + +func TestPackageConfigOverrideDir(t *testing.T) { + overrideDir := t.TempDir() + + mkdeb(t, overrideDir, "testpkg_0.0.0-1_amd64.deb") + mkdeb(t, overrideDir, "anotherpkg_3.2.1-1_amd64.deb") + mkdeb(t, overrideDir, "randompkg_0.0.0-0_amd64.deb") + + g := Package{}.MakePackageGroup("testpkg", "anotherpkg") + + assert.Equal(t, []string{"testpkg", "anotherpkg"}, g.Args()) + + { + c := PackageConfig{ + OverrideDir: &overrideDir, + Override: &[]Package{ + { + Name: "testpkg", + Version: ptr("1.2.3"), + }, + }, + } + + assert.NoError(t, c.UpdatePackageGroup(g)) + assert.Equal(t, []string{"testpkg=1.2.3", "./anotherpkg_3.2.1-1_amd64.deb"}, g.Args()) + } + + { + duplicate := mkdeb(t, overrideDir, "testpkg_3.2.1-2_amd64.deb") + c := PackageConfig{ + OverrideDir: &overrideDir, + } + + assert.ErrorContains(t, c.UpdatePackageGroup(g), "duplicate package name") + require.NoError(t, os.Remove(duplicate)) + + mkdeb(t, overrideDir, "bad-deb-format.deb") + assert.ErrorContains(t, c.UpdatePackageGroup(g), "invalid debian package name") + } +}