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_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.go` - Direct libpod implementation (Linux+CGO only)
|
||||||
- `pkg/commands/runtime_libpod_stub.go` - Stub for non-Linux platforms
|
- `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:**
|
**Podman integration:**
|
||||||
- `pkg/commands/podman.go` - Main client connection, auto-detection, and initialization
|
- `pkg/commands/podman.go` - Main client connection, auto-detection, and initialization
|
||||||
- `pkg/commands/container.go` - Container wrapper operations
|
- `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/image.go` - Image wrapper operations
|
||||||
- `pkg/commands/volume.go` - Volume wrapper operations
|
- `pkg/commands/volume.go` - Volume wrapper operations
|
||||||
- `pkg/commands/network.go` - Network wrapper operations
|
- `pkg/commands/network.go` - Network wrapper operations
|
||||||
|
|
@ -102,6 +104,9 @@ ListContainers() / InspectContainer() / ContainerStats()
|
||||||
StartContainer() / StopContainer() / PauseContainer() / UnpauseContainer()
|
StartContainer() / StopContainer() / PauseContainer() / UnpauseContainer()
|
||||||
RestartContainer() / RemoveContainer() / PruneContainers() / ContainerTop()
|
RestartContainer() / RemoveContainer() / PruneContainers() / ContainerTop()
|
||||||
|
|
||||||
|
// Pod operations
|
||||||
|
ListPods() - Returns all pods with their metadata
|
||||||
|
|
||||||
// Image operations
|
// Image operations
|
||||||
ListImages() / InspectImage() / ImageHistory() / RemoveImage() / PruneImages()
|
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/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1
|
||||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||||
github.com/containers/podman/v5 v5.7.1
|
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/fatih/color v1.15.0
|
||||||
github.com/go-errors/errors v1.5.1
|
github.com/go-errors/errors v1.5.1
|
||||||
github.com/gookit/color v1.5.0
|
github.com/gookit/color v1.5.0
|
||||||
|
|
@ -27,6 +26,7 @@ require (
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
|
go.podman.io/common v0.66.1
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
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/disiqueira/gotree/v3 v3.0.2 // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/docker/distribution v2.8.3+incompatible // 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/docker-credential-helpers v0.9.4 // indirect
|
||||||
github.com/docker/go-connections v0.6.0 // indirect
|
github.com/docker/go-connections v0.6.0 // indirect
|
||||||
github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8 // 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 v1.36.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace 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/image/v5 v5.38.0 // indirect
|
||||||
go.podman.io/storage v1.61.0 // indirect
|
go.podman.io/storage v1.61.0 // indirect
|
||||||
go.yaml.in/yaml/v2 v2.4.2 // 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"
|
ogLog "log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -347,15 +348,35 @@ func calculateMemoryPercentageFromEntry(stats ContainerStatsEntry) float64 {
|
||||||
return 0.0
|
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()
|
c.ServiceMutex.Lock()
|
||||||
defer c.ServiceMutex.Unlock()
|
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)
|
containers, err := c.GetContainers(currentContainers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
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
|
var services []*Service
|
||||||
// we only need to get these services once because they won't change in the runtime of the program
|
// we only need to get these services once because they won't change in the runtime of the program
|
||||||
if currentServices != nil {
|
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) {
|
func (c *PodmanCommand) assignContainersToServices(containers []*Container, services []*Service) {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ type ContainerRuntime interface {
|
||||||
RemoveNetwork(ctx context.Context, name string) error
|
RemoveNetwork(ctx context.Context, name string) error
|
||||||
PruneNetworks(ctx context.Context) error
|
PruneNetworks(ctx context.Context) error
|
||||||
|
|
||||||
|
// Pod operations
|
||||||
|
ListPods(ctx context.Context) ([]PodSummary, error)
|
||||||
|
|
||||||
// Events streams container/image/volume/network events
|
// Events streams container/image/volume/network events
|
||||||
Events(ctx context.Context) (<-chan Event, <-chan error)
|
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
|
// Parse the result: first line is headers, rest are process rows
|
||||||
// Each line is space-separated fields
|
// Each line is space-separated fields
|
||||||
headers := splitFields(result[0])
|
headers := splitFields(result[0])
|
||||||
var processes [][]string
|
processes := make([][]string, 0, len(result)-1)
|
||||||
for _, line := range result[1:] {
|
for _, line := range result[1:] {
|
||||||
processes = append(processes, splitFields(line))
|
processes = append(processes, splitFields(line))
|
||||||
}
|
}
|
||||||
|
|
@ -309,6 +309,37 @@ func (r *LibpodRuntime) PruneNetworks(ctx context.Context) error {
|
||||||
return nil
|
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.
|
// Events streams container runtime events.
|
||||||
// For libpod, we use a polling approach since direct event streaming requires more setup.
|
// 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) {
|
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 {
|
if len(config.Command) > 0 {
|
||||||
command = 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{
|
result[i] = ContainerSummary{
|
||||||
ID: ctr.ID(),
|
ID: ctr.ID(),
|
||||||
Names: []string{ctr.Name()},
|
Names: []string{ctr.Name()},
|
||||||
|
|
@ -372,6 +411,8 @@ func convertLibpodContainerList(ctrs []*libpod.Container) ([]ContainerSummary, e
|
||||||
Status: state.String(),
|
Status: state.String(),
|
||||||
Labels: config.Labels,
|
Labels: config.Labels,
|
||||||
Pod: ctr.PodID(),
|
Pod: ctr.PodID(),
|
||||||
|
PodName: podName,
|
||||||
|
IsInfra: ctr.IsInfra(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,12 @@ func (r *LibpodRuntime) PruneNetworks(ctx context.Context) error {
|
||||||
return ErrLibpodNotAvailable
|
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.
|
// Events returns an error channel on non-Linux platforms.
|
||||||
func (r *LibpodRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
|
func (r *LibpodRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,9 @@ type MockRuntime struct {
|
||||||
RemoveNetworkFunc func(ctx context.Context, name string) error
|
RemoveNetworkFunc func(ctx context.Context, name string) error
|
||||||
PruneNetworksFunc func(ctx context.Context) error
|
PruneNetworksFunc func(ctx context.Context) error
|
||||||
|
|
||||||
|
// Pod operation mocks
|
||||||
|
ListPodsFunc func(ctx context.Context) ([]PodSummary, error)
|
||||||
|
|
||||||
// Event mock
|
// Event mock
|
||||||
EventsFunc func(ctx context.Context) (<-chan Event, <-chan error)
|
EventsFunc func(ctx context.Context) (<-chan Event, <-chan error)
|
||||||
|
|
||||||
|
|
@ -251,6 +254,16 @@ func (m *MockRuntime) PruneNetworks(ctx context.Context) error {
|
||||||
return ErrMockNotImplemented
|
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
|
// Events
|
||||||
|
|
||||||
func (m *MockRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
|
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/containers"
|
||||||
"github.com/containers/podman/v5/pkg/bindings/images"
|
"github.com/containers/podman/v5/pkg/bindings/images"
|
||||||
"github.com/containers/podman/v5/pkg/bindings/network"
|
"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/system"
|
||||||
"github.com/containers/podman/v5/pkg/bindings/volumes"
|
"github.com/containers/podman/v5/pkg/bindings/volumes"
|
||||||
"github.com/containers/podman/v5/pkg/domain/entities"
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
||||||
|
|
@ -275,6 +276,27 @@ func (r *SocketRuntime) PruneNetworks(ctx context.Context) error {
|
||||||
return err
|
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.
|
// Events streams container runtime events.
|
||||||
func (r *SocketRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
|
func (r *SocketRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
|
||||||
eventChan := make(chan Event)
|
eventChan := make(chan Event)
|
||||||
|
|
@ -346,6 +368,7 @@ func convertPodmanContainerList(podmanContainers []entities.ListContainer) []Con
|
||||||
SizeRootFs: sizeRootFs,
|
SizeRootFs: sizeRootFs,
|
||||||
Pod: c.Pod,
|
Pod: c.Pod,
|
||||||
PodName: c.PodName,
|
PodName: c.PodName,
|
||||||
|
IsInfra: c.IsInfra,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ type ContainerSummary struct {
|
||||||
// Podman-specific fields
|
// Podman-specific fields
|
||||||
Pod string
|
Pod string
|
||||||
PodName string
|
PodName string
|
||||||
|
IsInfra bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// PortMapping represents a container port mapping.
|
// PortMapping represents a container port mapping.
|
||||||
|
|
@ -424,3 +425,13 @@ type EventActor struct {
|
||||||
ID string
|
ID string
|
||||||
Attributes map[string]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
|
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"
|
"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.
|
// 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 {
|
isStandaloneContainer := func(container *commands.Container) bool {
|
||||||
if container.OneOff || container.ServiceName == "" {
|
if container.OneOff || container.ServiceName == "" {
|
||||||
|
|
@ -30,58 +30,60 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return &panels.SideListPanel[*commands.Container]{
|
return &panels.SideListPanel[*commands.ContainerListItem]{
|
||||||
ContextState: &panels.ContextState[*commands.Container]{
|
ContextState: &panels.ContextState[*commands.ContainerListItem]{
|
||||||
GetMainTabs: func() []panels.MainTab[*commands.Container] {
|
GetMainTabs: func() []panels.MainTab[*commands.ContainerListItem] {
|
||||||
return []panels.MainTab[*commands.Container]{
|
return []panels.MainTab[*commands.ContainerListItem]{
|
||||||
{
|
{
|
||||||
Key: "logs",
|
Key: "logs",
|
||||||
Title: gui.Tr.LogsTitle,
|
Title: gui.Tr.LogsTitle,
|
||||||
Render: gui.renderContainerLogsToMain,
|
Render: gui.renderContainerListItemLogs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "stats",
|
Key: "stats",
|
||||||
Title: gui.Tr.StatsTitle,
|
Title: gui.Tr.StatsTitle,
|
||||||
Render: gui.renderContainerStats,
|
Render: gui.renderContainerListItemStats,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "env",
|
Key: "env",
|
||||||
Title: gui.Tr.EnvTitle,
|
Title: gui.Tr.EnvTitle,
|
||||||
Render: gui.renderContainerEnv,
|
Render: gui.renderContainerListItemEnv,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "config",
|
Key: "config",
|
||||||
Title: gui.Tr.ConfigTitle,
|
Title: gui.Tr.ConfigTitle,
|
||||||
Render: gui.renderContainerConfig,
|
Render: gui.renderContainerListItemConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "top",
|
Key: "top",
|
||||||
Title: gui.Tr.TopTitle,
|
Title: gui.Tr.TopTitle,
|
||||||
Render: gui.renderContainerTop,
|
Render: gui.renderContainerListItemTop,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
GetItemContextCacheKey: func(container *commands.Container) string {
|
GetItemContextCacheKey: func(item *commands.ContainerListItem) string {
|
||||||
// Including the container state in the cache key so that if the container
|
// Including the state in the cache key so that if the container/pod
|
||||||
// restarts we re-read the logs. In the past we've had some glitchiness
|
// restarts we re-read the logs.
|
||||||
// where a container restarts but the new logs don't get read.
|
return "containers-" + item.ID() + "-" + item.State()
|
||||||
// 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
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ListPanel: panels.ListPanel[*commands.Container]{
|
ListPanel: panels.ListPanel[*commands.ContainerListItem]{
|
||||||
List: panels.NewFilteredList[*commands.Container](),
|
List: panels.NewFilteredList[*commands.ContainerListItem](),
|
||||||
View: gui.Views.Containers,
|
View: gui.Views.Containers,
|
||||||
},
|
},
|
||||||
NoItemsMessage: gui.Tr.NoContainers,
|
NoItemsMessage: gui.Tr.NoContainers,
|
||||||
Gui: gui.intoInterface(),
|
Gui: gui.intoInterface(),
|
||||||
// sortedContainers returns containers sorted by state if c.SortContainersByState is true (follows 1- running, 2- exited, 3- created)
|
// Sort items: pods first (with their containers grouped), then standalone containers
|
||||||
// and sorted by name if c.SortContainersByState is false
|
Sort: func(a *commands.ContainerListItem, b *commands.ContainerListItem) bool {
|
||||||
Sort: func(a *commands.Container, b *commands.Container) bool {
|
return sortContainerListItems(a, b, gui.Config.UserConfig.Gui.LegacySortContainers)
|
||||||
return sortContainers(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
|
// 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,
|
// 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.
|
// so we will need to keep an eye on this.
|
||||||
|
|
@ -95,8 +97,8 @@ func (gui *Gui) getContainersPanel() *panels.SideListPanel[*commands.Container]
|
||||||
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
GetTableCells: func(container *commands.Container) []string {
|
GetTableCells: func(item *commands.ContainerListItem) []string {
|
||||||
return presentation.GetContainerDisplayStrings(&gui.Config.UserConfig.Gui, container)
|
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]
|
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 {
|
func (gui *Gui) renderContainerEnv(container *commands.Container) tasks.TaskFunc {
|
||||||
return gui.NewSimpleRenderStringTask(func() string { return gui.containerEnv(container) })
|
return gui.NewSimpleRenderStringTask(func() string { return gui.containerEnv(container) })
|
||||||
}
|
}
|
||||||
|
|
@ -179,9 +312,9 @@ func (gui *Gui) containerConfigStr(container *commands.Container) string {
|
||||||
output += "\n"
|
output += "\n"
|
||||||
for _, mount := range container.Details.Mounts {
|
for _, mount := range container.Details.Mounts {
|
||||||
if mount.Type == "volume" {
|
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 {
|
} 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 {
|
} 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 {
|
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 {
|
if err != nil {
|
||||||
return 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 {
|
handleMenuPress := func(force bool, removeVolumes bool) error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||||
if err := ctr.Remove(force, removeVolumes); err != nil {
|
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 {
|
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 {
|
if err != nil {
|
||||||
return 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 {
|
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 {
|
if err != nil {
|
||||||
return 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.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.StopContainer, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
||||||
if err := ctr.Stop(); err != nil {
|
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 {
|
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 {
|
if err != nil {
|
||||||
return 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 {
|
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
|
||||||
if err := ctr.Restart(); err != nil {
|
if err := ctr.Restart(); err != nil {
|
||||||
return gui.createErrorPanel(err.Error())
|
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 {
|
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 {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if item.IsPod {
|
||||||
|
return gui.createErrorPanel("Attach not yet supported for pods")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctr := item.Container
|
||||||
c, err := ctr.Attach()
|
c, err := ctr.Attach()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.createErrorPanel(err.Error())
|
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 {
|
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 {
|
if err != nil {
|
||||||
return 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleContainersExecShell(g *gocui.Gui, v *gocui.View) error {
|
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 {
|
if err != nil {
|
||||||
return 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 {
|
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 {
|
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 {
|
if err != nil {
|
||||||
return 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{
|
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{
|
||||||
Container: ctr,
|
Container: item.Container,
|
||||||
})
|
})
|
||||||
|
|
||||||
customCommands := gui.Config.UserConfig.CustomCommands.Containers
|
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 {
|
func (gui *Gui) handleStopContainers() error {
|
||||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmStopContainers, func(g *gocui.Gui, v *gocui.View) 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 {
|
return gui.WithWaitingStatus(gui.Tr.StoppingStatus, func() error {
|
||||||
for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
|
for _, item := range gui.Panels.Containers.List.GetAllItems() {
|
||||||
if err := ctr.Stop(); err != nil {
|
if !item.IsPod && item.Container != nil {
|
||||||
gui.Log.Error(err)
|
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 {
|
func (gui *Gui) handleRemoveContainers() error {
|
||||||
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmRemoveContainers, func(g *gocui.Gui, v *gocui.View) 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 {
|
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
|
||||||
for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
|
for _, item := range gui.Panels.Containers.List.GetAllItems() {
|
||||||
if err := ctr.Remove(true, false); err != nil {
|
if !item.IsPod && item.Container != nil {
|
||||||
gui.Log.Error(err)
|
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
|
// Open first port in browser
|
||||||
func (gui *Gui) handleContainersOpenInBrowserCommand(g *gocui.Gui, v *gocui.View) error {
|
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 {
|
if err != nil {
|
||||||
return 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 {
|
func (gui *Gui) openContainerInBrowser(ctr *commands.Container) error {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ type Gui struct {
|
||||||
type Panels struct {
|
type Panels struct {
|
||||||
Projects *panels.SideListPanel[*commands.Project]
|
Projects *panels.SideListPanel[*commands.Project]
|
||||||
Services *panels.SideListPanel[*commands.Service]
|
Services *panels.SideListPanel[*commands.Service]
|
||||||
Containers *panels.SideListPanel[*commands.Container]
|
Containers *panels.SideListPanel[*commands.ContainerListItem]
|
||||||
Images *panels.SideListPanel[*commands.Image]
|
Images *panels.SideListPanel[*commands.Image]
|
||||||
Volumes *panels.SideListPanel[*commands.Volume]
|
Volumes *panels.SideListPanel[*commands.Volume]
|
||||||
Networks *panels.SideListPanel[*commands.Network]
|
Networks *panels.SideListPanel[*commands.Network]
|
||||||
|
|
@ -290,7 +290,14 @@ func (gui *Gui) setPanels() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) updateContainerDetails() error {
|
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() {
|
func (gui *Gui) refresh() {
|
||||||
|
|
@ -469,9 +476,9 @@ func (gui *Gui) monitorContainerStats(ctx context.Context) {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
for _, container := range gui.Panels.Containers.List.GetAllItems() {
|
for _, item := range gui.Panels.Containers.List.GetAllItems() {
|
||||||
if !container.MonitoringStats {
|
if !item.IsPod && item.Container != nil && !item.Container.MonitoringStats {
|
||||||
go gui.PodmanCommand.CreateClientStatMonitor(container)
|
go gui.PodmanCommand.CreateClientStatMonitor(item.Container)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/jesseduffield/asciigraph"
|
|
||||||
"github.com/christophe-duc/lazypodman/pkg/commands"
|
"github.com/christophe-duc/lazypodman/pkg/commands"
|
||||||
"github.com/christophe-duc/lazypodman/pkg/config"
|
"github.com/christophe-duc/lazypodman/pkg/config"
|
||||||
"github.com/christophe-duc/lazypodman/pkg/utils"
|
"github.com/christophe-duc/lazypodman/pkg/utils"
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/jesseduffield/asciigraph"
|
||||||
"github.com/mcuadros/go-lookup"
|
"github.com/mcuadros/go-lookup"
|
||||||
"github.com/samber/lo"
|
"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 {
|
func displayContainerImage(container *commands.Container) string {
|
||||||
return strings.TrimPrefix(container.Summary.Image, "sha256:")
|
return strings.TrimPrefix(container.Summary.Image, "sha256:")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,18 +109,17 @@ type TranslationSet struct {
|
||||||
FilterList string
|
FilterList string
|
||||||
OpenInBrowser string
|
OpenInBrowser string
|
||||||
SortContainersByState string
|
SortContainersByState string
|
||||||
|
LogsTitle string
|
||||||
LogsTitle string
|
ConfigTitle string
|
||||||
ConfigTitle string
|
EnvTitle string
|
||||||
EnvTitle string
|
ComposeConfigTitle string
|
||||||
ComposeConfigTitle string
|
StatsTitle string
|
||||||
StatsTitle string
|
CreditsTitle string
|
||||||
CreditsTitle string
|
ContainerConfigTitle string
|
||||||
ContainerConfigTitle string
|
ContainerEnvTitle string
|
||||||
ContainerEnvTitle string
|
NothingToDisplay string
|
||||||
NothingToDisplay string
|
NoContainerForService string
|
||||||
NoContainerForService string
|
CannotDisplayEnvVariables string
|
||||||
CannotDisplayEnvVariables string
|
|
||||||
|
|
||||||
No string
|
No string
|
||||||
Yes 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/images
|
||||||
github.com/containers/podman/v5/pkg/bindings/internal/util
|
github.com/containers/podman/v5/pkg/bindings/internal/util
|
||||||
github.com/containers/podman/v5/pkg/bindings/network
|
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/system
|
||||||
github.com/containers/podman/v5/pkg/bindings/volumes
|
github.com/containers/podman/v5/pkg/bindings/volumes
|
||||||
github.com/containers/podman/v5/pkg/checkpoint/crutils
|
github.com/containers/podman/v5/pkg/checkpoint/crutils
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue