mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 08:31:03 +00:00
Merge pull request #1 from christophe-duc/feature/add_pod_support
feature: add pod support in the container pane
This commit is contained in:
commit
0730120daa
35 changed files with 2277 additions and 625 deletions
|
|
@ -55,11 +55,13 @@ pkg/
|
|||
- `pkg/commands/runtime_socket.go` - Socket mode implementation using Podman REST API bindings
|
||||
- `pkg/commands/runtime_libpod.go` - Direct libpod implementation (Linux+CGO only)
|
||||
- `pkg/commands/runtime_libpod_stub.go` - Stub for non-Linux platforms
|
||||
- `pkg/commands/runtime_types.go` - Custom types (ContainerSummary, ImageSummary, etc.)
|
||||
- `pkg/commands/runtime_types.go` - Custom types (ContainerSummary, ImageSummary, PodSummary, etc.)
|
||||
|
||||
**Podman integration:**
|
||||
- `pkg/commands/podman.go` - Main client connection, auto-detection, and initialization
|
||||
- `pkg/commands/container.go` - Container wrapper operations
|
||||
- `pkg/commands/pod.go` - Pod wrapper with state and container count methods
|
||||
- `pkg/commands/container_list_item.go` - Unified wrapper for pods/containers in list view
|
||||
- `pkg/commands/image.go` - Image wrapper operations
|
||||
- `pkg/commands/volume.go` - Volume wrapper operations
|
||||
- `pkg/commands/network.go` - Network wrapper operations
|
||||
|
|
@ -102,6 +104,9 @@ ListContainers() / InspectContainer() / ContainerStats()
|
|||
StartContainer() / StopContainer() / PauseContainer() / UnpauseContainer()
|
||||
RestartContainer() / RemoveContainer() / PruneContainers() / ContainerTop()
|
||||
|
||||
// Pod operations
|
||||
ListPods() - Returns all pods with their metadata
|
||||
|
||||
// Image operations
|
||||
ListImages() / InspectImage() / ImageHistory() / RemoveImage() / PruneImages()
|
||||
|
||||
|
|
|
|||
1284
coverage.txt
1284
coverage.txt
File diff suppressed because it is too large
Load diff
4
go.mod
4
go.mod
|
|
@ -7,7 +7,6 @@ require (
|
|||
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||
github.com/containers/podman/v5 v5.7.1
|
||||
github.com/docker/docker v28.5.1+incompatible
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/go-errors/errors v1.5.1
|
||||
github.com/gookit/color v1.5.0
|
||||
|
|
@ -27,6 +26,7 @@ require (
|
|||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||
github.com/stretchr/testify v1.11.1
|
||||
go.podman.io/common v0.66.1
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
)
|
||||
|
||||
|
|
@ -63,6 +63,7 @@ require (
|
|||
github.com/disiqueira/gotree/v3 v3.0.2 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker v28.5.1+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.4 // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8 // indirect
|
||||
|
|
@ -155,7 +156,6 @@ require (
|
|||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
go.podman.io/common v0.66.1 // indirect
|
||||
go.podman.io/image/v5 v5.38.0 // indirect
|
||||
go.podman.io/storage v1.61.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
|
|
|
|||
84
pkg/commands/container_list_item.go
Normal file
84
pkg/commands/container_list_item.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package commands
|
||||
|
||||
// ContainerListItem represents either a pod or a container in the unified list view.
|
||||
// This allows the container panel to display both pods and containers.
|
||||
type ContainerListItem struct {
|
||||
IsPod bool
|
||||
Pod *Pod // Set if IsPod is true
|
||||
Container *Container // Set if IsPod is false
|
||||
Indent int // 0 for pods/standalone containers, 2 for containers in pods
|
||||
}
|
||||
|
||||
// ID returns the unique ID for the item.
|
||||
func (c *ContainerListItem) ID() string {
|
||||
if c.IsPod && c.Pod != nil {
|
||||
return c.Pod.ID
|
||||
}
|
||||
if c.Container != nil {
|
||||
return c.Container.ID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Name returns the display name for the item.
|
||||
func (c *ContainerListItem) Name() string {
|
||||
if c.IsPod && c.Pod != nil {
|
||||
return c.Pod.Name
|
||||
}
|
||||
if c.Container != nil {
|
||||
return c.Container.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// State returns the state for the item.
|
||||
func (c *ContainerListItem) State() string {
|
||||
if c.IsPod && c.Pod != nil {
|
||||
return c.Pod.State()
|
||||
}
|
||||
if c.Container != nil {
|
||||
return c.Container.Summary.State
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetContainers returns the containers if this is a pod, nil otherwise.
|
||||
func (c *ContainerListItem) GetContainers() []*Container {
|
||||
if c.IsPod && c.Pod != nil {
|
||||
return c.Pod.Containers
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsInPod returns true if this is a container that belongs to a pod.
|
||||
func (c *ContainerListItem) IsInPod() bool {
|
||||
if c.IsPod {
|
||||
return false
|
||||
}
|
||||
if c.Container != nil {
|
||||
return c.Container.Summary.Pod != ""
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PodID returns the pod ID if this container is in a pod, empty string otherwise.
|
||||
func (c *ContainerListItem) PodID() string {
|
||||
if c.IsPod && c.Pod != nil {
|
||||
return c.Pod.ID
|
||||
}
|
||||
if c.Container != nil {
|
||||
return c.Container.Summary.Pod
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// PodName returns the pod name if this container is in a pod, empty string otherwise.
|
||||
func (c *ContainerListItem) PodName() string {
|
||||
if c.IsPod && c.Pod != nil {
|
||||
return c.Pod.Name
|
||||
}
|
||||
if c.Container != nil {
|
||||
return c.Container.Summary.PodName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
36
pkg/commands/pod.go
Normal file
36
pkg/commands/pod.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Pod represents a Podman pod with its containers.
|
||||
type Pod struct {
|
||||
ID string
|
||||
Name string
|
||||
Summary PodSummary
|
||||
Containers []*Container // Non-infra containers in this pod
|
||||
OSCommand *OSCommand
|
||||
Log *logrus.Entry
|
||||
}
|
||||
|
||||
// State returns the pod state.
|
||||
func (p *Pod) State() string {
|
||||
return p.Summary.Status
|
||||
}
|
||||
|
||||
// HasContainers returns true if the pod has non-infra containers.
|
||||
func (p *Pod) HasContainers() bool {
|
||||
return len(p.Containers) > 0
|
||||
}
|
||||
|
||||
// GetRunningContainerCount returns the number of running containers in the pod.
|
||||
func (p *Pod) GetRunningContainerCount() int {
|
||||
count := 0
|
||||
for _, c := range p.Containers {
|
||||
if c.Summary.State == "running" {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import (
|
|||
ogLog "log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -347,15 +348,35 @@ func calculateMemoryPercentageFromEntry(stats ContainerStatsEntry) float64 {
|
|||
return 0.0
|
||||
}
|
||||
|
||||
func (c *PodmanCommand) RefreshContainersAndServices(currentServices []*Service, currentContainers []*Container) ([]*Container, []*Service, error) {
|
||||
func (c *PodmanCommand) RefreshContainersAndServices(currentServices []*Service, currentItems []*ContainerListItem) ([]*ContainerListItem, []*Service, error) {
|
||||
c.ServiceMutex.Lock()
|
||||
defer c.ServiceMutex.Unlock()
|
||||
|
||||
// Extract existing containers from current items
|
||||
var currentContainers []*Container
|
||||
for _, item := range currentItems {
|
||||
if !item.IsPod && item.Container != nil {
|
||||
currentContainers = append(currentContainers, item.Container)
|
||||
}
|
||||
}
|
||||
|
||||
containers, err := c.GetContainers(currentContainers)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Get pods
|
||||
ctx := context.Background()
|
||||
podSummaries, err := c.Runtime.ListPods(ctx)
|
||||
if err != nil {
|
||||
// Don't fail if pods can't be listed, just log and continue without pods
|
||||
c.Log.Warnf("Failed to list pods: %v", err)
|
||||
podSummaries = nil
|
||||
}
|
||||
|
||||
// Build the unified list
|
||||
items := c.buildContainerListItems(containers, podSummaries)
|
||||
|
||||
var services []*Service
|
||||
// we only need to get these services once because they won't change in the runtime of the program
|
||||
if currentServices != nil {
|
||||
|
|
@ -367,9 +388,98 @@ func (c *PodmanCommand) RefreshContainersAndServices(currentServices []*Service,
|
|||
}
|
||||
}
|
||||
|
||||
c.assignContainersToServices(containers, services)
|
||||
// Extract just containers for service assignment
|
||||
var containersForServices []*Container
|
||||
for _, item := range items {
|
||||
if !item.IsPod && item.Container != nil {
|
||||
containersForServices = append(containersForServices, item.Container)
|
||||
}
|
||||
}
|
||||
c.assignContainersToServices(containersForServices, services)
|
||||
|
||||
return containers, services, nil
|
||||
return items, services, nil
|
||||
}
|
||||
|
||||
// buildContainerListItems creates a unified list of pods and containers
|
||||
func (c *PodmanCommand) buildContainerListItems(containers []*Container, podSummaries []PodSummary) []*ContainerListItem {
|
||||
var items []*ContainerListItem
|
||||
|
||||
// Create a map of pod ID -> pod summary
|
||||
podMap := make(map[string]PodSummary)
|
||||
for _, ps := range podSummaries {
|
||||
podMap[ps.ID] = ps
|
||||
}
|
||||
|
||||
// Create a map of pod ID -> containers in that pod
|
||||
podContainers := make(map[string][]*Container)
|
||||
var standaloneContainers []*Container
|
||||
|
||||
for _, ctr := range containers {
|
||||
// Skip infra containers
|
||||
if ctr.Summary.IsInfra {
|
||||
continue
|
||||
}
|
||||
|
||||
if ctr.Summary.Pod != "" {
|
||||
podContainers[ctr.Summary.Pod] = append(podContainers[ctr.Summary.Pod], ctr)
|
||||
} else {
|
||||
standaloneContainers = append(standaloneContainers, ctr)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort pod IDs for deterministic ordering
|
||||
podIDs := make([]string, 0, len(podMap))
|
||||
for podID := range podMap {
|
||||
podIDs = append(podIDs, podID)
|
||||
}
|
||||
sort.Slice(podIDs, func(i, j int) bool {
|
||||
return podMap[podIDs[i]].Name < podMap[podIDs[j]].Name
|
||||
})
|
||||
|
||||
// Add pods and their containers
|
||||
for _, podID := range podIDs {
|
||||
ps := podMap[podID]
|
||||
// Create pod object
|
||||
pod := &Pod{
|
||||
ID: ps.ID,
|
||||
Name: ps.Name,
|
||||
Summary: ps,
|
||||
Containers: podContainers[podID],
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
}
|
||||
|
||||
// Add pod item
|
||||
items = append(items, &ContainerListItem{
|
||||
IsPod: true,
|
||||
Pod: pod,
|
||||
Indent: 0,
|
||||
})
|
||||
|
||||
// Add containers in this pod with indent
|
||||
for _, ctr := range podContainers[podID] {
|
||||
// Set pod name on container if not already set
|
||||
if ctr.Summary.PodName == "" {
|
||||
ctr.Summary.PodName = ps.Name
|
||||
}
|
||||
items = append(items, &ContainerListItem{
|
||||
IsPod: false,
|
||||
Container: ctr,
|
||||
Indent: 2,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Add standalone containers
|
||||
for _, ctr := range standaloneContainers {
|
||||
items = append(items, &ContainerListItem{
|
||||
IsPod: false,
|
||||
Container: ctr,
|
||||
Indent: 0,
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
func (c *PodmanCommand) assignContainersToServices(containers []*Container, services []*Service) {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ type ContainerRuntime interface {
|
|||
RemoveNetwork(ctx context.Context, name string) error
|
||||
PruneNetworks(ctx context.Context) error
|
||||
|
||||
// Pod operations
|
||||
ListPods(ctx context.Context) ([]PodSummary, error)
|
||||
|
||||
// Events streams container/image/volume/network events
|
||||
Events(ctx context.Context) (<-chan Event, <-chan error)
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ func (r *LibpodRuntime) ContainerTop(ctx context.Context, id string) ([]string,
|
|||
// Parse the result: first line is headers, rest are process rows
|
||||
// Each line is space-separated fields
|
||||
headers := splitFields(result[0])
|
||||
var processes [][]string
|
||||
processes := make([][]string, 0, len(result)-1)
|
||||
for _, line := range result[1:] {
|
||||
processes = append(processes, splitFields(line))
|
||||
}
|
||||
|
|
@ -309,6 +309,37 @@ func (r *LibpodRuntime) PruneNetworks(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Pod operations
|
||||
|
||||
// ListPods returns all pods.
|
||||
func (r *LibpodRuntime) ListPods(ctx context.Context) ([]PodSummary, error) {
|
||||
pods, err := r.runtime.GetAllPods()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]PodSummary, len(pods))
|
||||
for i, pod := range pods {
|
||||
status, err := pod.GetPodStatus()
|
||||
if err != nil {
|
||||
status = "unknown"
|
||||
}
|
||||
config, configErr := pod.Config()
|
||||
infraID := ""
|
||||
if configErr == nil && config != nil {
|
||||
infraID = config.InfraContainerID
|
||||
}
|
||||
result[i] = PodSummary{
|
||||
ID: pod.ID(),
|
||||
Name: pod.Name(),
|
||||
Status: status,
|
||||
Created: pod.CreatedTime(),
|
||||
InfraID: infraID,
|
||||
Labels: pod.Labels(),
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Events streams container runtime events.
|
||||
// For libpod, we use a polling approach since direct event streaming requires more setup.
|
||||
func (r *LibpodRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
|
||||
|
|
@ -361,6 +392,14 @@ func convertLibpodContainerList(ctrs []*libpod.Container) ([]ContainerSummary, e
|
|||
if len(config.Command) > 0 {
|
||||
command = config.Command[0]
|
||||
}
|
||||
// Get pod name if container is in a pod
|
||||
podName := ""
|
||||
if podID := ctr.PodID(); podID != "" {
|
||||
// Try to get pod name from config labels or leave empty
|
||||
if pn, ok := config.Labels["io.kubernetes.pod.name"]; ok {
|
||||
podName = pn
|
||||
}
|
||||
}
|
||||
result[i] = ContainerSummary{
|
||||
ID: ctr.ID(),
|
||||
Names: []string{ctr.Name()},
|
||||
|
|
@ -372,6 +411,8 @@ func convertLibpodContainerList(ctrs []*libpod.Container) ([]ContainerSummary, e
|
|||
Status: state.String(),
|
||||
Labels: config.Labels,
|
||||
Pod: ctr.PodID(),
|
||||
PodName: podName,
|
||||
IsInfra: ctr.IsInfra(),
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
|
|
|
|||
|
|
@ -128,6 +128,12 @@ func (r *LibpodRuntime) PruneNetworks(ctx context.Context) error {
|
|||
return ErrLibpodNotAvailable
|
||||
}
|
||||
|
||||
// Pod operations - all return ErrLibpodNotAvailable
|
||||
|
||||
func (r *LibpodRuntime) ListPods(ctx context.Context) ([]PodSummary, error) {
|
||||
return nil, ErrLibpodNotAvailable
|
||||
}
|
||||
|
||||
// Events returns an error channel on non-Linux platforms.
|
||||
func (r *LibpodRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
|
||||
errChan := make(chan error, 1)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ type MockRuntime struct {
|
|||
RemoveNetworkFunc func(ctx context.Context, name string) error
|
||||
PruneNetworksFunc func(ctx context.Context) error
|
||||
|
||||
// Pod operation mocks
|
||||
ListPodsFunc func(ctx context.Context) ([]PodSummary, error)
|
||||
|
||||
// Event mock
|
||||
EventsFunc func(ctx context.Context) (<-chan Event, <-chan error)
|
||||
|
||||
|
|
@ -251,6 +254,16 @@ func (m *MockRuntime) PruneNetworks(ctx context.Context) error {
|
|||
return ErrMockNotImplemented
|
||||
}
|
||||
|
||||
// Pod operations
|
||||
|
||||
func (m *MockRuntime) ListPods(ctx context.Context) ([]PodSummary, error) {
|
||||
m.recordCall("ListPods")
|
||||
if m.ListPodsFunc != nil {
|
||||
return m.ListPodsFunc(ctx)
|
||||
}
|
||||
return nil, ErrMockNotImplemented
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
func (m *MockRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/containers/podman/v5/pkg/bindings/containers"
|
||||
"github.com/containers/podman/v5/pkg/bindings/images"
|
||||
"github.com/containers/podman/v5/pkg/bindings/network"
|
||||
"github.com/containers/podman/v5/pkg/bindings/pods"
|
||||
"github.com/containers/podman/v5/pkg/bindings/system"
|
||||
"github.com/containers/podman/v5/pkg/bindings/volumes"
|
||||
"github.com/containers/podman/v5/pkg/domain/entities"
|
||||
|
|
@ -275,6 +276,27 @@ func (r *SocketRuntime) PruneNetworks(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// ListPods returns all pods using the pods bindings API.
|
||||
func (r *SocketRuntime) ListPods(ctx context.Context) ([]PodSummary, error) {
|
||||
podList, err := pods.List(r.conn, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]PodSummary, len(podList))
|
||||
for i, p := range podList {
|
||||
result[i] = PodSummary{
|
||||
ID: p.Id,
|
||||
Name: p.Name,
|
||||
Status: p.Status,
|
||||
Created: p.Created,
|
||||
InfraID: p.InfraId,
|
||||
Labels: p.Labels,
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Events streams container runtime events.
|
||||
func (r *SocketRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
|
||||
eventChan := make(chan Event)
|
||||
|
|
@ -346,6 +368,7 @@ func convertPodmanContainerList(podmanContainers []entities.ListContainer) []Con
|
|||
SizeRootFs: sizeRootFs,
|
||||
Pod: c.Pod,
|
||||
PodName: c.PodName,
|
||||
IsInfra: c.IsInfra,
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ type ContainerSummary struct {
|
|||
// Podman-specific fields
|
||||
Pod string
|
||||
PodName string
|
||||
IsInfra bool
|
||||
}
|
||||
|
||||
// PortMapping represents a container port mapping.
|
||||
|
|
@ -424,3 +425,13 @@ type EventActor struct {
|
|||
ID string
|
||||
Attributes map[string]string
|
||||
}
|
||||
|
||||
// PodSummary provides runtime-agnostic pod information.
|
||||
type PodSummary struct {
|
||||
ID string
|
||||
Name string
|
||||
Status string
|
||||
Created time.Time
|
||||
InfraID string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,3 +142,75 @@ func (gui *Gui) writeContainerLogs(ctr *commands.Container, ctx context.Context,
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Pod logs rendering
|
||||
|
||||
func (gui *Gui) renderPodLogsToMain(pod *commands.Pod) tasks.TaskFunc {
|
||||
return gui.NewTickerTask(TickerTaskOpts{
|
||||
Func: func(ctx context.Context, notifyStopped chan struct{}) {
|
||||
gui.renderPodLogsToMainAux(pod, ctx, notifyStopped)
|
||||
},
|
||||
Duration: time.Millisecond * 200,
|
||||
Before: func(ctx context.Context) { gui.clearMainView() },
|
||||
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
|
||||
Autoscroll: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) renderPodLogsToMainAux(pod *commands.Pod, ctx context.Context, notifyStopped chan struct{}) {
|
||||
gui.clearMainView()
|
||||
defer func() {
|
||||
notifyStopped <- struct{}{}
|
||||
}()
|
||||
|
||||
mainView := gui.Views.Main
|
||||
|
||||
if err := gui.writePodLogs(pod, ctx, mainView); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
|
||||
// Wait for context cancellation
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func (gui *Gui) writePodLogs(pod *commands.Pod, ctx context.Context, writer io.Writer) error {
|
||||
// Build podman pod logs command
|
||||
// Note: --color is used to distinguish output from different containers in the pod
|
||||
args := []string{"pod", "logs", "--follow"}
|
||||
|
||||
if gui.Config.UserConfig.Logs.Timestamps {
|
||||
args = append(args, "--timestamps")
|
||||
}
|
||||
if gui.Config.UserConfig.Logs.Since != "" {
|
||||
args = append(args, "--since", gui.Config.UserConfig.Logs.Since)
|
||||
}
|
||||
if gui.Config.UserConfig.Logs.Tail != "" {
|
||||
args = append(args, "--tail", gui.Config.UserConfig.Logs.Tail)
|
||||
}
|
||||
args = append(args, pod.Name)
|
||||
|
||||
cmd := pod.OSCommand.NewCmd("podman", args...)
|
||||
cmd.Stdout = writer
|
||||
cmd.Stderr = writer
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
gui.Log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for context cancellation or command completion
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if err := pod.OSCommand.Kill(cmd); err != nil {
|
||||
gui.Log.Warn(err)
|
||||
}
|
||||
return nil
|
||||
case err := <-done:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import (
|
|||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container] {
|
||||
func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.ContainerListItem] {
|
||||
// Standalone containers are containers which are either one-off containers, or whose service is not part of this docker-compose context.
|
||||
isStandaloneContainer := func(container *commands.Container) bool {
|
||||
if container.OneOff || container.ServiceName == "" {
|
||||
|
|
@ -30,58 +30,60 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
|
|||
})
|
||||
}
|
||||
|
||||
return &panels.SideListPanel[*commands.Container]{
|
||||
ContextState: &panels.ContextState[*commands.Container]{
|
||||
GetMainTabs: func() []panels.MainTab[*commands.Container] {
|
||||
return []panels.MainTab[*commands.Container]{
|
||||
return &panels.SideListPanel[*commands.ContainerListItem]{
|
||||
ContextState: &panels.ContextState[*commands.ContainerListItem]{
|
||||
GetMainTabs: func() []panels.MainTab[*commands.ContainerListItem] {
|
||||
return []panels.MainTab[*commands.ContainerListItem]{
|
||||
{
|
||||
Key: "logs",
|
||||
Title: gui.Tr.LogsTitle,
|
||||
Render: gui.renderContainerLogsToMain,
|
||||
Render: gui.renderContainerListItemLogs,
|
||||
},
|
||||
{
|
||||
Key: "stats",
|
||||
Title: gui.Tr.StatsTitle,
|
||||
Render: gui.renderContainerStats,
|
||||
Render: gui.renderContainerListItemStats,
|
||||
},
|
||||
{
|
||||
Key: "env",
|
||||
Title: gui.Tr.EnvTitle,
|
||||
Render: gui.renderContainerEnv,
|
||||
Render: gui.renderContainerListItemEnv,
|
||||
},
|
||||
{
|
||||
Key: "config",
|
||||
Title: gui.Tr.ConfigTitle,
|
||||
Render: gui.renderContainerConfig,
|
||||
Render: gui.renderContainerListItemConfig,
|
||||
},
|
||||
{
|
||||
Key: "top",
|
||||
Title: gui.Tr.TopTitle,
|
||||
Render: gui.renderContainerTop,
|
||||
Render: gui.renderContainerListItemTop,
|
||||
},
|
||||
}
|
||||
},
|
||||
GetItemContextCacheKey: func(container *commands.Container) string {
|
||||
// Including the container state in the cache key so that if the container
|
||||
// restarts we re-read the logs. In the past we've had some glitchiness
|
||||
// where a container restarts but the new logs don't get read.
|
||||
// Note that this might be jarring if we have a lot of logs and the container
|
||||
// restarts a lot, so let's keep an eye on it.
|
||||
return "containers-" + container.ID + "-" + container.Summary.State
|
||||
GetItemContextCacheKey: func(item *commands.ContainerListItem) string {
|
||||
// Including the state in the cache key so that if the container/pod
|
||||
// restarts we re-read the logs.
|
||||
return "containers-" + item.ID() + "-" + item.State()
|
||||
},
|
||||
},
|
||||
ListPanel: panels.ListPanel[*commands.Container]{
|
||||
List: panels.NewFilteredList[*commands.Container](),
|
||||
ListPanel: panels.ListPanel[*commands.ContainerListItem]{
|
||||
List: panels.NewFilteredList[*commands.ContainerListItem](),
|
||||
View: gui.Views.Containers,
|
||||
},
|
||||
NoItemsMessage: gui.Tr.NoContainers,
|
||||
Gui: gui.intoInterface(),
|
||||
// sortedContainers returns containers sorted by state if c.SortContainersByState is true (follows 1- running, 2- exited, 3- created)
|
||||
// and sorted by name if c.SortContainersByState is false
|
||||
Sort: func(a *commands.Container, b *commands.Container) bool {
|
||||
return sortContainers(a, b, gui.Config.UserConfig.Gui.LegacySortContainers)
|
||||
// Sort items: pods first (with their containers grouped), then standalone containers
|
||||
Sort: func(a *commands.ContainerListItem, b *commands.ContainerListItem) bool {
|
||||
return sortContainerListItems(a, b, gui.Config.UserConfig.Gui.LegacySortContainers)
|
||||
},
|
||||
Filter: func(container *commands.Container) bool {
|
||||
Filter: func(item *commands.ContainerListItem) bool {
|
||||
// Pods are always shown
|
||||
if item.IsPod {
|
||||
return true
|
||||
}
|
||||
|
||||
container := item.Container
|
||||
// Note that this is O(N*M) time complexity where N is the number of services
|
||||
// and M is the number of containers. We expect N to be small but M may be large,
|
||||
// so we will need to keep an eye on this.
|
||||
|
|
@ -95,8 +97,8 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
|
|||
|
||||
return true
|
||||
},
|
||||
GetTableCells: func(container *commands.Container) []string {
|
||||
return presentation.GetContainerDisplayStrings(&gui.Config.UserConfig.Gui, container)
|
||||
GetTableCells: func(item *commands.ContainerListItem) []string {
|
||||
return presentation.GetContainerListItemDisplayStrings(&gui.Config.UserConfig.Gui, item)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -121,6 +123,137 @@ func sortContainers(a *commands.Container, b *commands.Container, legacySort boo
|
|||
return containerStates[a.Summary.State] < containerStates[b.Summary.State]
|
||||
}
|
||||
|
||||
// sortContainerListItems sorts items to group pods with their containers.
|
||||
// Order: pods first (sorted by state/name), then their containers indented,
|
||||
// then standalone containers (sorted by state/name).
|
||||
func sortContainerListItems(a *commands.ContainerListItem, b *commands.ContainerListItem, legacySort bool) bool {
|
||||
// If both are in the same pod, sort by indent (pod first) then by name
|
||||
if a.PodID() != "" && a.PodID() == b.PodID() {
|
||||
// Pod comes before its containers
|
||||
if a.IsPod && !b.IsPod {
|
||||
return true
|
||||
}
|
||||
if !a.IsPod && b.IsPod {
|
||||
return false
|
||||
}
|
||||
// Both are containers in the same pod, sort by name
|
||||
return a.Name() < b.Name()
|
||||
}
|
||||
|
||||
// Get the effective sort key (pod name for items in pods, own name for standalone)
|
||||
aKey := a.Name()
|
||||
bKey := b.Name()
|
||||
if a.PodID() != "" && !a.IsPod {
|
||||
aKey = a.PodName() + "\x00" + a.Name() // Sort after the pod
|
||||
}
|
||||
if b.PodID() != "" && !b.IsPod {
|
||||
bKey = b.PodName() + "\x00" + b.Name()
|
||||
}
|
||||
if a.IsPod {
|
||||
aKey = a.Name() + "\x00" // Pod sorts before its containers
|
||||
}
|
||||
if b.IsPod {
|
||||
bKey = b.Name() + "\x00"
|
||||
}
|
||||
|
||||
// Pods and their containers sort together, standalone containers at the end
|
||||
aInPod := a.IsPod || a.PodID() != ""
|
||||
bInPod := b.IsPod || b.PodID() != ""
|
||||
|
||||
if aInPod && !bInPod {
|
||||
return true // Pods/pod containers before standalone
|
||||
}
|
||||
if !aInPod && bInPod {
|
||||
return false
|
||||
}
|
||||
|
||||
// Both in same category (pod-related or standalone)
|
||||
if legacySort {
|
||||
return aKey < bKey
|
||||
}
|
||||
|
||||
// Sort by state, then by key
|
||||
stateA := containerStates[a.State()]
|
||||
stateB := containerStates[b.State()]
|
||||
if stateA == stateB {
|
||||
return aKey < bKey
|
||||
}
|
||||
return stateA < stateB
|
||||
}
|
||||
|
||||
// Wrapper functions that delegate to container or pod rendering
|
||||
|
||||
func (gui *Gui) renderContainerListItemLogs(item *commands.ContainerListItem) tasks.TaskFunc {
|
||||
if item.IsPod {
|
||||
return gui.renderPodLogsToMain(item.Pod)
|
||||
}
|
||||
return gui.renderContainerLogsToMain(item.Container)
|
||||
}
|
||||
|
||||
func (gui *Gui) renderContainerListItemStats(item *commands.ContainerListItem) tasks.TaskFunc {
|
||||
if item.IsPod {
|
||||
return gui.NewSimpleRenderStringTask(func() string {
|
||||
return fmt.Sprintf("Pod: %s\nStatus: %s\nContainers: %d\n\nStats not available for pods. Select a container to view stats.",
|
||||
item.Pod.Name, item.Pod.State(), len(item.Pod.Containers))
|
||||
})
|
||||
}
|
||||
return gui.renderContainerStats(item.Container)
|
||||
}
|
||||
|
||||
func (gui *Gui) renderContainerListItemEnv(item *commands.ContainerListItem) tasks.TaskFunc {
|
||||
if item.IsPod {
|
||||
return gui.NewSimpleRenderStringTask(func() string {
|
||||
return "Environment variables not available for pods. Select a container to view environment."
|
||||
})
|
||||
}
|
||||
return gui.renderContainerEnv(item.Container)
|
||||
}
|
||||
|
||||
func (gui *Gui) renderContainerListItemConfig(item *commands.ContainerListItem) tasks.TaskFunc {
|
||||
if item.IsPod {
|
||||
return gui.renderPodConfig(item.Pod)
|
||||
}
|
||||
return gui.renderContainerConfig(item.Container)
|
||||
}
|
||||
|
||||
func (gui *Gui) renderContainerListItemTop(item *commands.ContainerListItem) tasks.TaskFunc {
|
||||
if item.IsPod {
|
||||
return gui.NewSimpleRenderStringTask(func() string {
|
||||
return "Process list not available for pods. Select a container to view processes."
|
||||
})
|
||||
}
|
||||
return gui.renderContainerTop(item.Container)
|
||||
}
|
||||
|
||||
func (gui *Gui) renderPodConfig(pod *commands.Pod) tasks.TaskFunc {
|
||||
return gui.NewSimpleRenderStringTask(func() string {
|
||||
padding := 10
|
||||
output := ""
|
||||
output += utils.ColoredString("Pod Information\n\n", color.FgCyan)
|
||||
output += utils.WithPadding("ID: ", padding) + pod.ID + "\n"
|
||||
output += utils.WithPadding("Name: ", padding) + pod.Name + "\n"
|
||||
output += utils.WithPadding("Status: ", padding) + pod.State() + "\n"
|
||||
output += utils.WithPadding("Created: ", padding) + pod.Summary.Created.Format(time.RFC3339) + "\n"
|
||||
output += fmt.Sprintf("%s%d\n", utils.WithPadding("Containers: ", padding), len(pod.Containers))
|
||||
|
||||
if len(pod.Containers) > 0 {
|
||||
output += "\n" + utils.ColoredString("Containers in this pod:\n", color.FgYellow)
|
||||
for _, c := range pod.Containers {
|
||||
stateColor := color.FgWhite
|
||||
switch c.Summary.State {
|
||||
case "running":
|
||||
stateColor = color.FgGreen
|
||||
case "exited":
|
||||
stateColor = color.FgRed
|
||||
}
|
||||
output += fmt.Sprintf(" - %s (%s)\n", c.Name, utils.ColoredString(c.Summary.State, stateColor))
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) renderContainerEnv(container *commands.Container) tasks.TaskFunc {
|
||||
return gui.NewSimpleRenderStringTask(func() string { return gui.containerEnv(container) })
|
||||
}
|
||||
|
|
@ -179,9 +312,9 @@ func (gui *Gui) containerConfigStr(container *commands.Container) string {
|
|||
output += "\n"
|
||||
for _, mount := range container.Details.Mounts {
|
||||
if mount.Type == "volume" {
|
||||
output += fmt.Sprintf("%s%s %s\n", strings.Repeat(" ", padding), utils.ColoredString(string(mount.Type)+":", color.FgYellow), mount.Name)
|
||||
output += fmt.Sprintf("%s%s %s\n", strings.Repeat(" ", padding), utils.ColoredString(mount.Type+":", color.FgYellow), mount.Name)
|
||||
} else {
|
||||
output += fmt.Sprintf("%s%s %s:%s\n", strings.Repeat(" ", padding), utils.ColoredString(string(mount.Type)+":", color.FgYellow), mount.Source, mount.Destination)
|
||||
output += fmt.Sprintf("%s%s %s:%s\n", strings.Repeat(" ", padding), utils.ColoredString(mount.Type+":", color.FgYellow), mount.Source, mount.Destination)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -302,11 +435,16 @@ func (gui *Gui) handleHideStoppedContainers(g *gocui.Gui, v *gocui.View) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
item, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if item.IsPod {
|
||||
return gui.createErrorPanel("Remove not yet supported for pods")
|
||||
}
|
||||
|
||||
ctr := item.Container
|
||||
handleMenuPress := func(force bool, removeVolumes bool) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||
if err := ctr.Remove(force, removeVolumes); err != nil {
|
||||
|
|
@ -357,20 +495,29 @@ func (gui *Gui) PauseContainer(container *commands.Container) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainerPause(g *gocui.Gui, v *gocui.View) error {
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
item, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.PauseContainer(ctr)
|
||||
if item.IsPod {
|
||||
return gui.createErrorPanel("Pause not yet supported for pods")
|
||||
}
|
||||
|
||||
return gui.PauseContainer(item.Container)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
item, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if item.IsPod {
|
||||
return gui.createErrorPanel("Stop not yet supported for pods")
|
||||
}
|
||||
|
||||
ctr := item.Container
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopContainer, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
||||
if err := ctr.Stop(); err != nil {
|
||||
|
|
@ -383,11 +530,16 @@ func (gui *Gui) handleContainerStop(g *gocui.Gui, v *gocui.View) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
item, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if item.IsPod {
|
||||
return gui.createErrorPanel("Restart not yet supported for pods")
|
||||
}
|
||||
|
||||
ctr := item.Container
|
||||
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
|
||||
if err := ctr.Restart(); err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
|
|
@ -398,11 +550,16 @@ func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
item, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if item.IsPod {
|
||||
return gui.createErrorPanel("Attach not yet supported for pods")
|
||||
}
|
||||
|
||||
ctr := item.Container
|
||||
c, err := ctr.Attach()
|
||||
if err != nil {
|
||||
return gui.createErrorPanel(err.Error())
|
||||
|
|
@ -424,23 +581,32 @@ func (gui *Gui) handlePruneContainers() error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainerViewLogs(g *gocui.Gui, v *gocui.View) error {
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
item, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
gui.renderLogsToStdout(ctr)
|
||||
if item.IsPod {
|
||||
// TODO: implement pod logs to stdout
|
||||
return gui.createErrorPanel("View logs (stdout) not yet supported for pods")
|
||||
}
|
||||
|
||||
gui.renderLogsToStdout(item.Container)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleContainersExecShell(g *gocui.Gui, v *gocui.View) error {
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
item, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.containerExecShell(ctr)
|
||||
if item.IsPod {
|
||||
return gui.createErrorPanel("Exec shell not yet supported for pods. Select a container instead.")
|
||||
}
|
||||
|
||||
return gui.containerExecShell(item.Container)
|
||||
}
|
||||
|
||||
func (gui *Gui) containerExecShell(container *commands.Container) error {
|
||||
|
|
@ -456,13 +622,17 @@ func (gui *Gui) containerExecShell(container *commands.Container) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error {
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
item, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if item.IsPod {
|
||||
return gui.createErrorPanel("Custom commands not yet supported for pods. Select a container instead.")
|
||||
}
|
||||
|
||||
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{
|
||||
Container: ctr,
|
||||
Container: item.Container,
|
||||
})
|
||||
|
||||
customCommands := gui.Config.UserConfig.CustomCommands.Containers
|
||||
|
|
@ -473,9 +643,11 @@ func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error
|
|||
func (gui *Gui) handleStopContainers() error {
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmStopContainers, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
||||
for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if err := ctr.Stop(); err != nil {
|
||||
gui.Log.Error(err)
|
||||
for _, item := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if !item.IsPod && item.Container != nil {
|
||||
if err := item.Container.Stop(); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -487,9 +659,11 @@ func (gui *Gui) handleStopContainers() error {
|
|||
func (gui *Gui) handleRemoveContainers() error {
|
||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmRemoveContainers, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||
for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if err := ctr.Remove(true, false); err != nil {
|
||||
gui.Log.Error(err)
|
||||
for _, item := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if !item.IsPod && item.Container != nil {
|
||||
if err := item.Container.Remove(true, false); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -522,12 +696,16 @@ func (gui *Gui) handleContainersBulkCommand(g *gocui.Gui, v *gocui.View) error {
|
|||
|
||||
// Open first port in browser
|
||||
func (gui *Gui) handleContainersOpenInBrowserCommand(g *gocui.Gui, v *gocui.View) error {
|
||||
ctr, err := gui.Panels.Containers.GetSelectedItem()
|
||||
item, err := gui.Panels.Containers.GetSelectedItem()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.openContainerInBrowser(ctr)
|
||||
if item.IsPod {
|
||||
return gui.createErrorPanel("Cannot open pod in browser. Select a container instead.")
|
||||
}
|
||||
|
||||
return gui.openContainerInBrowser(item.Container)
|
||||
}
|
||||
|
||||
func (gui *Gui) openContainerInBrowser(ctr *commands.Container) error {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ type Gui struct {
|
|||
type Panels struct {
|
||||
Projects *panels.SideListPanel[*commands.Project]
|
||||
Services *panels.SideListPanel[*commands.Service]
|
||||
Containers *panels.SideListPanel[*commands.Container]
|
||||
Containers *panels.SideListPanel[*commands.ContainerListItem]
|
||||
Images *panels.SideListPanel[*commands.Image]
|
||||
Volumes *panels.SideListPanel[*commands.Volume]
|
||||
Networks *panels.SideListPanel[*commands.Network]
|
||||
|
|
@ -290,7 +290,14 @@ func (gui *Gui) setPanels() {
|
|||
}
|
||||
|
||||
func (gui *Gui) updateContainerDetails() error {
|
||||
return gui.PodmanCommand.RefreshContainerDetails(gui.Panels.Containers.List.GetAllItems())
|
||||
// Extract containers from ContainerListItems
|
||||
var containers []*commands.Container
|
||||
for _, item := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if !item.IsPod && item.Container != nil {
|
||||
containers = append(containers, item.Container)
|
||||
}
|
||||
}
|
||||
return gui.PodmanCommand.RefreshContainerDetails(containers)
|
||||
}
|
||||
|
||||
func (gui *Gui) refresh() {
|
||||
|
|
@ -469,9 +476,9 @@ func (gui *Gui) monitorContainerStats(ctx context.Context) {
|
|||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
for _, container := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if !container.MonitoringStats {
|
||||
go gui.PodmanCommand.CreateClientStatMonitor(container)
|
||||
for _, item := range gui.Panels.Containers.List.GetAllItems() {
|
||||
if !item.IsPod && item.Container != nil && !item.Container.MonitoringStats {
|
||||
go gui.PodmanCommand.CreateClientStatMonitor(item.Container)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/asciigraph"
|
||||
"github.com/christophe-duc/lazypodman/pkg/commands"
|
||||
"github.com/christophe-duc/lazypodman/pkg/config"
|
||||
"github.com/christophe-duc/lazypodman/pkg/utils"
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/asciigraph"
|
||||
"github.com/mcuadros/go-lookup"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,96 @@ func GetContainerDisplayStrings(guiConfig *config.GuiConfig, container *commands
|
|||
}
|
||||
}
|
||||
|
||||
// GetContainerListItemDisplayStrings returns display strings for a ContainerListItem (pod or container)
|
||||
func GetContainerListItemDisplayStrings(guiConfig *config.GuiConfig, item *commands.ContainerListItem) []string {
|
||||
if item.IsPod && item.Pod != nil {
|
||||
return GetPodDisplayStrings(guiConfig, item.Pod)
|
||||
}
|
||||
|
||||
if item.Container == nil {
|
||||
// Return an empty row with the expected number of columns when container data is missing.
|
||||
return []string{"", "", "", "", "", ""}
|
||||
}
|
||||
|
||||
// Add indentation for containers in pods
|
||||
strings := GetContainerDisplayStrings(guiConfig, item.Container)
|
||||
if item.Indent > 0 {
|
||||
strings[2] = fmt.Sprintf("%s%s", createIndent(item.Indent), strings[2])
|
||||
}
|
||||
return strings
|
||||
}
|
||||
|
||||
// GetPodDisplayStrings returns display strings for a pod
|
||||
func GetPodDisplayStrings(guiConfig *config.GuiConfig, pod *commands.Pod) []string {
|
||||
return []string{
|
||||
getPodDisplayStatus(guiConfig, pod),
|
||||
"", // No substatus for pods
|
||||
utils.ColoredString(pod.Name, color.FgCyan),
|
||||
"", // No CPU% for pods
|
||||
"", // No ports for pods
|
||||
utils.ColoredString(fmt.Sprintf("(%d containers)", len(pod.Containers)), color.FgMagenta),
|
||||
}
|
||||
}
|
||||
|
||||
func getPodDisplayStatus(guiConfig *config.GuiConfig, pod *commands.Pod) string {
|
||||
shortStatusMap := map[string]string{
|
||||
"Running": "R",
|
||||
"Degraded": "D",
|
||||
"Exited": "X",
|
||||
"Dead": "!",
|
||||
"Created": "C",
|
||||
}
|
||||
|
||||
iconStatusMap := map[string]rune{
|
||||
"Running": '▶',
|
||||
"Degraded": '◐',
|
||||
"Exited": '⨯',
|
||||
"Dead": '!',
|
||||
"Created": '+',
|
||||
}
|
||||
|
||||
var podState string
|
||||
switch guiConfig.ContainerStatusHealthStyle {
|
||||
case "short":
|
||||
if s, ok := shortStatusMap[pod.State()]; ok {
|
||||
podState = s
|
||||
} else {
|
||||
podState = "?"
|
||||
}
|
||||
case "icon":
|
||||
if r, ok := iconStatusMap[pod.State()]; ok {
|
||||
podState = string(r)
|
||||
} else {
|
||||
podState = "?"
|
||||
}
|
||||
default:
|
||||
podState = pod.State()
|
||||
}
|
||||
|
||||
return utils.ColoredString(podState, getPodColor(pod))
|
||||
}
|
||||
|
||||
func getPodColor(pod *commands.Pod) color.Attribute {
|
||||
switch pod.State() {
|
||||
case "Running":
|
||||
return color.FgGreen
|
||||
case "Degraded":
|
||||
return color.FgYellow
|
||||
case "Exited":
|
||||
return color.FgRed
|
||||
case "Dead":
|
||||
return color.FgRed
|
||||
case "Created":
|
||||
return color.FgCyan
|
||||
default:
|
||||
return color.FgWhite
|
||||
}
|
||||
}
|
||||
|
||||
func createIndent(spaces int) string {
|
||||
return fmt.Sprintf("%*s", spaces, "")
|
||||
}
|
||||
|
||||
func displayContainerImage(container *commands.Container) string {
|
||||
return strings.TrimPrefix(container.Summary.Image, "sha256:")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,18 +109,17 @@ type TranslationSet struct {
|
|||
FilterList string
|
||||
OpenInBrowser string
|
||||
SortContainersByState string
|
||||
|
||||
LogsTitle string
|
||||
ConfigTitle string
|
||||
EnvTitle string
|
||||
ComposeConfigTitle string
|
||||
StatsTitle string
|
||||
CreditsTitle string
|
||||
ContainerConfigTitle string
|
||||
ContainerEnvTitle string
|
||||
NothingToDisplay string
|
||||
NoContainerForService string
|
||||
CannotDisplayEnvVariables string
|
||||
LogsTitle string
|
||||
ConfigTitle string
|
||||
EnvTitle string
|
||||
ComposeConfigTitle string
|
||||
StatsTitle string
|
||||
CreditsTitle string
|
||||
ContainerConfigTitle string
|
||||
ContainerEnvTitle string
|
||||
NothingToDisplay string
|
||||
NoContainerForService string
|
||||
CannotDisplayEnvVariables string
|
||||
|
||||
No string
|
||||
Yes string
|
||||
|
|
|
|||
342
vendor/github.com/containers/podman/v5/pkg/bindings/pods/pods.go
generated
vendored
Normal file
342
vendor/github.com/containers/podman/v5/pkg/bindings/pods/pods.go
generated
vendored
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
package pods
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/api/handlers"
|
||||
"github.com/containers/podman/v5/pkg/bindings"
|
||||
entitiesTypes "github.com/containers/podman/v5/pkg/domain/entities/types"
|
||||
"github.com/containers/podman/v5/pkg/errorhandling"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
func CreatePodFromSpec(ctx context.Context, spec *entitiesTypes.PodSpec) (*entitiesTypes.PodCreateReport, error) {
|
||||
var pcr entitiesTypes.PodCreateReport
|
||||
if spec == nil {
|
||||
spec = new(entitiesTypes.PodSpec)
|
||||
}
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
specString, err := jsoniter.MarshalToString(spec.PodSpecGen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stringReader := strings.NewReader(specString)
|
||||
response, err := conn.DoRequest(ctx, stringReader, http.MethodPost, "/pods/create", nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return &pcr, response.Process(&pcr)
|
||||
}
|
||||
|
||||
// Exists is a lightweight method to determine if a pod exists in local storage
|
||||
func Exists(ctx context.Context, nameOrID string, _ *ExistsOptions) (bool, error) {
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/pods/%s/exists", nil, nil, nameOrID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return response.IsSuccess(), nil
|
||||
}
|
||||
|
||||
// Inspect returns low-level information about the given pod.
|
||||
func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*entitiesTypes.PodInspectReport, error) {
|
||||
var report entitiesTypes.PodInspectReport
|
||||
if options == nil {
|
||||
options = new(InspectOptions)
|
||||
}
|
||||
_ = options
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/pods/%s/json", nil, nil, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return &report, response.Process(&report)
|
||||
}
|
||||
|
||||
// Kill sends a SIGTERM to all the containers in a pod. The optional signal parameter
|
||||
// can be used to override SIGTERM.
|
||||
func Kill(ctx context.Context, nameOrID string, options *KillOptions) (*entitiesTypes.PodKillReport, error) {
|
||||
var report entitiesTypes.PodKillReport
|
||||
if options == nil {
|
||||
options = new(KillOptions)
|
||||
}
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params, err := options.ToParams()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/kill", params, nil, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Pause pauses all running containers in a given pod.
|
||||
func Pause(ctx context.Context, nameOrID string, options *PauseOptions) (*entitiesTypes.PodPauseReport, error) {
|
||||
var report entitiesTypes.PodPauseReport
|
||||
if options == nil {
|
||||
options = new(PauseOptions)
|
||||
}
|
||||
_ = options
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/pause", nil, nil, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Prune by default removes all non-running pods in local storage.
|
||||
// And with force set true removes all pods.
|
||||
func Prune(ctx context.Context, options *PruneOptions) ([]*entitiesTypes.PodPruneReport, error) {
|
||||
var reports []*entitiesTypes.PodPruneReport
|
||||
if options == nil {
|
||||
options = new(PruneOptions)
|
||||
}
|
||||
_ = options
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/prune", nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return reports, response.Process(&reports)
|
||||
}
|
||||
|
||||
// List returns all pods in local storage. The optional filters parameter can
|
||||
// be used to refine which pods should be listed.
|
||||
func List(ctx context.Context, options *ListOptions) ([]*entitiesTypes.ListPodsReport, error) {
|
||||
var podsReports []*entitiesTypes.ListPodsReport
|
||||
if options == nil {
|
||||
options = new(ListOptions)
|
||||
}
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params, err := options.ToParams()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/pods/json", params, nil)
|
||||
if err != nil {
|
||||
return podsReports, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return podsReports, response.Process(&podsReports)
|
||||
}
|
||||
|
||||
// Restart restarts all containers in a pod.
|
||||
func Restart(ctx context.Context, nameOrID string, options *RestartOptions) (*entitiesTypes.PodRestartReport, error) {
|
||||
var report entitiesTypes.PodRestartReport
|
||||
if options == nil {
|
||||
options = new(RestartOptions)
|
||||
}
|
||||
_ = options
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/restart", nil, nil, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Remove deletes a Pod from local storage. The optional force parameter denotes
|
||||
// that the Pod can be removed even if in a running state.
|
||||
func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) (*entitiesTypes.PodRmReport, error) {
|
||||
var report entitiesTypes.PodRmReport
|
||||
if options == nil {
|
||||
options = new(RemoveOptions)
|
||||
}
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params, err := options.ToParams()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodDelete, "/pods/%s", params, nil, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return &report, response.Process(&report)
|
||||
}
|
||||
|
||||
// Start starts all containers in a pod.
|
||||
func Start(ctx context.Context, nameOrID string, options *StartOptions) (*entitiesTypes.PodStartReport, error) {
|
||||
var report entitiesTypes.PodStartReport
|
||||
if options == nil {
|
||||
options = new(StartOptions)
|
||||
}
|
||||
_ = options
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/start", nil, nil, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode == http.StatusNotModified {
|
||||
report.Id = nameOrID
|
||||
report.RawInput = nameOrID
|
||||
return &report, nil
|
||||
}
|
||||
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Stop stops all containers in a Pod. The optional timeout parameter can be
|
||||
// used to override the timeout before the container is killed.
|
||||
func Stop(ctx context.Context, nameOrID string, options *StopOptions) (*entitiesTypes.PodStopReport, error) {
|
||||
var report entitiesTypes.PodStopReport
|
||||
if options == nil {
|
||||
options = new(StopOptions)
|
||||
}
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params, err := options.ToParams()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/stop", params, nil, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode == http.StatusNotModified {
|
||||
report.Id = nameOrID
|
||||
return &report, nil
|
||||
}
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Top gathers statistics about the running processes in a pod. The nameOrID can be a pod name
|
||||
// or a partial/full ID. The descriptors allow for specifying which data to collect from each process.
|
||||
func Top(ctx context.Context, nameOrID string, options *TopOptions) ([]string, error) {
|
||||
if options == nil {
|
||||
options = new(TopOptions)
|
||||
}
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params := url.Values{}
|
||||
if descriptors := options.GetDescriptors(); len(descriptors) > 0 {
|
||||
params.Set("ps_args", strings.Join(descriptors, ","))
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/pods/%s/top", params, nil, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
body := handlers.PodTopOKBody{}
|
||||
if err = response.Process(&body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// handlers.PodTopOKBody{} returns a slice of slices where each cell in the top table is an item.
|
||||
// In libpod land, we're just using a slice with cells being split by tabs, which allows for an idiomatic
|
||||
// usage of the tabwriter.
|
||||
topOutput := []string{strings.Join(body.Titles, "\t")}
|
||||
for _, out := range body.Processes {
|
||||
topOutput = append(topOutput, strings.Join(out, "\t"))
|
||||
}
|
||||
|
||||
return topOutput, err
|
||||
}
|
||||
|
||||
// Unpause unpauses all paused containers in a Pod.
|
||||
func Unpause(ctx context.Context, nameOrID string, options *UnpauseOptions) (*entitiesTypes.PodUnpauseReport, error) {
|
||||
if options == nil {
|
||||
options = new(UnpauseOptions)
|
||||
}
|
||||
_ = options
|
||||
var report entitiesTypes.PodUnpauseReport
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodPost, "/pods/%s/unpause", nil, nil, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
|
||||
}
|
||||
|
||||
// Stats display resource-usage statistics of one or more pods.
|
||||
func Stats(ctx context.Context, namesOrIDs []string, options *StatsOptions) ([]*entitiesTypes.PodStatsReport, error) {
|
||||
if options == nil {
|
||||
options = new(StatsOptions)
|
||||
}
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params, err := options.ToParams()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, i := range namesOrIDs {
|
||||
params.Add("namesOrIDs", i)
|
||||
}
|
||||
|
||||
var reports []*entitiesTypes.PodStatsReport
|
||||
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/pods/stats", params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
return reports, response.Process(&reports)
|
||||
}
|
||||
92
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types.go
generated
vendored
Normal file
92
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package pods
|
||||
|
||||
// CreateOptions are optional options for creating pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go CreateOptions
|
||||
type CreateOptions struct {
|
||||
}
|
||||
|
||||
// InspectOptions are optional options for inspecting pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go InspectOptions
|
||||
type InspectOptions struct {
|
||||
}
|
||||
|
||||
// KillOptions are optional options for killing pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go KillOptions
|
||||
type KillOptions struct {
|
||||
Signal *string
|
||||
}
|
||||
|
||||
// PauseOptions are optional options for pausing pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go PauseOptions
|
||||
type PauseOptions struct {
|
||||
}
|
||||
|
||||
// PruneOptions are optional options for pruning pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go PruneOptions
|
||||
type PruneOptions struct {
|
||||
}
|
||||
|
||||
// ListOptions are optional options for listing pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go ListOptions
|
||||
type ListOptions struct {
|
||||
Filters map[string][]string
|
||||
}
|
||||
|
||||
// RestartOptions are optional options for restarting pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go RestartOptions
|
||||
type RestartOptions struct {
|
||||
}
|
||||
|
||||
// StartOptions are optional options for starting pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go StartOptions
|
||||
type StartOptions struct {
|
||||
}
|
||||
|
||||
// StopOptions are optional options for stopping pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go StopOptions
|
||||
type StopOptions struct {
|
||||
Timeout *int
|
||||
}
|
||||
|
||||
// TopOptions are optional options for getting top on pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go TopOptions
|
||||
type TopOptions struct {
|
||||
Descriptors []string
|
||||
}
|
||||
|
||||
// UnpauseOptions are optional options for unpausinging pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go UnpauseOptions
|
||||
type UnpauseOptions struct {
|
||||
}
|
||||
|
||||
// StatsOptions are optional options for getting stats of pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go StatsOptions
|
||||
type StatsOptions struct {
|
||||
All *bool
|
||||
}
|
||||
|
||||
// RemoveOptions are optional options for removing pods
|
||||
//
|
||||
//go:generate go run ../generator/generator.go RemoveOptions
|
||||
type RemoveOptions struct {
|
||||
Force *bool
|
||||
Timeout *uint
|
||||
}
|
||||
|
||||
// ExistsOptions are optional options for checking if a pod exists
|
||||
//
|
||||
//go:generate go run ../generator/generator.go ExistsOptions
|
||||
type ExistsOptions struct {
|
||||
}
|
||||
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_create_options.go
generated
vendored
Normal file
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_create_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *CreateOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *CreateOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_exists_options.go
generated
vendored
Normal file
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_exists_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *ExistsOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *ExistsOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_inspect_options.go
generated
vendored
Normal file
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_inspect_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *InspectOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *InspectOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
33
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_kill_options.go
generated
vendored
Normal file
33
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_kill_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *KillOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *KillOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
|
||||
// WithSignal set field Signal to given value
|
||||
func (o *KillOptions) WithSignal(value string) *KillOptions {
|
||||
o.Signal = &value
|
||||
return o
|
||||
}
|
||||
|
||||
// GetSignal returns value of field Signal
|
||||
func (o *KillOptions) GetSignal() string {
|
||||
if o.Signal == nil {
|
||||
var z string
|
||||
return z
|
||||
}
|
||||
return *o.Signal
|
||||
}
|
||||
33
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_list_options.go
generated
vendored
Normal file
33
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_list_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *ListOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *ListOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
|
||||
// WithFilters set field Filters to given value
|
||||
func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions {
|
||||
o.Filters = value
|
||||
return o
|
||||
}
|
||||
|
||||
// GetFilters returns value of field Filters
|
||||
func (o *ListOptions) GetFilters() map[string][]string {
|
||||
if o.Filters == nil {
|
||||
var z map[string][]string
|
||||
return z
|
||||
}
|
||||
return o.Filters
|
||||
}
|
||||
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_pause_options.go
generated
vendored
Normal file
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_pause_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *PauseOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *PauseOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_prune_options.go
generated
vendored
Normal file
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_prune_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *PruneOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *PruneOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
48
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_remove_options.go
generated
vendored
Normal file
48
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_remove_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *RemoveOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *RemoveOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
|
||||
// WithForce set field Force to given value
|
||||
func (o *RemoveOptions) WithForce(value bool) *RemoveOptions {
|
||||
o.Force = &value
|
||||
return o
|
||||
}
|
||||
|
||||
// GetForce returns value of field Force
|
||||
func (o *RemoveOptions) GetForce() bool {
|
||||
if o.Force == nil {
|
||||
var z bool
|
||||
return z
|
||||
}
|
||||
return *o.Force
|
||||
}
|
||||
|
||||
// WithTimeout set field Timeout to given value
|
||||
func (o *RemoveOptions) WithTimeout(value uint) *RemoveOptions {
|
||||
o.Timeout = &value
|
||||
return o
|
||||
}
|
||||
|
||||
// GetTimeout returns value of field Timeout
|
||||
func (o *RemoveOptions) GetTimeout() uint {
|
||||
if o.Timeout == nil {
|
||||
var z uint
|
||||
return z
|
||||
}
|
||||
return *o.Timeout
|
||||
}
|
||||
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_restart_options.go
generated
vendored
Normal file
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_restart_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *RestartOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *RestartOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_start_options.go
generated
vendored
Normal file
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_start_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *StartOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *StartOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
33
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_stats_options.go
generated
vendored
Normal file
33
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_stats_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *StatsOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *StatsOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
|
||||
// WithAll set field All to given value
|
||||
func (o *StatsOptions) WithAll(value bool) *StatsOptions {
|
||||
o.All = &value
|
||||
return o
|
||||
}
|
||||
|
||||
// GetAll returns value of field All
|
||||
func (o *StatsOptions) GetAll() bool {
|
||||
if o.All == nil {
|
||||
var z bool
|
||||
return z
|
||||
}
|
||||
return *o.All
|
||||
}
|
||||
33
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_stop_options.go
generated
vendored
Normal file
33
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_stop_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *StopOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *StopOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
|
||||
// WithTimeout set field Timeout to given value
|
||||
func (o *StopOptions) WithTimeout(value int) *StopOptions {
|
||||
o.Timeout = &value
|
||||
return o
|
||||
}
|
||||
|
||||
// GetTimeout returns value of field Timeout
|
||||
func (o *StopOptions) GetTimeout() int {
|
||||
if o.Timeout == nil {
|
||||
var z int
|
||||
return z
|
||||
}
|
||||
return *o.Timeout
|
||||
}
|
||||
33
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_top_options.go
generated
vendored
Normal file
33
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_top_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *TopOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *TopOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
|
||||
// WithDescriptors set field Descriptors to given value
|
||||
func (o *TopOptions) WithDescriptors(value []string) *TopOptions {
|
||||
o.Descriptors = value
|
||||
return o
|
||||
}
|
||||
|
||||
// GetDescriptors returns value of field Descriptors
|
||||
func (o *TopOptions) GetDescriptors() []string {
|
||||
if o.Descriptors == nil {
|
||||
var z []string
|
||||
return z
|
||||
}
|
||||
return o.Descriptors
|
||||
}
|
||||
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_unpause_options.go
generated
vendored
Normal file
18
vendor/github.com/containers/podman/v5/pkg/bindings/pods/types_unpause_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Code generated by go generate; DO NOT EDIT.
|
||||
package pods
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/podman/v5/pkg/bindings/internal/util"
|
||||
)
|
||||
|
||||
// Changed returns true if named field has been set
|
||||
func (o *UnpauseOptions) Changed(fieldName string) bool {
|
||||
return util.Changed(o, fieldName)
|
||||
}
|
||||
|
||||
// ToParams formats struct fields to be passed to API service
|
||||
func (o *UnpauseOptions) ToParams() (url.Values, error) {
|
||||
return util.ToParams(o)
|
||||
}
|
||||
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
|
|
@ -165,6 +165,7 @@ github.com/containers/podman/v5/pkg/bindings/containers
|
|||
github.com/containers/podman/v5/pkg/bindings/images
|
||||
github.com/containers/podman/v5/pkg/bindings/internal/util
|
||||
github.com/containers/podman/v5/pkg/bindings/network
|
||||
github.com/containers/podman/v5/pkg/bindings/pods
|
||||
github.com/containers/podman/v5/pkg/bindings/system
|
||||
github.com/containers/podman/v5/pkg/bindings/volumes
|
||||
github.com/containers/podman/v5/pkg/checkpoint/crutils
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue