gabrieldemarmiesse / python-on-whales

An awesome Python wrapper for an awesome Docker CLI!
MIT License
537 stars 100 forks source link

Implement `DockerClient.version()` #591

Closed LewisGaul closed 3 months ago

LewisGaul commented 3 months ago

This would be useful for skipping certain tests that cover features not available on older versions, as well as perhaps being needed in the main code at some point if we care about supporting more than just the latest version of docker/podman. Users may also find this useful.

Note the API already exists, it just raises NotImplementedError - just creating this issue to track/discuss it and have something to link to from other issues :)

LewisGaul commented 3 months ago

Would be good to discuss how we want to solve this.

Docker/podman have two version commands: docker --version and docker version. The former is the simple client version, returning something like "Docker version 20.10.23, build 7155243". The latter connects to the server/engine side (which may be on a remote host) so can fail (meaning it can be used as a basic connectivity check) and provides more information. I think DockerClient.version() should correspond to the latter to provide that full information (server-side version is likely more relevant in general, and the connection check can be useful). [Aside: awkwardly, when you want to check whether a feature is supported you might actually have to care about the minimum of the client and server versions...]

Examples:

Server: Docker Engine - Community Engine: Version: 20.10.23 API version: 1.41 (minimum version 1.12) Go version: go1.18.10 Git commit: 6051f14 Built: Thu Jan 19 17:34:14 2023 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.6.21 GitCommit: 3dce8eb055cbb6872793272b4f20ed16117344f8 runc: Version: 1.1.7 GitCommit: v1.1.7-0-g860f061 docker-init: Version: 0.19.0 GitCommit: de40ad0

* With remote docker connection, using JSON format

docker version -f '{{json .}}' | jq { "Client": { "Platform": { "Name": "Docker Engine - Community" }, "Version": "20.10.17", "ApiVersion": "1.41", "DefaultAPIVersion": "1.41", "GitCommit": "100c701", "GoVersion": "go1.17.11", "Os": "linux", "Arch": "amd64", "BuildTime": "Mon Jun 6 23:05:12 2022", "Context": "default", "Experimental": true }, "Server": { "Platform": { "Name": "Docker Engine - Community" }, "Components": [ { "Name": "Engine", "Version": "20.10.23", "Details": { "ApiVersion": "1.41", "Arch": "amd64", "BuildTime": "Thu Jan 19 17:34:14 2023", "Experimental": "false", "GitCommit": "6051f14", "GoVersion": "go1.18.10", "KernelVersion": "5.4.0-52-generic", "MinAPIVersion": "1.12", "Os": "linux" } }, { "Name": "containerd", "Version": "1.6.21", "Details": { "GitCommit": "3dce8eb055cbb6872793272b4f20ed16117344f8" } }, { "Name": "runc", "Version": "1.1.7", "Details": { "GitCommit": "v1.1.7-0-g860f061" } }, { "Name": "docker-init", "Version": "0.19.0", "Details": { "GitCommit": "de40ad0" } } ], "Version": "20.10.23", "ApiVersion": "1.41", "MinAPIVersion": "1.12", "GitCommit": "6051f14", "GoVersion": "go1.18.10", "Os": "linux", "Arch": "amd64", "KernelVersion": "5.4.0-52-generic", "BuildTime": "2023-01-19T17:34:14.000000000+00:00" } }

* Local podman in JSON format (has no daemon so doesn't report server information)

podman version -f '{{json .}}' | jq { "Client": { "APIVersion": "4.4.1", "Version": "4.4.1", "GoVersion": "go1.19.6", "GitCommit": "", "BuiltTime": "Thu Jun 15 07:39:56 2023", "Built": 1686839996, "OsArch": "linux/amd64", "Os": "linux" } }

* Remote podman connection in JSON format

podman --url ssh://user@host:22/run/user/$(id -u user)/podman/podman.sock version -f '{{json .}}' | jq { "Client": { "APIVersion": "4.4.1", "Version": "4.4.1", "GoVersion": "go1.19.6", "GitCommit": "", "BuiltTime": "Thu Jun 15 07:39:56 2023", "Built": 1686839996, "OsArch": "linux/amd64", "Os": "linux" }, "Server": { "APIVersion": "4.6.1", "Version": "4.6.1", "GoVersion": "go1.20.6", "GitCommit": "", "BuiltTime": "Mon Oct 9 09:15:55 2023", "Built": 1696868155, "OsArch": "linux/amd64", "Os": "linux" } }

So, what do we want to return from DockerClient.version()? I think I'd go for a pydantic object that fits the above examples (although it's a bit awkward that podman doesn't always give the server information).

Note that docker's system info (docker [system] info, supported via SystemInfo pydantic model) does already include the server version, but not the client version. Also podman seems to have completely broken compatibility with this command (see https://github.com/gabrieldemarmiesse/python-on-whales/issues/592).

LewisGaul commented 3 months ago

I put up a draft PR (https://github.com/gabrieldemarmiesse/python-on-whales/pull/593) that implements the API with my suggested approach. Getting compatibility between docker and podman is a bit of a pain (notice the different spelling of APIVersion / ApiVersion and BuildTime / BuiltTime), but doable if we can drop support for pydantic v1 (note their version policy says v1 support will be stopped in June 2024)?