Merge pull request #7 from christophe-duc/feature/add_restart_pod

added restart functionality for pods
This commit is contained in:
Christophe 2026-01-07 23:59:22 -04:00 committed by GitHub
commit 70a9e480de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 1042 additions and 879 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,8 @@
package commands
import (
"context"
"fmt"
"time"
"github.com/sasha-s/go-deadlock"
@ -32,6 +34,13 @@ func (p *Pod) State() string {
return p.Summary.Status
}
// Restart restarts the pod
func (p *Pod) Restart() error {
p.Log.Warn(fmt.Sprintf("restarting pod %s", p.Name))
ctx := context.Background()
return p.Runtime.RestartPod(ctx, p.ID, nil)
}
// HasContainers returns true if the pod has non-infra containers.
func (p *Pod) HasContainers() bool {
return len(p.Containers) > 0

View file

@ -40,6 +40,7 @@ type ContainerRuntime interface {
// Pod operations
ListPods(ctx context.Context) ([]PodSummary, error)
PodStats(ctx context.Context, id string, stream bool) (<-chan PodStatsEntry, <-chan error)
RestartPod(ctx context.Context, id string, timeout *int) error
// Events streams container/image/volume/network events
Events(ctx context.Context) (<-chan Event, <-chan error)

View file

@ -375,7 +375,7 @@ func (r *LibpodRuntime) PodStats(ctx context.Context, id string, stream bool) (<
}
// aggregatePodContainerStats collects and aggregates stats from all containers in a pod.
func (r *LibpodRuntime) aggregatePodContainerStats(ctx context.Context, pod *libpod.Pod) (PodStatsEntry, error) {
func (r *LibpodRuntime) aggregatePodContainerStats(_ context.Context, pod *libpod.Pod) (PodStatsEntry, error) {
ctrs, err := pod.AllContainers()
if err != nil {
return PodStatsEntry{}, err
@ -438,6 +438,16 @@ func (r *LibpodRuntime) aggregatePodContainerStats(ctx context.Context, pod *lib
return entry, nil
}
// RestartPod restarts a pod.
func (r *LibpodRuntime) RestartPod(ctx context.Context, id string, timeout *int) error {
pod, err := r.runtime.LookupPod(id)
if err != nil {
return err
}
_, err = pod.Restart(ctx)
return err
}
// Events streams container runtime events.
// For libpod, we use a polling approach since direct event streaming requires more setup.
func (r *LibpodRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {

View file

@ -145,6 +145,10 @@ func (r *LibpodRuntime) PodStats(ctx context.Context, id string, stream bool) (<
return statsChan, errChan
}
func (r *LibpodRuntime) RestartPod(ctx context.Context, id string, timeout *int) error {
return ErrLibpodNotAvailable
}
// Events returns an error channel on non-Linux platforms.
func (r *LibpodRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
eventsChan := make(chan Event)

View file

@ -40,8 +40,9 @@ type MockRuntime struct {
PruneNetworksFunc func(ctx context.Context) error
// Pod operation mocks
ListPodsFunc func(ctx context.Context) ([]PodSummary, error)
PodStatsFunc func(ctx context.Context, id string, stream bool) (<-chan PodStatsEntry, <-chan error)
ListPodsFunc func(ctx context.Context) ([]PodSummary, error)
PodStatsFunc func(ctx context.Context, id string, stream bool) (<-chan PodStatsEntry, <-chan error)
RestartPodFunc func(ctx context.Context, id string, timeout *int) error
// Event mock
EventsFunc func(ctx context.Context) (<-chan Event, <-chan error)
@ -280,6 +281,14 @@ func (m *MockRuntime) PodStats(ctx context.Context, id string, stream bool) (<-c
return statsCh, errCh
}
func (m *MockRuntime) RestartPod(ctx context.Context, id string, timeout *int) error {
m.recordCall("RestartPod", id, timeout)
if m.RestartPodFunc != nil {
return m.RestartPodFunc(ctx, id, timeout)
}
return ErrMockNotImplemented
}
// Events
func (m *MockRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {

View file

@ -340,6 +340,12 @@ func (r *SocketRuntime) PodStats(ctx context.Context, id string, stream bool) (<
return statsChan, errChan
}
// RestartPod restarts a pod.
func (r *SocketRuntime) RestartPod(ctx context.Context, id string, timeout *int) error {
_, err := pods.Restart(r.conn, id, nil)
return err
}
// aggregatePodStats combines stats from all containers in a pod into a single entry.
func aggregatePodStats(reports []*types.PodStatsReport) PodStatsEntry {
if len(reports) == 0 {
@ -405,30 +411,30 @@ func aggregatePodStats(reports []*types.PodStatsReport) PodStatsEntry {
func parsePercentage(s string) float64 {
s = strings.TrimSuffix(strings.TrimSpace(s), "%")
var val float64
fmt.Sscanf(s, "%f", &val)
_, _ = fmt.Sscanf(s, "%f", &val)
return val
}
// parseMemoryBytes parses memory usage string like "1000000 / 4000000" into usage and limit.
func parseMemoryBytes(s string) (usage, limit uint64) {
func parseMemoryBytes(s string) (uint64, uint64) {
parts := strings.Split(s, "/")
if len(parts) != 2 {
return 0, 0
}
usage = parseByteValue(strings.TrimSpace(parts[0]))
limit = parseByteValue(strings.TrimSpace(parts[1]))
return
usage := parseByteValue(strings.TrimSpace(parts[0]))
limit := parseByteValue(strings.TrimSpace(parts[1]))
return usage, limit
}
// parseIOBytes parses I/O string like "1.5kB / 2.3kB" into input and output bytes.
func parseIOBytes(s string) (input, output uint64) {
func parseIOBytes(s string) (uint64, uint64) {
parts := strings.Split(s, "/")
if len(parts) != 2 {
return 0, 0
}
input = parseByteValue(strings.TrimSpace(parts[0]))
output = parseByteValue(strings.TrimSpace(parts[1]))
return
input := parseByteValue(strings.TrimSpace(parts[0]))
output := parseByteValue(strings.TrimSpace(parts[1]))
return input, output
}
// parseByteValue parses a byte value string with optional unit (e.g., "1.5kB", "10MB", "1000000").
@ -469,7 +475,7 @@ func parseByteValue(s string) uint64 {
func parseUint(s string) uint64 {
s = strings.TrimSpace(s)
var val uint64
fmt.Sscanf(s, "%d", &val)
_, _ = fmt.Sscanf(s, "%d", &val)
return val
}

View file

@ -568,7 +568,12 @@ func (gui *Gui) handleContainerRestart(g *gocui.Gui, v *gocui.View) error {
}
if item.IsPod {
return gui.createErrorPanel("Restart not yet supported for pods")
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
if err := item.Pod.Restart(); err != nil {
return gui.createErrorPanel(err.Error())
}
return nil
})
}
ctr := item.Container