feat: added optional platform selection for driver install - #466
feat: added optional platform selection for driver install#466wbeardall wants to merge 1 commit into
Conversation
…to host platform, so standard install use-case is unaffected. WASM ops do not expose platform selection.
|
The failing CI for windows is because windows has different behavior on install, using a registry key rather than a toml file on disk, which is handled in // config/dirs_windows.go:321
setKeyMust(dkey, "driver", driver.Driver.Shared.Get(PlatformTuple()))Of course, that won't be sufficient to fix the problem. We don't currently persist the platform tuple in the windows registry with the rest of the manifest. It just has a single So you have to modify the registry usage most likely so that we persist an equivalent to the platformMap into the registry manifest we use. |
zeroshade
left a comment
There was a problem hiding this comment.
Overall this looks good but there's a structural issue with the behavior.
A cross-platform re-install when a version differs would end up orphaning the original installation. You'll have to re-work how we handle re-installation with cross-platform installs.
Currently, dbc assumes only one platform is ever installed at a time for a given driver. So InstallDriver below always builds a fresh manifest and sets the platform and driverPath, replacing the manifest rather than merging and leveraging the driverMap to hold all the installed platforms. You'll have to change this behavior, and we'll have to decide what we want the behavior to be for the following cases, assuming the user already has Driver D installed, for platform A at version X:
- User installs Driver D for platform B at version X
- User installs Driver D for platform A at version Y
- User installs Driver D for platform B at version Y
Currently, in this PR, all three cases would result in a manifest that only references one platform at one version, potentially leaving behind the extracted tarball from the previous platform without the manifest referencing it for Uninstall to remove.
@amoeba how do you envision each of the scenarios above should result when using the platform option for dbc install?
Conversely, if a user does dbc uninstall should it remove for all platforms? Or do we need to add a --platform option to uninstall as well?
| func (m progressiveInstallModel) isAlreadyInstalled() bool { | ||
| return m.conflictingInfo.ID != "" && m.conflictingInfo.Version != nil && | ||
| m.conflictingInfo.Version.Equal(m.DriverPackage.Version) | ||
| } |
There was a problem hiding this comment.
you need to also update this method to check the platform in addition to the version so that we don't report a driver is already installed if you're installing for a different platform than what is currently installed.
| var validPlatformTuples = []string{ | ||
| "linux_amd64", | ||
| "linux_arm64", | ||
| "macos_amd64", | ||
| "macos_arm64", | ||
| "windows_amd64", | ||
| "windows_arm64", | ||
| } |
There was a problem hiding this comment.
At minimum, this should get expanded to cover everything that could be produced by
Lines 42 to 65 in d85580c
i.e. this is missing freebsd, openbsd, linux_x86, linux_powerpc64le, unknown_wasm64 and others.
If we don't add those platforms here, then a plain dbc install would work on FreeBSD or a x86 host, but dbc install --platform freebsd_amd64 would get rejected. This list shouldn't be kept in line with the registry index (since we already have handling to report when a driver can't be found for a given platform) instead. We should probably just duplicate the platform validation from the stdlib internal package, something like this:
// validPairs maps GOOS to its allowed GOARCH architectures.
// Sourced directly from Go's official internal/platform list.
var validPairs = map[string]map[string]bool{
"aix": {"ppc64": true},
"android": {"386": true, "amd64": true, "arm": true, "arm64": true},
"darwin": {"amd64": true, "arm64": true},
"dragonfly": {"amd64": true},
"freebsd": {"386": true, "amd64": true, "arm": true, "arm64": true, "riscv64": true},
"illumos": {"amd64": true},
"ios": {"amd64": true, "arm64": true},
"js": {"wasm": true},
"wasip1": {"wasm": true},
"linux": {
"386": true, "amd64": true, "arm": true, "arm64": true,
"loong64": true, "mips": true, "mips64": true, "mips64le": true,
"mipsle": true, "ppc64": true, "ppc64le": true, "riscv64": true, "s390x": true,
},
"netbsd": {"386": true, "amd64": true, "arm": true, "arm64": true},
"openbsd": {"386": true, "amd64": true, "arm": true, "arm64": true},
"plan9": {"386": true, "amd64": true, "arm": true},
"solaris": {"amd64": true},
"windows": {"386": true, "amd64": true, "arm": true, "arm64": true},
}
// IsValidPlatform verifies if a GOOS/GOARCH combination is compilation-ready.
func IsValidPlatform(platformTuple string) bool {
goos, goarch, found := strings.Cut(platformTuple, "_")
if !found {
return false
}
goos = strings.ToLower(strings.TrimSpace(goos))
goarch = strings.ToLower(strings.TrimSpace(goarch))
archs, osExists := validPairs[goos]
if !osExists {
return false
}
return archs[goarch]
}Obviously doing the swap from darwin -> macos and whatever else we need.
| driverName := strings.TrimSuffix( | ||
| strings.TrimSuffix(filepath.Base(m.Driver), ".tar.gz"), ".tgz") | ||
| parts := strings.Split(driverName, "_"+config.PlatformTuple()+"_") | ||
| parts := strings.Split(driverName, "_"+m.platform+"_") |
There was a problem hiding this comment.
this can cause a problem now.
If you did dbc install ./foo_linux_amd64_1.0.0.tar.gz on macOS, the manifest would record foo_linux_amd64 as the whole driver name and record it as a macOS driver unless you explicitly pass --platform linux_amd64 when doing the local package install. Should we at least attempt to try to derive the platform from the file name first?
| } | ||
|
|
||
| func (suite *SubcommandTestSuite) TestInstallWithPlatform() { | ||
| const platform = "linux_amd64" |
There was a problem hiding this comment.
when this test runs on a linux_amd64 host, it doesn't use the cross-platform branch at all and isn't testing anything useful. Make sure that the tuple we use for this test is guaranteed to differ from PlatformTuple() at runtime
| }) | ||
|
|
||
| t.Run("installs driver for explicit platform", func(t *testing.T) { | ||
| const platform = "linux_amd64" |
There was a problem hiding this comment.
same comment as elsewhere, change this so that platform is guaranteed to not match the host PlatformTuple() at runtime regardless of where this is run.
| assert.NotNil(t, manifest.DriverInfo.Version) | ||
| }) | ||
|
|
||
| t.Run("installs driver for explicit platform", func(t *testing.T) { |
There was a problem hiding this comment.
Since the subtests here all share the tmpDir and cfg this test is running against state left by the preceding "installs driver successfully" test. We should probably give this it's own t.TempDir() in this subtest
| err := p.UnmarshalText([]byte(tuple)) | ||
| assert.ErrorContains(t, err, "unknown platform") | ||
| assert.ErrorContains(t, err, "valid values are:") |
There was a problem hiding this comment.
include tuple in the message so that if something fails, we'll at least know which input failed.
Also, add assert.Empty(t, p)
| func ValidPlatformTuples() []string { | ||
| return slices.Clone(validPlatformTuples) | ||
| } |
There was a problem hiding this comment.
nothing is calling this, let's drop this. we don't need it.
| payload := jsonschema.InstallStatus{ | ||
| Status: "already installed", | ||
| Driver: m.conflictingInfo.ID, | ||
| Version: m.conflictingInfo.Version.String(), | ||
| Location: filepath.SplitList(m.cfg.Location)[0], | ||
| } |
There was a problem hiding this comment.
we should add the platform to this so that the JSON output (using --json) will also get the platform information for what was installed.
Summary
This is a contribution in response to Issue #50
The idea is to expand platform resolution, enabling users to install drivers which target non-host platforms when using the
dbc installcommand.Goals and Constraints
dbc installbehaviour; non-host always opt-inplatform.go, worth considering in case someone ends up adding weird and wonderful supported platforms to the CDNwindows_arm64forsqlite. In this case, the standardError: no package found for platform 'windows_arm64'path occurs.Potential Concerns and Considerations
The main concern I have currently is that
dbc listreads installed manifests without confirming that the driver is actually installed for the host machine. This has the potential for user confusion, as they might have a non-host driver installed, and assume that the entry indbc listmeans that they can use that driver with ADBC now!I deliberately haven't changed the
dbc listbehaviour, because it has the potential for a bigger direct impact on user experience than the rest of this PR. That said, this is how I'd go about modifyingdbc list:--all-platformsflag to thedbc list commanddbc listshould filter out any drivers without a host lib explicitly linked in the<driver>.toml, and then print exactly as current--all-platformsflag, thedbc listtable would expand with anotherPLATFORMcolumn, with the host platform being marked explicitly (e.g.linux_amd64 (*))I've put together a follow-up PR #467 implementing this.
Tests
config/platform_test.go(new file)TestPlatformUnmarshalText
TestPlatformResolve
config/config_api_test.goTestInstallDriver/records_explicit_platform_in_manifest
cmd/dbc/main_test.goTestInstallInvalidPlatformRejectedAtParse
TestInstallHelpMentionsVersionConstraints (extended)
cmd/dbc/install_test.goTestInstallWithPlatform (SubcommandTestSuite)
client_methods_test.goTestClientInstall/installs_driver_for_explicit_platform
Updated Tests
TestClientInstall, TestClientUninstall, TestInstallDriver/success,
TestInstallDriver/invalid_tarball — updated for new Install/InstallDriver signatures
Notes
gofmt-ed the PR specifically!