mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
feat(logs): update 'uc logs' command to support filtering by service/container
This commit is contained in:
+5
-1
@@ -46,12 +46,16 @@ func LogStreamTypeToProto(s LogStreamType) pb.LogEntry_StreamType {
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceLogsOptions specifies parameters for ServiceLogs.
|
||||
// ServiceLogsOptions specifies parameters for ServiceLogs and MachineLogs.
|
||||
type ServiceLogsOptions struct {
|
||||
Follow bool
|
||||
Tail int
|
||||
Since string
|
||||
Until string
|
||||
// Containers filters logs to only include the specified service containers (names, full IDs,
|
||||
// or unique ID prefixes). If empty, logs from all containers in the service are included.
|
||||
// Ignored by MachineLogs.
|
||||
Containers []string
|
||||
// Machines filters logs to only include containers running on the specified machines (names or IDs).
|
||||
// If empty, logs from all machines are included.
|
||||
Machines []string
|
||||
|
||||
@@ -519,6 +519,29 @@ type MachineServiceContainer struct {
|
||||
Container ServiceContainer
|
||||
}
|
||||
|
||||
// FindContainer returns the service container by exact name, ID, or unique ID prefix.
|
||||
// Returns ErrNotFound if no container matches, or an error if an ID prefix matches more than one container.
|
||||
func (s *Service) FindContainer(nameOrID string) (MachineServiceContainer, error) {
|
||||
var prefixMatches []MachineServiceContainer
|
||||
for _, c := range append(s.Containers, s.HookContainers...) {
|
||||
if c.Container.ID == nameOrID || c.Container.Name == nameOrID {
|
||||
return c, nil
|
||||
}
|
||||
if strings.HasPrefix(c.Container.ID, nameOrID) {
|
||||
prefixMatches = append(prefixMatches, c)
|
||||
}
|
||||
}
|
||||
|
||||
if len(prefixMatches) == 1 {
|
||||
return prefixMatches[0], nil
|
||||
}
|
||||
if len(prefixMatches) > 1 {
|
||||
return MachineServiceContainer{}, fmt.Errorf("multiple containers found with ID prefix '%s'", nameOrID)
|
||||
}
|
||||
|
||||
return MachineServiceContainer{}, ErrNotFound
|
||||
}
|
||||
|
||||
// MachineIDs returns a list of unique machine IDs where the service containers are running.
|
||||
func (s *Service) MachineIDs() []string {
|
||||
ids := mapset.NewSet[string]()
|
||||
|
||||
+1
-20
@@ -256,26 +256,7 @@ func (cli *Client) InspectContainer(
|
||||
return api.MachineServiceContainer{}, fmt.Errorf("inspect service: %w", err)
|
||||
}
|
||||
|
||||
prefixMatchCandidates := []api.MachineServiceContainer{}
|
||||
for _, c := range append(svc.Containers, svc.HookContainers...) {
|
||||
if c.Container.ID == containerNameOrID ||
|
||||
c.Container.Name == containerNameOrID {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(c.Container.ID, containerNameOrID) {
|
||||
prefixMatchCandidates = append(prefixMatchCandidates, c)
|
||||
}
|
||||
}
|
||||
|
||||
if len(prefixMatchCandidates) == 1 {
|
||||
return prefixMatchCandidates[0], nil
|
||||
} else if len(prefixMatchCandidates) > 1 {
|
||||
return api.MachineServiceContainer{}, fmt.Errorf(
|
||||
"multiple containers found with ID prefix '%s'", containerNameOrID)
|
||||
}
|
||||
|
||||
return api.MachineServiceContainer{}, api.ErrNotFound
|
||||
return svc.FindContainer(containerNameOrID)
|
||||
}
|
||||
|
||||
// StartContainer starts the specified container within the service.
|
||||
|
||||
+20
-4
@@ -5,6 +5,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"slices"
|
||||
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
@@ -24,11 +26,25 @@ func (cli *Client) ServiceLogs(
|
||||
return svc, nil, fmt.Errorf("inspect service: %w", err)
|
||||
}
|
||||
|
||||
allContainers := append(svc.Containers, svc.HookContainers...)
|
||||
if len(allContainers) == 0 {
|
||||
containers := append(svc.Containers, svc.HookContainers...)
|
||||
if len(containers) == 0 {
|
||||
return svc, nil, fmt.Errorf("no containers found for service: %s", serviceNameOrID)
|
||||
}
|
||||
|
||||
if len(opts.Containers) > 0 {
|
||||
selected := make(map[string]api.MachineServiceContainer, len(opts.Containers))
|
||||
for _, nameOrID := range opts.Containers {
|
||||
ctr, err := svc.FindContainer(nameOrID)
|
||||
if err != nil {
|
||||
return svc, nil, fmt.Errorf("find container '%s' in service '%s': %w",
|
||||
nameOrID, serviceNameOrID, err)
|
||||
}
|
||||
selected[ctr.Container.ID] = ctr
|
||||
}
|
||||
|
||||
containers = slices.Collect(maps.Values(selected))
|
||||
}
|
||||
|
||||
machines, err := cli.ListMachines(ctx, &api.MachineFilter{
|
||||
NamesOrIDs: opts.Machines,
|
||||
})
|
||||
@@ -36,8 +52,8 @@ func (cli *Client) ServiceLogs(
|
||||
return svc, nil, fmt.Errorf("list machines: %w", err)
|
||||
}
|
||||
|
||||
ctrStreams := make([]<-chan api.ServiceLogEntry, 0, len(allContainers))
|
||||
for _, ctr := range allContainers {
|
||||
ctrStreams := make([]<-chan api.ServiceLogEntry, 0, len(containers))
|
||||
for _, ctr := range containers {
|
||||
// Skip containers not running on the specified machines.
|
||||
m := machines.FindByNameOrID(ctr.MachineID)
|
||||
if len(opts.Machines) > 0 && m == nil {
|
||||
|
||||
Reference in New Issue
Block a user