mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
chore: update ListVolumes to accept a filter
This commit is contained in:
+8
-4
@@ -48,20 +48,24 @@ type ServiceClient interface {
|
|||||||
|
|
||||||
// ProxyMachinesContext returns a new context that proxies gRPC requests to the specified machines.
|
// ProxyMachinesContext returns a new context that proxies gRPC requests to the specified machines.
|
||||||
// If namesOrIDs is nil, all machines are included.
|
// If namesOrIDs is nil, all machines are included.
|
||||||
func ProxyMachinesContext(ctx context.Context, cli MachineClient, namesOrIDs []string) (context.Context, error) {
|
func ProxyMachinesContext(
|
||||||
|
ctx context.Context, cli MachineClient, namesOrIDs []string,
|
||||||
|
) (context.Context, MachineMembersList, error) {
|
||||||
machines, err := cli.ListMachines(ctx)
|
machines, err := cli.ListMachines(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("list machines: %w", err)
|
return nil, nil, fmt.Errorf("list machines: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var proxiedMachines MachineMembersList
|
||||||
md := metadata.New(nil)
|
md := metadata.New(nil)
|
||||||
for _, m := range machines {
|
for _, m := range machines {
|
||||||
if namesOrIDs == nil ||
|
if len(namesOrIDs) == 0 ||
|
||||||
slices.Contains(namesOrIDs, m.Machine.Name) || slices.Contains(namesOrIDs, m.Machine.Id) {
|
slices.Contains(namesOrIDs, m.Machine.Name) || slices.Contains(namesOrIDs, m.Machine.Id) {
|
||||||
|
proxiedMachines = append(proxiedMachines, m)
|
||||||
machineIP, _ := m.Machine.Network.ManagementIp.ToAddr()
|
machineIP, _ := m.Machine.Network.ManagementIp.ToAddr()
|
||||||
md.Append("machines", machineIP.String())
|
md.Append("machines", machineIP.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return metadata.NewOutgoingContext(ctx, md), nil
|
return metadata.NewOutgoingContext(ctx, md), proxiedMachines, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,3 +18,13 @@ func (m MachineMembersList) FindByManagementIP(ip string) *pb.MachineMember {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m MachineMembersList) FindByNameOrID(nameOrID string) *pb.MachineMember {
|
||||||
|
for _, machine := range m {
|
||||||
|
if machine.Machine.Id == nameOrID || machine.Machine.Name == nameOrID {
|
||||||
|
return machine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -195,3 +196,47 @@ type MachineVolume struct {
|
|||||||
// Volume is the Docker volume model.
|
// Volume is the Docker volume model.
|
||||||
Volume volume.Volume
|
Volume volume.Volume
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VolumeFilter defines criteria to filter volumes in ListVolumes.
|
||||||
|
type VolumeFilter struct {
|
||||||
|
// Driver filters volumes by storage driver name.
|
||||||
|
Driver string
|
||||||
|
// Labels filters volumes by label key-value pairs. Volumes must match all labels.
|
||||||
|
Labels map[string]string
|
||||||
|
// MachineIDs filters volumes to those on the specified machines (names or IDs).
|
||||||
|
Machines []string
|
||||||
|
// Names filters volumes by name. Volumes must match one of the names.
|
||||||
|
Names []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesFilter checks if a volume matches the specified filter criteria.
|
||||||
|
func (v *MachineVolume) MatchesFilter(filter *VolumeFilter) bool {
|
||||||
|
if filter == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Filter by name.
|
||||||
|
if len(filter.Names) > 0 && !slices.Contains(filter.Names, v.Volume.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Filter by driver.
|
||||||
|
if filter.Driver != "" && v.Volume.Driver != filter.Driver {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Filter by labels.
|
||||||
|
for key, value := range filter.Labels {
|
||||||
|
labelValue, exists := v.Volume.Labels[key]
|
||||||
|
if !exists || labelValue != value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Filter by machines.
|
||||||
|
if len(filter.Machines) > 0 {
|
||||||
|
if !slices.ContainsFunc(filter.Machines, func(nameOrID string) bool {
|
||||||
|
return v.MachineName == nameOrID || v.MachineID == nameOrID
|
||||||
|
}) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,14 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/distribution/reference"
|
"github.com/distribution/reference"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
"github.com/psviderski/uncloud/internal/secret"
|
"github.com/psviderski/uncloud/internal/secret"
|
||||||
"github.com/psviderski/uncloud/pkg/api"
|
"github.com/psviderski/uncloud/pkg/api"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceSpecResolver transforms user-provided service specs into deployment-ready form.
|
// ServiceSpecResolver transforms user-provided service specs into deployment-ready form.
|
||||||
@@ -187,7 +188,7 @@ func (r *ImageDigestResolver) Resolve(image, policy string) (string, error) {
|
|||||||
// resolveAlways resolves the image to the image with the digest by querying the registry from all machines.
|
// resolveAlways resolves the image to the image with the digest by querying the registry from all machines.
|
||||||
func (r *ImageDigestResolver) resolveAlways(image string) (string, error) {
|
func (r *ImageDigestResolver) resolveAlways(image string) (string, error) {
|
||||||
// TODO: broadcast to a subset of machines in large clusters to avoid being rate-limited by the registry.
|
// TODO: broadcast to a subset of machines in large clusters to avoid being rate-limited by the registry.
|
||||||
ctx, err := api.ProxyMachinesContext(r.Ctx, r.Client, nil)
|
ctx, _, err := api.ProxyMachinesContext(r.Ctx, r.Client, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("create request context to broadcast to all machines: %w", err)
|
return "", fmt.Errorf("create request context to broadcast to all machines: %w", err)
|
||||||
}
|
}
|
||||||
@@ -215,7 +216,7 @@ func (r *ImageDigestResolver) resolveAlways(image string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *ImageDigestResolver) resolveMissing(image string) (string, error) {
|
func (r *ImageDigestResolver) resolveMissing(image string) (string, error) {
|
||||||
ctx, err := api.ProxyMachinesContext(r.Ctx, r.Client, nil)
|
ctx, _, err := api.ProxyMachinesContext(r.Ctx, r.Client, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("create request context to broadcast to all machines: %w", err)
|
return "", fmt.Errorf("create request context to broadcast to all machines: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-10
@@ -34,7 +34,7 @@ func (cli *Client) CreateVolume(
|
|||||||
|
|
||||||
vol, err := cli.Docker.CreateVolume(ctx, opts)
|
vol, err := cli.Docker.CreateVolume(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, fmt.Errorf("create volume on machine '%s': %w", machine.Machine.Name, err)
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp = api.MachineVolume{
|
resp = api.MachineVolume{
|
||||||
@@ -48,21 +48,21 @@ func (cli *Client) CreateVolume(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListVolumes returns a list of all volumes on the cluster machines.
|
// ListVolumes returns a list of all volumes on the cluster machines.
|
||||||
func (cli *Client) ListVolumes(ctx context.Context) ([]api.MachineVolume, error) {
|
func (cli *Client) ListVolumes(ctx context.Context, filter *api.VolumeFilter) ([]api.MachineVolume, error) {
|
||||||
machines, err := cli.ListMachines(ctx)
|
// Broadcast the volume list request to the specified machines in the filter or all machines if filter is nil.
|
||||||
if err != nil {
|
var proxyMachines []string
|
||||||
return nil, fmt.Errorf("list machines: %w", err)
|
if filter != nil {
|
||||||
|
proxyMachines = filter.Machines
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast the volume list request to all machines.
|
listCtx, machines, err := api.ProxyMachinesContext(ctx, cli, proxyMachines)
|
||||||
listCtx, err := api.ProxyMachinesContext(ctx, cli, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("create request context to broadcast to all machines: %w", err)
|
return nil, fmt.Errorf("create request context to broadcast to all machines: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
machineVolumes, err := cli.Docker.ListVolumes(listCtx, volume.ListOptions{})
|
machineVolumes, err := cli.Docker.ListVolumes(listCtx, volume.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("list volumes: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var volumes []api.MachineVolume
|
var volumes []api.MachineVolume
|
||||||
@@ -95,6 +95,17 @@ func (cli *Client) ListVolumes(ctx context.Context) ([]api.MachineVolume, error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter volumes based on the provided filter criteria.
|
||||||
|
if filter != nil {
|
||||||
|
var filteredVolumes []api.MachineVolume
|
||||||
|
for _, vol := range volumes {
|
||||||
|
if vol.MatchesFilter(filter) {
|
||||||
|
filteredVolumes = append(filteredVolumes, vol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
volumes = filteredVolumes
|
||||||
|
}
|
||||||
|
|
||||||
return volumes, nil
|
return volumes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,8 +126,7 @@ func (cli *Client) RemoveVolume(ctx context.Context, machineNameOrID, volumeName
|
|||||||
if dockerclient.IsErrNotFound(err) {
|
if dockerclient.IsErrNotFound(err) {
|
||||||
return api.ErrNotFound
|
return api.ErrNotFound
|
||||||
}
|
}
|
||||||
return fmt.Errorf("remove volume '%s' from machine '%s': %w",
|
return err
|
||||||
volumeName, machine.Machine.Name, err)
|
|
||||||
}
|
}
|
||||||
pw.Event(progress.RemovedEvent(eventID))
|
pw.Event(progress.RemovedEvent(eventID))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user