From 03c5c054e12eeecebdd60ccaabf133b46d97c87f Mon Sep 17 00:00:00 2001 From: Pavel Sviderski Date: Wed, 26 Mar 2025 20:38:41 +1000 Subject: [PATCH] feat(compose): client methods for inspecting images --- pkg/api/client.go | 29 +++++++++++++++++++++++++++++ pkg/api/image.go | 28 ++++++++++++++++++++++++++++ pkg/client/image.go | 20 ++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 pkg/api/image.go create mode 100644 pkg/client/image.go diff --git a/pkg/api/client.go b/pkg/api/client.go index 52629d05..f6200cee 100644 --- a/pkg/api/client.go +++ b/pkg/api/client.go @@ -2,13 +2,17 @@ package api import ( "context" + "fmt" "github.com/docker/docker/api/types/container" "github.com/psviderski/uncloud/internal/machine/api/pb" + "google.golang.org/grpc/metadata" + "slices" ) type Client interface { ContainerClient DNSClient + ImageClient MachineClient ServiceClient } @@ -27,6 +31,11 @@ type DNSClient interface { GetDomain(ctx context.Context) (string, error) } +type ImageClient interface { + InspectImage(ctx context.Context, id string) ([]MachineImage, error) + InspectRemoteImage(ctx context.Context, id string) ([]MachineRemoteImage, error) +} + type MachineClient interface { InspectMachine(ctx context.Context, id string) (*pb.MachineMember, error) ListMachines(ctx context.Context) ([]*pb.MachineMember, error) @@ -35,3 +44,23 @@ type MachineClient interface { type ServiceClient interface { InspectService(ctx context.Context, id string) (Service, error) } + +// ProxyMachinesContext returns a new context that proxies gRPC requests to the specified machines. +// If namesOrIDs is nil, all machines are included. +func ProxyMachinesContext(ctx context.Context, cli MachineClient, namesOrIDs []string) (context.Context, error) { + machines, err := cli.ListMachines(ctx) + if err != nil { + return nil, fmt.Errorf("list machines: %w", err) + } + + md := metadata.New(nil) + for _, m := range machines { + if namesOrIDs == nil || + slices.Contains(namesOrIDs, m.Machine.Name) || slices.Contains(namesOrIDs, m.Machine.Id) { + machineIP, _ := m.Machine.Network.ManagementIp.ToAddr() + md.Append("machines", machineIP.String()) + } + } + + return metadata.NewOutgoingContext(ctx, md), nil +} diff --git a/pkg/api/image.go b/pkg/api/image.go new file mode 100644 index 00000000..f027ffd4 --- /dev/null +++ b/pkg/api/image.go @@ -0,0 +1,28 @@ +package api + +import ( + "github.com/distribution/reference" + "github.com/docker/docker/api/types" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/psviderski/uncloud/internal/machine/api/pb" +) + +type MachineImage struct { + Metadata *pb.Metadata + Image types.ImageInspect +} + +// MachineRemoteImage represents an image in a remote registry fetched by a particular machine. +type MachineRemoteImage struct { + Metadata *pb.Metadata + Image RemoteImage +} + +// RemoteImage represents an image in a remote registry. The canonical reference includes the image digest. +// Either IndexManifest or ImageManifest must be set depending on whether the reference points to an index +// (multi-platform image) or a single-platform image. +type RemoteImage struct { + Reference reference.Canonical + IndexManifest *v1.Index + ImageManifest *v1.Manifest +} diff --git a/pkg/client/image.go b/pkg/client/image.go new file mode 100644 index 00000000..91f22cba --- /dev/null +++ b/pkg/client/image.go @@ -0,0 +1,20 @@ +package client + +import ( + "context" + dockerclient "github.com/docker/docker/client" + "github.com/psviderski/uncloud/pkg/api" +) + +func (cli *Client) InspectImage(ctx context.Context, id string) ([]api.MachineImage, error) { + images, err := cli.Docker.InspectImage(ctx, id) + if dockerclient.IsErrNotFound(err) { + err = api.ErrNotFound + } + + return images, err +} + +func (cli *Client) InspectRemoteImage(ctx context.Context, id string) ([]api.MachineRemoteImage, error) { + return cli.Docker.InspectRemoteImage(ctx, id) +}