diff --git a/cmd/podman/images/buildx_inspect.go b/cmd/podman/images/buildx_inspect.go new file mode 100644 index 0000000000..5fe1dab607 --- /dev/null +++ b/cmd/podman/images/buildx_inspect.go @@ -0,0 +1,81 @@ +package images + +import ( + "fmt" + "strings" + + "github.com/containers/podman/v5/cmd/podman/registry" + "github.com/spf13/cobra" +) + +type buildNode struct { + Name string + Endpoint string + Status string + BuildkitVersion string + Platforms []string +} + +type buildxInspectOutput struct { + builderName string + driverName string + Nodes []buildNode +} + +var buildxInspectCmd = &cobra.Command{ + Use: "inspect", + Short: "Inspects build capabilities", + Long: "Displays information about the current builder instance (compatibility with Docker buildx inspect)", + RunE: runBuildxInspect, + Example: `podman buildx inspect + podman buildx inspect --bootstrap`, +} + +func init() { + buildxInspectCmd.Flags().Bool("bootstrap", false, "Currently a No Op for podman") + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: buildxInspectCmd, + Parent: buildxCmd, + }) +} + +func runBuildxInspect(cmd *cobra.Command, args []string) error { + info, err := registry.ContainerEngine().Info(registry.Context()) + + if err != nil { + return fmt.Errorf("retrieving podman information: %w", err) + } + + nativePlatform := fmt.Sprintf("%s/%s", info.Host.OS, info.Host.Arch) + + // Constants are based on default values for Docker buildx inspect. + defaultNode := buildNode{ + Name: "default", + Endpoint: "default", + Status: "running", + BuildkitVersion: "N/A", + Platforms: []string{nativePlatform}, + } + + defaultNode.Platforms = append(defaultNode.Platforms, info.Host.EmulatedArchitectures...) + + out := buildxInspectOutput{ + builderName: "default", + driverName: "podman", + Nodes: []buildNode{defaultNode}, + } + + fmt.Printf("Name: %s\n", out.builderName) + fmt.Printf("Driver: %s\n", out.driverName) + fmt.Println() + + fmt.Println("Nodes:") + fmt.Printf("Name: %s\n", out.Nodes[0].Name) + fmt.Printf("Endpoint: %s\n", out.Nodes[0].Endpoint) + fmt.Printf("Status: %s\n", out.Nodes[0].Status) + fmt.Printf("Buildkit version: %s\n", out.Nodes[0].BuildkitVersion) + + fmt.Printf("Platforms: %s\n", strings.Join(out.Nodes[0].Platforms, ", ")) + fmt.Println("Labels: ") + return nil +} diff --git a/libpod/define/info.go b/libpod/define/info.go index bc37d81396..967a664c99 100644 --- a/libpod/define/info.go +++ b/libpod/define/info.go @@ -66,6 +66,8 @@ type HostInfo struct { Uptime string `json:"uptime"` Variant string `json:"variant"` Linkmode string `json:"linkmode"` + + EmulatedArchitectures []string `json:"emulatedArchitectures,omitempty"` } // RemoteSocket describes information about the API socket diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index fd7c32eea9..204c851edc 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -15,6 +15,7 @@ import ( "github.com/containers/podman/v5/libpod/define" "github.com/containers/podman/v5/pkg/domain/entities" "github.com/containers/podman/v5/pkg/domain/entities/reports" + "github.com/containers/podman/v5/pkg/emulation" "github.com/containers/podman/v5/pkg/util" "github.com/containers/storage" "github.com/containers/storage/pkg/directory" @@ -27,6 +28,9 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { if err != nil { return nil, err } + + info.Host.EmulatedArchitectures = emulation.Registered() + info.Host.RemoteSocket = &define.RemoteSocket{Path: ic.Libpod.RemoteURI()} // `podman system connection add` invokes podman via ssh to fill in connection string. Here diff --git a/test/e2e/buildx_inspect_test.go b/test/e2e/buildx_inspect_test.go new file mode 100644 index 0000000000..2ce79861b4 --- /dev/null +++ b/test/e2e/buildx_inspect_test.go @@ -0,0 +1,38 @@ +//go:build linux || freebsd + +package integration + +import ( + "encoding/json" + "regexp" + "strings" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman buildx inspect", func() { + It("podman buildx inspect", func() { + session := podmanTest.PodmanExitCleanly("buildx", "inspect") + out := session.OutputToString() + + session_bootstrap := podmanTest.PodmanExitCleanly("buildx", "inspect", "--bootstrap") + out_bootstrap := session_bootstrap.OutputToString() + Expect(out_bootstrap).To(Equal(out), "Output of 'podman buildx inspect' and 'podman buildx inspect --bootstrap' should be the same") + + emuInfo := podmanTest.PodmanExitCleanly("info", "--format", "{{json .Host.EmulatedArchitectures}}") + var emuArchs []string + Expect(json.Unmarshal([]byte(emuInfo.OutputToString()), &emuArchs)).To(Succeed()) + + nativeInfo := podmanTest.PodmanExitCleanly("info", "--format", "{{.Host.OS}}/{{.Host.Arch}}") + nativePlat := strings.TrimSpace(nativeInfo.OutputToString()) + Expect(nativePlat).ToNot(BeEmpty()) + + expected := append(emuArchs, nativePlat) + + for _, p := range expected { + re := regexp.MustCompile(`(?s)Platforms:.*\b` + regexp.QuoteMeta(p) + `\b`) + Expect(out).To(MatchRegexp(re.String()), "missing %q in:\n%s", p, out) + } + }) +})