executed phase2 of the plan

This commit is contained in:
christophe-duc 2026-01-07 08:56:09 -04:00
parent 1d12ef109d
commit 56e691c662
31 changed files with 3210 additions and 922 deletions

200
go.mod
View file

@ -1,16 +1,14 @@
module github.com/christophe-duc/lazypodman
go 1.22
toolchain go1.23.6
go 1.24.2
require (
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/docker/cli v27.1.1+incompatible
github.com/docker/docker v28.5.2+incompatible
github.com/fatih/color v1.10.0
github.com/containers/podman/v5 v5.7.1
github.com/docker/docker v28.5.1+incompatible
github.com/fatih/color v1.15.0
github.com/go-errors/errors v1.5.1
github.com/gookit/color v1.5.0
github.com/imdario/mergo v0.3.16
@ -20,66 +18,180 @@ require (
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56
github.com/mattn/go-runewidth v0.0.15
github.com/mattn/go-runewidth v0.0.16
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767
github.com/mgutz/str v1.2.0
github.com/pmezard/go-difflib v1.0.0
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
github.com/samber/lo v1.31.0
github.com/sasha-s/go-deadlock v0.3.1
github.com/sasha-s/go-deadlock v0.3.5
github.com/sirupsen/logrus v1.9.3
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.11.1
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
)
require (
dario.cat/mergo v1.0.2 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/checkpoint-restore/checkpointctl v1.4.0 // indirect
github.com/checkpoint-restore/go-criu/v7 v7.2.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v1.0.0-rc.1 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.17.0 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/containernetworking/cni v1.3.0 // indirect
github.com/containernetworking/plugins v1.8.0 // indirect
github.com/containers/buildah v1.42.2 // indirect
github.com/containers/conmon v2.0.20+incompatible // indirect
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
github.com/containers/luksy v0.0.0-20250910190358-2cf5bc928957 // indirect
github.com/containers/ocicrypt v1.2.1 // indirect
github.com/containers/psgo v1.9.1-0.20250826150930-4ae76f200c86 // indirect
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
github.com/coreos/go-systemd/v22 v22.6.0 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect
github.com/cyphar/filepath-securejoin v0.5.2 // indirect
github.com/disiqueira/gotree/v3 v3.0.2 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.4 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/ebitengine/purego v0.9.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fsouza/go-dockerclient v1.12.2 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus/v5 v5.1.1-0.20241109141217-c266b19b28e9 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-containerregistry v0.20.6 // indirect
github.com/google/go-intervals v0.0.2 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/schema v1.4.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hpcloud/tail v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.4.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/mattn/go-sqlite3 v1.14.32 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mistifyio/go-zfs/v3 v3.1.0 // indirect
github.com/moby/buildkit v0.25.1 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.1.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/capability v0.4.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/nxadm/tail v1.4.11 // indirect
github.com/opencontainers/cgroups v0.0.5 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runc v1.3.4 // indirect
github.com/opencontainers/runtime-spec v1.2.1 // indirect
github.com/opencontainers/runtime-tools v0.9.1-0.20250523060157-0ea5ed0382a2 // indirect
github.com/opencontainers/selinux v1.13.1 // indirect
github.com/openshift/imagebuilder v1.2.19 // indirect
github.com/pkg/sftp v1.13.9 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/proglottis/gpgme v0.1.5 // indirect
github.com/seccomp/libseccomp-golang v0.11.1 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect
github.com/shirou/gopsutil/v4 v4.25.9 // indirect
github.com/sigstore/fulcio v1.7.1 // indirect
github.com/sigstore/protobuf-specs v0.4.1 // indirect
github.com/sigstore/sigstore v1.9.5 // indirect
github.com/skeema/knownhosts v1.3.2 // indirect
github.com/smallstep/pkcs7 v0.1.1 // indirect
github.com/spf13/cobra v1.10.1 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect
github.com/sylabs/sif/v2 v2.22.0 // indirect
github.com/tchap/go-patricia/v2 v2.3.3 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/vbatts/tar-split v0.12.1 // indirect
github.com/vbauerster/mpb/v8 v8.10.2 // indirect
github.com/vishvananda/netlink v1.3.1 // indirect
github.com/vishvananda/netns v0.0.5 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.etcd.io/bbolt v1.4.3 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.podman.io/common v0.66.1 // indirect
go.podman.io/image/v5 v5.38.0 // indirect
go.podman.io/storage v1.61.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/crypto v0.43.0 // indirect
golang.org/x/mod v0.28.0 // indirect
golang.org/x/net v0.45.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/time v0.11.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
google.golang.org/grpc v1.72.2 // indirect
google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
tags.cncf.io/container-device-interface v1.0.1 // indirect
tags.cncf.io/container-device-interface/specs-go v1.0.0 // indirect
)
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/gdamore/encoding v1.0.1 // indirect
github.com/gdamore/tcell/v2 v2.7.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-yaml v1.11.0
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/term v0.36.0 // indirect
golang.org/x/text v0.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.5.1 // indirect
)

541
go.sum
View file

@ -1,13 +1,46 @@
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c h1:YDsGA6tou+tAxVe0Dre29iSbQ8TrWdWfwOisKArJT5E=
github.com/OpenPeeDeeP/xdg v0.2.1-0.20190312153938-4ba9e1eb294c/go.mod h1:tMoSueLQlMf0TCldjrJLNIjAc5qAOIcHt5REi88/Ygo=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 h1:5L8Mj9Co9sJVgW3TpYk2gxGJnDjsYuboNTcRmbtGKGs=
github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6/go.mod h1:3HgLJ9d18kXMLQlJvIY3+FszZYMxCz8WfE2MQ7hDY0w=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1 h1:1fx+RA5lk1ZkzPAUP7DEgZnVHYxEcHO77vQO/V8z/2Q=
github.com/boz/go-throttle v0.0.0-20160922054636-fdc4eab740c1/go.mod h1:z0nyIb42Zs97wyX1V+8MbEFhHeTw1OgFQfR6q57ZuHc=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/checkpointctl v1.4.0 h1:3kRns56TArwiyHOMakaumUgSZZlB1hZBkjVgR6IeZ3E=
github.com/checkpoint-restore/checkpointctl v1.4.0/go.mod h1:ynQ52zQBazgcTZuxpwTFzRinIcAf0haDTC1X1LA/FKA=
github.com/checkpoint-restore/go-criu/v7 v7.2.0 h1:qGiWA4App1gGlEfIJ68WR9jbezV9J7yZdjzglezcqKo=
github.com/checkpoint-restore/go-criu/v7 v7.2.0/go.mod h1:u0LCWLg0w4yqqu14aXhiB4YD3a1qd8EcCEg7vda5dwo=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@ -16,28 +49,76 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
github.com/containerd/stargz-snapshotter/estargz v0.17.0 h1:+TyQIsR/zSFI1Rm31EQBwpAA1ovYgIKHy7kctL3sLcE=
github.com/containerd/stargz-snapshotter/estargz v0.17.0/go.mod h1:s06tWAiJcXQo9/8AReBCIo/QxcXFZ2n4qfsRnpl71SM=
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo=
github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4=
github.com/containernetworking/plugins v1.8.0 h1:WjGbV/0UQyo8A4qBsAh6GaDAtu1hevxVxsEuqtBqUFk=
github.com/containernetworking/plugins v1.8.0/go.mod h1:JG3BxoJifxxHBhG3hFyxyhid7JgRVBu/wtooGEvWf1c=
github.com/containers/buildah v1.42.2 h1:be4mKtMOtvuW3R1TYWP+MupxzCaq6PRn7+m1iZH9YbE=
github.com/containers/buildah v1.42.2/go.mod h1:SDA+ClXamnZPV7GBS2uKY0dfXKfvTykSUA+kJWa1mNg=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/luksy v0.0.0-20250910190358-2cf5bc928957 h1:WxixhZ0typ8o668V0V7RVCZb3lNs58UF42RbwlQ4vdE=
github.com/containers/luksy v0.0.0-20250910190358-2cf5bc928957/go.mod h1:fGPsLPRi1etbHfe5o6sdx6ajsW810tI43uyF6ugmP/o=
github.com/containers/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpVhSmM=
github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ=
github.com/containers/podman/v5 v5.7.1 h1:rxt3Fl3JS85ZnWZU7jTqR9YOk9fYFqI/2Gy6UMooAhk=
github.com/containers/podman/v5 v5.7.1/go.mod h1:+3SYEQiTjid9pnoFMF8jKWVOymTzIQCFGDsZ28b5nqw=
github.com/containers/psgo v1.9.1-0.20250826150930-4ae76f200c86 h1:bYj0TVlkRZtMJYd6SbFOi1gjUJDJmVsYCpJla3URD7Y=
github.com/containers/psgo v1.9.1-0.20250826150930-4ae76f200c86/go.mod h1:52GX23ST30pXpeviDvNpOCXoHqr+WJDv+7BjEH+AabY=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo=
github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q=
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
github.com/cyphar/filepath-securejoin v0.5.2 h1:w/T2bhKr4pgwG0SUGjU4S/Is9+zUknLh5ROTJLzWX8E=
github.com/cyphar/filepath-securejoin v0.5.2/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/disiqueira/gotree/v3 v3.0.2 h1:ik5iuLQQoufZBNPY518dXhiO5056hyNBIK9lWhkNRq8=
github.com/disiqueira/gotree/v3 v3.0.2/go.mod h1:ZuyjE4+mUQZlbpkI24AmruZKhg3VHEgPLDY8Qk+uUu8=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY=
github.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM=
github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI=
github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8 h1:IMfrF5LCzP2Vhw7j4IIH3HxPsCLuZYjDqFAM/C88ulg=
github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8/go.mod h1:LFyLie6XcDbyKGeVK6bHe+9aJTYCxWLBg5IrJZOaXKA=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/ebitengine/purego v0.9.0 h1:mh0zpKBIXDceC63hpvPuGLiJ8ZAa3DfrFTudmfi8A4k=
github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fsouza/go-dockerclient v1.12.2 h1:+pbP/SacoHfqaVZuiudvcdYGd9jzU7y9EcgoBOHivEI=
github.com/fsouza/go-dockerclient v1.12.2/go.mod h1:ZGCkAsnBGjnTRG9wV6QaICPJ5ig2KlaxTccDQy5WQ38=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
@ -46,32 +127,68 @@ github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54=
github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/godbus/dbus/v5 v5.1.1-0.20241109141217-c266b19b28e9 h1:Kzr9J0S0V2PRxiX6B6xw1kWjzsIyjLO2Ibi4fNTaYBM=
github.com/godbus/dbus/v5 v5.1.1-0.20241109141217-c266b19b28e9/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU=
github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y=
github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM=
github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
github.com/jesseduffield/asciigraph v0.0.0-20190605104717-6d88e39309ee h1:7Zi/OQlGbMz4MT2V1+prN/gv1C64NDyVb/MbJnS0ZfA=
@ -84,164 +201,396 @@ github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996 h1:CH1en6Gp
github.com/jesseduffield/lazycore v0.0.0-20221023210126-718a4caea996/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56 h1:33wSxJWU/f2TAozHYtJ8zqBxEnEVYM+22moLoiAkxvg=
github.com/jesseduffield/yaml v0.0.0-20190702115811-b900b7e08b56/go.mod h1:FZJBwOhE+RXz8EVZfY+xnbCw2cVOwxlK3/aIi581z/s=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs=
github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec h1:2tTW6cDth2TSgRbAhD7yjZzTQmcN25sDRPEeinR51yQ=
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec/go.mod h1:TmwEoGCwIti7BCeJ9hescZgRtatxRE+A72pCoPfmcfk=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767 h1:BrhJNdEFWGuiJk/3/SwsG5Rex3zjFxYsDi2bpd7382Y=
github.com/mcuadros/go-lookup v0.0.0-20171110082742-5650f26be767/go.mod h1:ct+byCpkFokm4J0tiuAvB8cf2ttm6GcCe89Yr25nGKg=
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mistifyio/go-zfs/v3 v3.1.0 h1:FZaylcg0hjUp27i23VcJJQiuBeAZjrC8lPqCGM1CopY=
github.com/mistifyio/go-zfs/v3 v3.1.0/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
github.com/moby/buildkit v0.25.1 h1:j7IlVkeNbEo+ZLoxdudYCHpmTsbwKvhgc/6UJ/mY/o8=
github.com/moby/buildkit v0.25.1/go.mod h1:phM8sdqnvgK2y1dPDnbwI6veUCXHOZ6KFSl6E164tkc=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/ginkgo/v2 v2.26.0 h1:1J4Wut1IlYZNEAWIV3ALrT9NfiaGW2cDCJQSFQMs/gE=
github.com/onsi/ginkgo/v2 v2.26.0/go.mod h1:qhEywmzWTBUY88kfO0BRvX4py7scov9yR+Az2oavUzw=
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/opencontainers/cgroups v0.0.5 h1:DRITAqcOnY0uSBzIpt1RYWLjh5DPDiqUs4fY6Y0ktls=
github.com/opencontainers/cgroups v0.0.5/go.mod h1:oWVzJsKK0gG9SCRBfTpnn16WcGEqDI8PAcpMGbqWxcs=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runc v1.3.4 h1:+lwmPUTzbgv0JFqu8zBU2WtHYbm+JPPS9hxB/PvWd30=
github.com/opencontainers/runc v1.3.4/go.mod h1:o1wyv76EDlTkcf0KTFgN8bMWLPvgF/HfX709lDv+rr4=
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.9.1-0.20250523060157-0ea5ed0382a2 h1:2xZEHOdeQBV6PW8ZtimN863bIOl7OCW/X10K0cnxKeA=
github.com/opencontainers/runtime-tools v0.9.1-0.20250523060157-0ea5ed0382a2/go.mod h1:MXdPzqAA8pHC58USHqNCSjyLnRQ6D+NjbpP+02Z1U/0=
github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE=
github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg=
github.com/openshift/imagebuilder v1.2.19 h1:Xqq36KMJgsRU2MPaLRML23Myvk+AaY8pE8VJ6m6Vmy4=
github.com/openshift/imagebuilder v1.2.19/go.mod h1:fdbnfQWjxMBoB/jrvEzUk+UT1zqvtZZj7oQ7GU6RD9I=
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw=
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/proglottis/gpgme v0.1.5 h1:KCGyOw8sQ+SI96j6G8D8YkOGn+1TwbQTT9/zQXoVlz0=
github.com/proglottis/gpgme v0.1.5/go.mod h1:5LoXMgpE4bttgwwdv9bLs/vwqv3qV7F4glEEZ7mRKrM=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/samber/lo v1.31.0 h1:Sfa+/064Tdo4SvlohQUQzBhgSer9v/coGvKQI/XLWAM=
github.com/samber/lo v1.31.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
github.com/sebdah/goldie/v2 v2.7.1 h1:PkBHymaYdtvEkZV7TmyqKxdmn5/Vcj+8TpATWZjnG5E=
github.com/sebdah/goldie/v2 v2.7.1/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/seccomp/libseccomp-golang v0.11.1 h1:wuk4ZjSx6kyQII4rj6G6fvVzRHQaSiPvccJazDagu4g=
github.com/seccomp/libseccomp-golang v0.11.1/go.mod h1:5m1Lk8E9OwgZTTVz4bBOer7JuazaBa+xTkM895tDiWc=
github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g=
github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shirou/gopsutil/v4 v4.25.9 h1:JImNpf6gCVhKgZhtaAHJ0serfFGtlfIlSC08eaKdTrU=
github.com/shirou/gopsutil/v4 v4.25.9/go.mod h1:gxIxoC+7nQRwUl/xNhutXlD8lq+jxTgpIkEf3rADHL8=
github.com/sigstore/fulcio v1.7.1 h1:RcoW20Nz49IGeZyu3y9QYhyyV3ZKQ85T+FXPKkvE+aQ=
github.com/sigstore/fulcio v1.7.1/go.mod h1:7lYY+hsd8Dt+IvKQRC+KEhWpCZ/GlmNvwIa5JhypMS8=
github.com/sigstore/protobuf-specs v0.4.1 h1:5SsMqZbdkcO/DNHudaxuCUEjj6x29tS2Xby1BxGU7Zc=
github.com/sigstore/protobuf-specs v0.4.1/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc=
github.com/sigstore/sigstore v1.9.5 h1:Wm1LT9yF4LhQdEMy5A2JeGRHTrAWGjT3ubE5JUSrGVU=
github.com/sigstore/sigstore v1.9.5/go.mod h1:VtxgvGqCmEZN9X2zhFSOkfXxvKUjpy8RpUW39oCtoII=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
github.com/smallstep/pkcs7 v0.1.1 h1:x+rPdt2W088V9Vkjho4KtoggyktZJlMduZAtRHm68LU=
github.com/smallstep/pkcs7 v0.1.1/go.mod h1:dL6j5AIz9GHjVEBTXtW+QliALcgM19RtXaTeyxI+AfA=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw=
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/sylabs/sif/v2 v2.22.0 h1:Y+xXufp4RdgZe02SR3nWEg7S6q4tPWN237WHYzkDSKA=
github.com/sylabs/sif/v2 v2.22.0/go.mod h1:W1XhWTmG1KcG7j5a3KSYdMcUIFvbs240w/MMVW627hs=
github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc=
github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/vbauerster/mpb/v8 v8.10.2 h1:2uBykSHAYHekE11YvJhKxYmLATKHAGorZwFlyNw4hHM=
github.com/vbauerster/mpb/v8 v8.10.2/go.mod h1:+Ja4P92E3/CorSZgfDtK46D7AVbDqmBQRTmyTqPElo0=
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.podman.io/common v0.66.1 h1:zDyd4HhVgQAN8LupBHCnhtM3FEOJ9DwmThjulXZq2qA=
go.podman.io/common v0.66.1/go.mod h1:aNd2a0S7pY+fx1X5kpQYuF4hbwLU8ZOccuVrhu7h1Xc=
go.podman.io/image/v5 v5.38.0 h1:aUKrCANkPvze1bnhLJsaubcfz0d9v/bSDLnwsXJm6G4=
go.podman.io/image/v5 v5.38.0/go.mod h1:hSIoIUzgBnmc4DjoIdzk63aloqVbD7QXDMkSE/cvG90=
go.podman.io/storage v1.61.0 h1:5hD/oyRYt1f1gxgvect+8syZBQhGhV28dCw2+CZpx0Q=
go.podman.io/storage v1.61.0/go.mod h1:A3UBK0XypjNZ6pghRhuxg62+2NIm5lcUGv/7XyMhMUI=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0=
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e h1:UdXH7Kzbj+Vzastr5nVfccbmFsmYNygVLSPk1pEfDoY=
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e/go.mod h1:085qFyf2+XaZlRdCgKNCIZ3afY2p4HHZdoIRpId8F4A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc=
tags.cncf.io/container-device-interface v1.0.1/go.mod h1:JojJIOeW3hNbcnOH2q0NrWNha/JuHoDZcmYxAZwb2i0=
tags.cncf.io/container-device-interface/specs-go v1.0.0 h1:8gLw29hH1ZQP9K1YtAzpvkHCjjyIxHZYzBAvlQ+0vD8=
tags.cncf.io/container-device-interface/specs-go v1.0.0/go.mod h1:u86hoFWqnh3hWz3esofRFKbI261bUlvUfLKGrDhJkgQ=

View file

@ -8,10 +8,10 @@ import (
"runtime"
"runtime/debug"
"github.com/docker/docker/client"
"github.com/go-errors/errors"
"github.com/integrii/flaggy"
"github.com/christophe-duc/lazypodman/pkg/app"
"github.com/christophe-duc/lazypodman/pkg/commands"
"github.com/christophe-duc/lazypodman/pkg/config"
"github.com/christophe-duc/lazypodman/pkg/utils"
"github.com/jesseduffield/yaml"
@ -88,7 +88,7 @@ func main() {
os.Exit(0)
}
if client.IsErrConnectionFailed(err) {
if commands.IsConnectionFailed(err) {
log.Println(app.Tr.ConnectionFailed)
os.Exit(0)
}

View file

@ -20,7 +20,7 @@ type App struct {
Config *config.AppConfig
Log *logrus.Entry
OSCommand *commands.OSCommand
DockerCommand *commands.DockerCommand
PodmanCommand *commands.PodmanCommand
Gui *gui.Gui
Tr *i18n.TranslationSet
ErrorChan chan error
@ -43,12 +43,12 @@ func NewApp(config *config.AppConfig) (*App, error) {
// here is the place to make use of the docker-compose.yml file in the current directory
app.DockerCommand, err = commands.NewDockerCommand(app.Log, app.OSCommand, app.Tr, app.Config, app.ErrorChan)
app.PodmanCommand, err = commands.NewPodmanCommand(app.Log, app.OSCommand, app.Tr, app.Config, app.ErrorChan)
if err != nil {
return app, err
}
app.closers = append(app.closers, app.DockerCommand)
app.Gui, err = gui.NewGui(app.Log, app.DockerCommand, app.OSCommand, app.Tr, config, app.ErrorChan)
app.closers = append(app.closers, app.PodmanCommand)
app.Gui, err = gui.NewGui(app.Log, app.PodmanCommand, app.OSCommand, app.Tr, config, app.ErrorChan)
if err != nil {
return app, err
}
@ -77,6 +77,14 @@ func (app *App) KnownError(err error) (string, bool) {
originalError: "Got permission denied while trying to connect to the Docker daemon socket",
newError: app.Tr.CannotAccessDockerSocketError,
},
{
originalError: "Got permission denied while trying to connect to the Podman",
newError: app.Tr.CannotAccessDockerSocketError,
},
{
originalError: "permission denied while trying to connect",
newError: app.Tr.CannotAccessDockerSocketError,
},
}
for _, mapping := range mappings {

View file

@ -6,9 +6,6 @@ import (
"os/exec"
"strings"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/go-errors/errors"
"github.com/christophe-duc/lazypodman/pkg/i18n"
"github.com/christophe-duc/lazypodman/pkg/utils"
@ -17,7 +14,7 @@ import (
"golang.org/x/xerrors"
)
// Container : A docker Container
// Container represents a Podman container
type Container struct {
Name string
ServiceName string
@ -27,24 +24,26 @@ type Container struct {
OneOff bool
ProjectName string
ID string
Container container.Summary
Client *client.Client
Summary ContainerSummary
Runtime ContainerRuntime
OSCommand *OSCommand
Log *logrus.Entry
StatHistory []*RecordedStats
Details container.InspectResponse
Details *ContainerDetails
MonitoringStats bool
DockerCommand LimitedDockerCommand
PodmanCommand LimitedPodmanCommand
Tr *i18n.TranslationSet
StatsMutex deadlock.Mutex
}
// Remove removes the container
func (c *Container) Remove(options container.RemoveOptions) error {
func (c *Container) Remove(force bool, removeVolumes bool) error {
c.Log.Warn(fmt.Sprintf("removing container %s", c.Name))
if err := c.Client.ContainerRemove(context.Background(), c.ID, options); err != nil {
if strings.Contains(err.Error(), "Stop the container before attempting removal or force remove") {
ctx := context.Background()
if err := c.Runtime.RemoveContainer(ctx, c.ID, force, removeVolumes); err != nil {
if strings.Contains(err.Error(), "Stop the container before attempting removal or force remove") ||
strings.Contains(err.Error(), "container is running") {
return ComplexError{
Code: MustStopContainer,
Message: err.Error(),
@ -60,25 +59,29 @@ func (c *Container) Remove(options container.RemoveOptions) error {
// Stop stops the container
func (c *Container) Stop() error {
c.Log.Warn(fmt.Sprintf("stopping container %s", c.Name))
return c.Client.ContainerStop(context.Background(), c.ID, container.StopOptions{})
ctx := context.Background()
return c.Runtime.StopContainer(ctx, c.ID, nil)
}
// Pause pauses the container
func (c *Container) Pause() error {
c.Log.Warn(fmt.Sprintf("pausing container %s", c.Name))
return c.Client.ContainerPause(context.Background(), c.ID)
ctx := context.Background()
return c.Runtime.PauseContainer(ctx, c.ID)
}
// Unpause unpauses the container
func (c *Container) Unpause() error {
c.Log.Warn(fmt.Sprintf("unpausing container %s", c.Name))
return c.Client.ContainerUnpause(context.Background(), c.ID)
ctx := context.Background()
return c.Runtime.UnpauseContainer(ctx, c.ID)
}
// Restart restarts the container
func (c *Container) Restart() error {
c.Log.Warn(fmt.Sprintf("restarting container %s", c.Name))
return c.Client.ContainerRestart(context.Background(), c.ID, container.StopOptions{})
ctx := context.Background()
return c.Runtime.RestartContainer(ctx, c.ID, nil)
}
// Attach attaches the container
@ -88,44 +91,47 @@ func (c *Container) Attach() (*exec.Cmd, error) {
}
// verify that we can in fact attach to this container
if !c.Details.Config.OpenStdin {
if c.Details.Config != nil && !c.Details.Config.OpenStdin {
return nil, errors.New(c.Tr.UnattachableContainerError)
}
if c.Container.State == "exited" {
if c.Summary.State == "exited" {
return nil, errors.New(c.Tr.CannotAttachStoppedContainerError)
}
c.Log.Warn(fmt.Sprintf("attaching to container %s", c.Name))
// TODO: use SDK
cmd := c.OSCommand.NewCmd("docker", "attach", "--sig-proxy=false", c.ID)
// Use podman attach command
cmd := c.OSCommand.NewCmd("podman", "attach", "--sig-proxy=false", c.ID)
return cmd, nil
}
// Top returns process information
func (c *Container) Top(ctx context.Context) (container.TopResponse, error) {
detail, err := c.Inspect()
func (c *Container) Top(ctx context.Context) (TopResponse, error) {
details, err := c.Inspect()
if err != nil {
return container.TopResponse{}, err
return TopResponse{}, err
}
// check container status
if !detail.State.Running {
return container.TopResponse{}, errors.New("container is not running")
if details.State == nil || !details.State.Running {
return TopResponse{}, errors.New("container is not running")
}
return c.Client.ContainerTop(ctx, c.ID, []string{})
}
titles, processes, err := c.Runtime.ContainerTop(ctx, c.ID)
if err != nil {
return TopResponse{}, err
}
// PruneContainers prunes containers
func (c *DockerCommand) PruneContainers() error {
_, err := c.Client.ContainersPrune(context.Background(), filters.Args{})
return err
return TopResponse{
Titles: titles,
Processes: processes,
}, nil
}
// Inspect returns details about the container
func (c *Container) Inspect() (container.InspectResponse, error) {
return c.Client.ContainerInspect(context.Background(), c.ID)
func (c *Container) Inspect() (*ContainerDetails, error) {
ctx := context.Background()
return c.Runtime.InspectContainer(ctx, c.ID)
}
// RenderTop returns details about the container
@ -142,5 +148,10 @@ func (c *Container) RenderTop(ctx context.Context) (string, error) {
// Sometimes it takes some time for a container to have its details loaded
// after it starts.
func (c *Container) DetailsLoaded() bool {
return c.Details.ContainerJSONBase != nil
return c.Details != nil
}
// GetState returns the container state from the Summary
func (c *Container) GetState() string {
return c.Summary.State
}

View file

@ -1,425 +0,0 @@
package commands
import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
ogLog "log"
"os"
"os/exec"
"strings"
"sync"
"time"
cliconfig "github.com/docker/cli/cli/config"
ddocker "github.com/docker/cli/cli/context/docker"
ctxstore "github.com/docker/cli/cli/context/store"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/imdario/mergo"
"github.com/christophe-duc/lazypodman/pkg/commands/ssh"
"github.com/christophe-duc/lazypodman/pkg/config"
"github.com/christophe-duc/lazypodman/pkg/i18n"
"github.com/christophe-duc/lazypodman/pkg/utils"
"github.com/sasha-s/go-deadlock"
"github.com/sirupsen/logrus"
)
const (
dockerHostEnvKey = "DOCKER_HOST"
)
// DockerCommand is our main docker interface
type DockerCommand struct {
Log *logrus.Entry
OSCommand *OSCommand
Tr *i18n.TranslationSet
Config *config.AppConfig
Client *client.Client
InDockerComposeProject bool
ErrorChan chan error
ContainerMutex deadlock.Mutex
ServiceMutex deadlock.Mutex
Closers []io.Closer
}
var _ io.Closer = &DockerCommand{}
// LimitedDockerCommand is a stripped-down DockerCommand with just the methods the container/service/image might need
type LimitedDockerCommand interface {
NewCommandObject(CommandObject) CommandObject
}
// CommandObject is what we pass to our template resolvers when we are running a custom command. We do not guarantee that all fields will be populated: just the ones that make sense for the current context
type CommandObject struct {
DockerCompose string
Service *Service
Container *Container
Image *Image
Volume *Volume
Network *Network
}
// NewCommandObject takes a command object and returns a default command object with the passed command object merged in
func (c *DockerCommand) NewCommandObject(obj CommandObject) CommandObject {
defaultObj := CommandObject{DockerCompose: c.Config.UserConfig.CommandTemplates.DockerCompose}
_ = mergo.Merge(&defaultObj, obj)
return defaultObj
}
// NewDockerCommand it runs docker commands
func NewDockerCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*DockerCommand, error) {
dockerHost, err := determineDockerHost()
if err != nil {
ogLog.Printf("> could not determine host %v", err)
}
// NOTE: Inject the determined docker host to the environment. This allows the
// `SSHHandler.HandleSSHDockerHost()` to create a local unix socket tunneled
// over SSH to the specified ssh host.
if strings.HasPrefix(dockerHost, "ssh://") {
os.Setenv(dockerHostEnvKey, dockerHost)
}
tunnelCloser, err := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost()
if err != nil {
ogLog.Fatal(err)
}
// Retrieve the docker host from the environment which could have been set by
// the `SSHHandler.HandleSSHDockerHost()` and override `dockerHost`.
dockerHostFromEnv := os.Getenv(dockerHostEnvKey)
if dockerHostFromEnv != "" {
dockerHost = dockerHostFromEnv
}
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation(), client.WithHost(dockerHost))
if err != nil {
ogLog.Fatal(err)
}
dockerCommand := &DockerCommand{
Log: log,
OSCommand: osCommand,
Tr: tr,
Config: config,
Client: cli,
ErrorChan: errorChan,
InDockerComposeProject: true,
Closers: []io.Closer{tunnelCloser},
}
dockerCommand.setDockerComposeCommand(config)
err = osCommand.RunCommand(
utils.ApplyTemplate(
config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
dockerCommand.NewCommandObject(CommandObject{}),
),
)
if err != nil {
dockerCommand.InDockerComposeProject = false
log.Warn(err.Error())
}
return dockerCommand, nil
}
func (c *DockerCommand) setDockerComposeCommand(config *config.AppConfig) {
if config.UserConfig.CommandTemplates.DockerCompose != "docker compose" {
return
}
// it's possible that a user is still using docker-compose, so we'll check if 'docker comopose' is available, and if not, we'll fall back to 'docker-compose'
err := c.OSCommand.RunCommand("docker compose version")
if err != nil {
config.UserConfig.CommandTemplates.DockerCompose = "docker-compose"
}
}
func (c *DockerCommand) Close() error {
return utils.CloseMany(c.Closers)
}
func (c *DockerCommand) CreateClientStatMonitor(container *Container) {
container.MonitoringStats = true
stream, err := c.Client.ContainerStats(context.Background(), container.ID, true)
if err != nil {
// not creating error panel because if we've disconnected from docker we'll
// have already created an error panel
c.Log.Error(err)
container.MonitoringStats = false
return
}
defer stream.Body.Close()
scanner := bufio.NewScanner(stream.Body)
for scanner.Scan() {
data := scanner.Bytes()
var stats ContainerStats
_ = json.Unmarshal(data, &stats)
recordedStats := &RecordedStats{
ClientStats: stats,
DerivedStats: DerivedStats{
CPUPercentage: stats.CalculateContainerCPUPercentage(),
MemoryPercentage: stats.CalculateContainerMemoryUsage(),
},
RecordedAt: time.Now(),
}
container.appendStats(recordedStats, c.Config.UserConfig.Stats.MaxDuration)
}
container.MonitoringStats = false
}
func (c *DockerCommand) RefreshContainersAndServices(currentServices []*Service, currentContainers []*Container) ([]*Container, []*Service, error) {
c.ServiceMutex.Lock()
defer c.ServiceMutex.Unlock()
containers, err := c.GetContainers(currentContainers)
if err != nil {
return nil, nil, err
}
var services []*Service
// we only need to get these services once because they won't change in the runtime of the program
if currentServices != nil {
services = currentServices
} else {
services, err = c.GetServices()
if err != nil {
return nil, nil, err
}
}
c.assignContainersToServices(containers, services)
return containers, services, nil
}
func (c *DockerCommand) assignContainersToServices(containers []*Container, services []*Service) {
L:
for _, service := range services {
for _, ctr := range containers {
if !ctr.OneOff && ctr.ServiceName == service.Name {
service.Container = ctr
continue L
}
}
service.Container = nil
}
}
// GetContainers gets the docker containers
func (c *DockerCommand) GetContainers(existingContainers []*Container) ([]*Container, error) {
c.ContainerMutex.Lock()
defer c.ContainerMutex.Unlock()
containers, err := c.Client.ContainerList(context.Background(), container.ListOptions{All: true})
if err != nil {
return nil, err
}
ownContainers := make([]*Container, len(containers))
for i, ctr := range containers {
var newContainer *Container
// check if we already have data stored against the container
for _, existingContainer := range existingContainers {
if existingContainer.ID == ctr.ID {
newContainer = existingContainer
break
}
}
// initialise the container if it's completely new
if newContainer == nil {
newContainer = &Container{
ID: ctr.ID,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
DockerCommand: c,
Tr: c.Tr,
}
}
newContainer.Container = ctr
// if the container is made with a name label we will use that
if name, ok := ctr.Labels["name"]; ok {
newContainer.Name = name
} else {
if len(ctr.Names) > 0 {
newContainer.Name = strings.TrimLeft(ctr.Names[0], "/")
} else {
newContainer.Name = ctr.ID
}
}
newContainer.ServiceName = ctr.Labels["com.docker.compose.service"]
newContainer.ProjectName = ctr.Labels["com.docker.compose.project"]
newContainer.ContainerNumber = ctr.Labels["com.docker.compose.container"]
newContainer.OneOff = ctr.Labels["com.docker.compose.oneoff"] == "True"
ownContainers[i] = newContainer
}
c.SetContainerDetails(ownContainers)
return ownContainers, nil
}
// GetServices gets services
func (c *DockerCommand) GetServices() ([]*Service, error) {
if !c.InDockerComposeProject {
return nil, nil
}
composeCommand := c.Config.UserConfig.CommandTemplates.DockerCompose
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s config --services", composeCommand))
if err != nil {
return nil, err
}
// output looks like:
// service1
// service2
lines := utils.SplitLines(output)
services := make([]*Service, len(lines))
for i, str := range lines {
services[i] = &Service{
Name: str,
ID: str,
OSCommand: c.OSCommand,
Log: c.Log,
DockerCommand: c,
}
}
return services, nil
}
func (c *DockerCommand) RefreshContainerDetails(containers []*Container) error {
c.ContainerMutex.Lock()
defer c.ContainerMutex.Unlock()
c.SetContainerDetails(containers)
return nil
}
// Attaches the details returned from docker inspect to each of the containers
// this contains a bit more info than what you get from the go-docker client
func (c *DockerCommand) SetContainerDetails(containers []*Container) {
wg := sync.WaitGroup{}
for _, ctr := range containers {
ctr := ctr
wg.Add(1)
go func() {
details, err := c.Client.ContainerInspect(context.Background(), ctr.ID)
if err != nil {
c.Log.Error(err)
} else {
ctr.Details = details
}
wg.Done()
}()
}
wg.Wait()
}
// ViewAllLogs attaches to a subprocess viewing all the logs from docker-compose
func (c *DockerCommand) ViewAllLogs() (*exec.Cmd, error) {
cmd := c.OSCommand.ExecutableFromString(
utils.ApplyTemplate(
c.OSCommand.Config.UserConfig.CommandTemplates.ViewAllLogs,
c.NewCommandObject(CommandObject{}),
),
)
c.OSCommand.PrepareForChildren(cmd)
return cmd, nil
}
// DockerComposeConfig returns the result of 'docker-compose config'
func (c *DockerCommand) DockerComposeConfig() string {
output, err := c.OSCommand.RunCommandWithOutput(
utils.ApplyTemplate(
c.OSCommand.Config.UserConfig.CommandTemplates.DockerComposeConfig,
c.NewCommandObject(CommandObject{}),
),
)
if err != nil {
output = err.Error()
}
return output
}
// determineDockerHost tries to the determine the docker host that we should connect to
// in the following order of decreasing precedence:
// - value of "DOCKER_HOST" environment variable
// - host retrieved from the current context (specified via DOCKER_CONTEXT)
// - "default docker host" for the host operating system, otherwise
func determineDockerHost() (string, error) {
// If the docker host is explicitly set via the "DOCKER_HOST" environment variable,
// then its a no-brainer :shrug:
if os.Getenv("DOCKER_HOST") != "" {
return os.Getenv("DOCKER_HOST"), nil
}
currentContext := os.Getenv("DOCKER_CONTEXT")
if currentContext == "" {
cf, err := cliconfig.Load(cliconfig.Dir())
if err != nil {
return "", err
}
currentContext = cf.CurrentContext
}
// On some systems (windows) `default` is stored in the docker config as the currentContext.
if currentContext == "" || currentContext == "default" {
// If a docker context is neither specified via the "DOCKER_CONTEXT" environment variable nor via the
// $HOME/.docker/config file, then we fall back to connecting to the "default docker host" meant for
// the host operating system.
return defaultDockerHost, nil
}
storeConfig := ctxstore.NewConfig(
func() interface{} { return &ddocker.EndpointMeta{} },
ctxstore.EndpointTypeGetter(ddocker.DockerEndpoint, func() interface{} { return &ddocker.EndpointMeta{} }),
)
st := ctxstore.New(cliconfig.ContextStoreDir(), storeConfig)
md, err := st.GetMetadata(currentContext)
if err != nil {
return "", err
}
dockerEP, ok := md.Endpoints[ddocker.DockerEndpoint]
if !ok {
return "", err
}
dockerEPMeta, ok := dockerEP.(ddocker.EndpointMeta)
if !ok {
return "", fmt.Errorf("expected docker.EndpointMeta, got %T", dockerEP)
}
if dockerEPMeta.Host != "" {
return dockerEPMeta.Host, nil
}
// We might end up here, if the context was created with the `host` set to an empty value (i.e. '').
// For example:
// ```sh
// docker context create foo --docker "host="
// ```
// In such scenario, we mimic the `docker` cli and try to connect to the "default docker host".
return defaultDockerHost, nil
}

View file

@ -1,7 +0,0 @@
//go:build !windows
package commands
const (
defaultDockerHost = "unix:///var/run/docker.sock"
)

View file

@ -1,5 +0,0 @@
package commands
const (
defaultDockerHost = "npipe:////./pipe/docker_engine"
)

View file

@ -35,15 +35,15 @@ func NewDummyLog() *logrus.Entry {
return log.WithField("test", "test")
}
// NewDummyDockerCommand creates a new dummy DockerCommand for testing
func NewDummyDockerCommand() *DockerCommand {
return NewDummyDockerCommandWithOSCommand(NewDummyOSCommand())
// NewDummyPodmanCommand creates a new dummy PodmanCommand for testing
func NewDummyPodmanCommand() *PodmanCommand {
return NewDummyPodmanCommandWithOSCommand(NewDummyOSCommand())
}
// NewDummyDockerCommandWithOSCommand creates a new dummy DockerCommand for testing
func NewDummyDockerCommandWithOSCommand(osCommand *OSCommand) *DockerCommand {
// NewDummyPodmanCommandWithOSCommand creates a new dummy PodmanCommand for testing
func NewDummyPodmanCommandWithOSCommand(osCommand *OSCommand) *PodmanCommand {
newAppConfig := NewDummyAppConfig()
return &DockerCommand{
return &PodmanCommand{
Log: NewDummyLog(),
OSCommand: osCommand,
Tr: i18n.NewTranslationSet(NewDummyLog(), newAppConfig.UserConfig.Gui.Language),

View file

@ -2,6 +2,7 @@ package commands
import (
"fmt"
"strings"
"github.com/go-errors/errors"
"golang.org/x/xerrors"
@ -12,6 +13,21 @@ const (
MustStopContainer = iota
)
// IsConnectionFailed checks if the error indicates a connection failure to Podman
func IsConnectionFailed(err error) bool {
if err == nil {
return false
}
errStr := err.Error()
return strings.Contains(errStr, "connection refused") ||
strings.Contains(errStr, "no such host") ||
strings.Contains(errStr, "socket not found") ||
strings.Contains(errStr, "cannot connect") ||
strings.Contains(errStr, "unable to connect") ||
strings.Contains(errStr, "dial unix") ||
strings.Contains(errStr, "permission denied")
}
// WrapError wraps an error for the sake of showing a stack trace at the top level
// the go-errors package, for some reason, does not return nil when you try to wrap
// a non-error, so we're just doing it here

View file

@ -4,37 +4,31 @@ import (
"context"
"strings"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
"github.com/fatih/color"
"github.com/christophe-duc/lazypodman/pkg/utils"
"github.com/samber/lo"
"github.com/sirupsen/logrus"
)
// Image : A docker Image
// Image represents a Podman image
type Image struct {
Name string
Tag string
ID string
Image image.Summary
Client *client.Client
Summary ImageSummary
Runtime ContainerRuntime
OSCommand *OSCommand
Log *logrus.Entry
DockerCommand LimitedDockerCommand
PodmanCommand LimitedPodmanCommand
}
// Remove removes the image
func (i *Image) Remove(options image.RemoveOptions) error {
if _, err := i.Client.ImageRemove(context.Background(), i.ID, options); err != nil {
return err
}
return nil
func (i *Image) Remove(force bool) error {
ctx := context.Background()
return i.Runtime.RemoveImage(ctx, i.ID, force)
}
func getHistoryResponseItemDisplayStrings(layer image.HistoryResponseItem) []string {
func getHistoryResponseItemDisplayStrings(layer ImageHistoryEntry) []string {
tag := ""
if len(layer.Tags) > 0 {
tag = layer.Tags[0]
@ -75,12 +69,13 @@ func getHistoryResponseItemDisplayStrings(layer image.HistoryResponseItem) []str
// RenderHistory renders the history of the image
func (i *Image) RenderHistory() (string, error) {
history, err := i.Client.ImageHistory(context.Background(), i.ID)
ctx := context.Background()
history, err := i.Runtime.ImageHistory(ctx, i.ID)
if err != nil {
return "", err
}
tableBody := lo.Map(history, func(layer image.HistoryResponseItem, _ int) []string {
tableBody := lo.Map(history, func(layer ImageHistoryEntry, _ int) []string {
return getHistoryResponseItemDisplayStrings(layer)
})
@ -89,55 +84,3 @@ func (i *Image) RenderHistory() (string, error) {
return utils.RenderTable(table)
}
// RefreshImages returns a slice of docker images
func (c *DockerCommand) RefreshImages() ([]*Image, error) {
images, err := c.Client.ImageList(context.Background(), image.ListOptions{})
if err != nil {
return nil, err
}
ownImages := make([]*Image, len(images))
for i, img := range images {
firstTag := ""
tags := img.RepoTags
if len(tags) > 0 {
firstTag = tags[0]
}
nameParts := strings.Split(firstTag, ":")
tag := ""
name := "none"
if len(nameParts) > 1 {
tag = nameParts[len(nameParts)-1]
name = strings.Join(nameParts[:len(nameParts)-1], ":")
for prefix, replacement := range c.Config.UserConfig.Replacements.ImageNamePrefixes {
if strings.HasPrefix(name, prefix) {
name = strings.Replace(name, prefix, replacement, 1)
break
}
}
}
ownImages[i] = &Image{
ID: img.ID,
Name: name,
Tag: tag,
Image: img,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
DockerCommand: c,
}
}
return ownImages, nil
}
// PruneImages prunes images
func (c *DockerCommand) PruneImages() error {
_, err := c.Client.ImagesPrune(context.Background(), filters.Args{})
return err
}

View file

@ -3,52 +3,21 @@ package commands
import (
"context"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/sirupsen/logrus"
)
// Network : A docker Network
// Network represents a Podman network
type Network struct {
Name string
Network network.Inspect
Client *client.Client
Summary NetworkSummary
Runtime ContainerRuntime
OSCommand *OSCommand
Log *logrus.Entry
DockerCommand LimitedDockerCommand
}
// RefreshNetworks gets the networks and stores them
func (c *DockerCommand) RefreshNetworks() ([]*Network, error) {
networks, err := c.Client.NetworkList(context.Background(), network.ListOptions{})
if err != nil {
return nil, err
}
ownNetworks := make([]*Network, len(networks))
for i, nw := range networks {
ownNetworks[i] = &Network{
Name: nw.Name,
Network: nw,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
DockerCommand: c,
}
}
return ownNetworks, nil
}
// PruneNetworks prunes networks
func (c *DockerCommand) PruneNetworks() error {
_, err := c.Client.NetworksPrune(context.Background(), filters.Args{})
return err
PodmanCommand LimitedPodmanCommand
}
// Remove removes the network
func (v *Network) Remove() error {
return v.Client.NetworkRemove(context.Background(), v.Name)
ctx := context.Background()
return v.Runtime.RemoveNetwork(ctx, v.Name)
}

650
pkg/commands/podman.go Normal file
View file

@ -0,0 +1,650 @@
package commands
import (
"context"
"fmt"
"io"
ogLog "log"
"os"
"os/exec"
"strings"
"sync"
"time"
"github.com/imdario/mergo"
"github.com/christophe-duc/lazypodman/pkg/commands/ssh"
"github.com/christophe-duc/lazypodman/pkg/config"
"github.com/christophe-duc/lazypodman/pkg/i18n"
"github.com/christophe-duc/lazypodman/pkg/utils"
"github.com/sasha-s/go-deadlock"
"github.com/sirupsen/logrus"
)
const (
containerHostEnvKey = "CONTAINER_HOST"
)
// PodmanCommand is our main podman interface
type PodmanCommand struct {
Log *logrus.Entry
OSCommand *OSCommand
Tr *i18n.TranslationSet
Config *config.AppConfig
Runtime ContainerRuntime
InDockerComposeProject bool
ErrorChan chan error
ContainerMutex deadlock.Mutex
ServiceMutex deadlock.Mutex
Closers []io.Closer
}
var _ io.Closer = &PodmanCommand{}
// LimitedPodmanCommand is a stripped-down PodmanCommand with just the methods the container/service/image might need
type LimitedPodmanCommand interface {
NewCommandObject(CommandObject) CommandObject
}
// CommandObject is what we pass to our template resolvers when we are running a custom command.
// We do not guarantee that all fields will be populated: just the ones that make sense for the current context
type CommandObject struct {
DockerCompose string
Service *Service
Container *Container
Image *Image
Volume *Volume
Network *Network
}
// NewCommandObject takes a command object and returns a default command object with the passed command object merged in
func (c *PodmanCommand) NewCommandObject(obj CommandObject) CommandObject {
defaultObj := CommandObject{DockerCompose: c.Config.UserConfig.CommandTemplates.DockerCompose}
_ = mergo.Merge(&defaultObj, obj)
return defaultObj
}
// NewPodmanCommand creates a new PodmanCommand with the appropriate runtime.
// It tries socket mode first, then falls back to libpod if available.
func NewPodmanCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*PodmanCommand, error) {
var runtime ContainerRuntime
var err error
var closers []io.Closer
// Determine the socket path
socketPath := detectSocketPath()
// Handle SSH tunneling for remote hosts
if strings.HasPrefix(socketPath, "ssh://") {
os.Setenv(containerHostEnvKey, socketPath)
tunnelCloser, tunnelErr := ssh.NewSSHHandler(osCommand).HandleSSHDockerHost()
if tunnelErr != nil {
ogLog.Printf("> SSH tunnel setup failed: %v", tunnelErr)
} else {
closers = append(closers, tunnelCloser)
}
// Re-check for updated socket path after SSH setup
if updatedPath := os.Getenv(containerHostEnvKey); updatedPath != "" {
socketPath = updatedPath
}
}
// Try socket mode first
if socketPath != "" {
runtime, err = NewSocketRuntime(socketPath)
if err != nil {
log.Warnf("Socket connection failed (%s): %v, trying libpod", socketPath, err)
}
}
// Fall back to libpod if socket mode failed
if runtime == nil {
runtime, err = NewLibpodRuntime()
if err != nil {
return nil, fmt.Errorf("no Podman runtime available: socket error and libpod unavailable: %v", err)
}
log.Infof("Using libpod runtime (socket-less mode)")
} else {
log.Infof("Using socket runtime at %s", socketPath)
}
podmanCommand := &PodmanCommand{
Log: log,
OSCommand: osCommand,
Tr: tr,
Config: config,
Runtime: runtime,
ErrorChan: errorChan,
InDockerComposeProject: true,
Closers: closers,
}
podmanCommand.setComposeCommand(config)
err = osCommand.RunCommand(
utils.ApplyTemplate(
config.UserConfig.CommandTemplates.CheckDockerComposeConfig,
podmanCommand.NewCommandObject(CommandObject{}),
),
)
if err != nil {
podmanCommand.InDockerComposeProject = false
log.Warn(err.Error())
}
return podmanCommand, nil
}
// setComposeCommand detects and sets the appropriate compose command
func (c *PodmanCommand) setComposeCommand(config *config.AppConfig) {
// If user has explicitly set a compose command, respect it
if config.UserConfig.CommandTemplates.DockerCompose != "docker compose" &&
config.UserConfig.CommandTemplates.DockerCompose != "" {
return
}
// Try podman-compose first
if err := c.OSCommand.RunCommand("podman-compose version"); err == nil {
config.UserConfig.CommandTemplates.DockerCompose = "podman-compose"
return
}
// Try podman compose (built-in, if available)
if err := c.OSCommand.RunCommand("podman compose version"); err == nil {
config.UserConfig.CommandTemplates.DockerCompose = "podman compose"
return
}
// Fall back to docker-compose for compatibility
if err := c.OSCommand.RunCommand("docker-compose version"); err == nil {
config.UserConfig.CommandTemplates.DockerCompose = "docker-compose"
return
}
// Default to podman-compose
config.UserConfig.CommandTemplates.DockerCompose = "podman-compose"
}
func (c *PodmanCommand) Close() error {
var errs []error
if c.Runtime != nil {
if err := c.Runtime.Close(); err != nil {
errs = append(errs, err)
}
}
if err := utils.CloseMany(c.Closers); err != nil {
errs = append(errs, err)
}
if len(errs) > 0 {
return errs[0]
}
return nil
}
// CreateClientStatMonitor starts monitoring stats for a container
func (c *PodmanCommand) CreateClientStatMonitor(container *Container) {
container.MonitoringStats = true
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
statsChan, errChan := c.Runtime.ContainerStats(ctx, container.ID, true)
go func() {
for err := range errChan {
if err != nil {
c.Log.Error(err)
}
}
}()
for stats := range statsChan {
recordedStats := &RecordedStats{
ClientStats: convertStatsEntryToContainerStats(stats),
DerivedStats: DerivedStats{
CPUPercentage: calculateCPUPercentageFromEntry(stats),
MemoryPercentage: calculateMemoryPercentageFromEntry(stats),
},
RecordedAt: time.Now(),
}
container.appendStats(recordedStats, c.Config.UserConfig.Stats.MaxDuration)
}
container.MonitoringStats = false
}
// convertStatsEntryToContainerStats converts the new stats format to the legacy format
func convertStatsEntryToContainerStats(entry ContainerStatsEntry) ContainerStats {
return ContainerStats{
Read: entry.Read,
Preread: entry.PreRead,
PidsStats: struct {
Current int `json:"current"`
}{
Current: entry.PidsStats.Current,
},
CPUStats: struct {
CPUUsage struct {
TotalUsage int64 `json:"total_usage"`
PercpuUsage []int64 `json:"percpu_usage"`
UsageInKernelmode int64 `json:"usage_in_kernelmode"`
UsageInUsermode int64 `json:"usage_in_usermode"`
} `json:"cpu_usage"`
SystemCPUUsage int64 `json:"system_cpu_usage"`
OnlineCpus int `json:"online_cpus"`
ThrottlingData struct {
Periods int `json:"periods"`
ThrottledPeriods int `json:"throttled_periods"`
ThrottledTime int `json:"throttled_time"`
} `json:"throttling_data"`
}{
CPUUsage: struct {
TotalUsage int64 `json:"total_usage"`
PercpuUsage []int64 `json:"percpu_usage"`
UsageInKernelmode int64 `json:"usage_in_kernelmode"`
UsageInUsermode int64 `json:"usage_in_usermode"`
}{
TotalUsage: entry.CPUStats.CPUUsage.TotalUsage,
PercpuUsage: entry.CPUStats.CPUUsage.PercpuUsage,
UsageInKernelmode: entry.CPUStats.CPUUsage.UsageInKernelmode,
UsageInUsermode: entry.CPUStats.CPUUsage.UsageInUsermode,
},
SystemCPUUsage: entry.CPUStats.SystemCPUUsage,
OnlineCpus: entry.CPUStats.OnlineCpus,
},
PrecpuStats: struct {
CPUUsage struct {
TotalUsage int64 `json:"total_usage"`
PercpuUsage []int64 `json:"percpu_usage"`
UsageInKernelmode int64 `json:"usage_in_kernelmode"`
UsageInUsermode int64 `json:"usage_in_usermode"`
} `json:"cpu_usage"`
SystemCPUUsage int64 `json:"system_cpu_usage"`
OnlineCpus int `json:"online_cpus"`
ThrottlingData struct {
Periods int `json:"periods"`
ThrottledPeriods int `json:"throttled_periods"`
ThrottledTime int `json:"throttled_time"`
} `json:"throttling_data"`
}{
CPUUsage: struct {
TotalUsage int64 `json:"total_usage"`
PercpuUsage []int64 `json:"percpu_usage"`
UsageInKernelmode int64 `json:"usage_in_kernelmode"`
UsageInUsermode int64 `json:"usage_in_usermode"`
}{
TotalUsage: entry.PreCPUStats.CPUUsage.TotalUsage,
PercpuUsage: entry.PreCPUStats.CPUUsage.PercpuUsage,
UsageInKernelmode: entry.PreCPUStats.CPUUsage.UsageInKernelmode,
UsageInUsermode: entry.PreCPUStats.CPUUsage.UsageInUsermode,
},
SystemCPUUsage: entry.PreCPUStats.SystemCPUUsage,
OnlineCpus: entry.PreCPUStats.OnlineCpus,
},
MemoryStats: struct {
Usage int `json:"usage"`
MaxUsage int `json:"max_usage"`
Stats struct {
ActiveAnon int `json:"active_anon"`
ActiveFile int `json:"active_file"`
Cache int `json:"cache"`
Dirty int `json:"dirty"`
HierarchicalMemoryLimit int64 `json:"hierarchical_memory_limit"`
HierarchicalMemswLimit int64 `json:"hierarchical_memsw_limit"`
InactiveAnon int `json:"inactive_anon"`
InactiveFile int `json:"inactive_file"`
MappedFile int `json:"mapped_file"`
Pgfault int `json:"pgfault"`
Pgmajfault int `json:"pgmajfault"`
Pgpgin int `json:"pgpgin"`
Pgpgout int `json:"pgpgout"`
Rss int `json:"rss"`
RssHuge int `json:"rss_huge"`
TotalActiveAnon int `json:"total_active_anon"`
TotalActiveFile int `json:"total_active_file"`
TotalCache int `json:"total_cache"`
TotalDirty int `json:"total_dirty"`
TotalInactiveAnon int `json:"total_inactive_anon"`
TotalInactiveFile int `json:"total_inactive_file"`
TotalMappedFile int `json:"total_mapped_file"`
TotalPgfault int `json:"total_pgfault"`
TotalPgmajfault int `json:"total_pgmajfault"`
TotalPgpgin int `json:"total_pgpgin"`
TotalPgpgout int `json:"total_pgpgout"`
TotalRss int `json:"total_rss"`
TotalRssHuge int `json:"total_rss_huge"`
TotalUnevictable int `json:"total_unevictable"`
TotalWriteback int `json:"total_writeback"`
Unevictable int `json:"unevictable"`
Writeback int `json:"writeback"`
} `json:"stats"`
Limit int64 `json:"limit"`
}{
Usage: int(entry.MemoryStats.Usage),
MaxUsage: int(entry.MemoryStats.MaxUsage),
Limit: entry.MemoryStats.Limit,
},
Name: entry.Name,
ID: entry.ID,
}
}
func calculateCPUPercentageFromEntry(stats ContainerStatsEntry) float64 {
cpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage - stats.PreCPUStats.CPUUsage.TotalUsage)
systemDelta := float64(stats.CPUStats.SystemCPUUsage - stats.PreCPUStats.SystemCPUUsage)
if systemDelta > 0 && cpuDelta > 0 {
return (cpuDelta / systemDelta) * 100.0
}
return 0.0
}
func calculateMemoryPercentageFromEntry(stats ContainerStatsEntry) float64 {
if stats.MemoryStats.Limit > 0 {
return float64(stats.MemoryStats.Usage) / float64(stats.MemoryStats.Limit) * 100.0
}
return 0.0
}
func (c *PodmanCommand) RefreshContainersAndServices(currentServices []*Service, currentContainers []*Container) ([]*Container, []*Service, error) {
c.ServiceMutex.Lock()
defer c.ServiceMutex.Unlock()
containers, err := c.GetContainers(currentContainers)
if err != nil {
return nil, nil, err
}
var services []*Service
// we only need to get these services once because they won't change in the runtime of the program
if currentServices != nil {
services = currentServices
} else {
services, err = c.GetServices()
if err != nil {
return nil, nil, err
}
}
c.assignContainersToServices(containers, services)
return containers, services, nil
}
func (c *PodmanCommand) assignContainersToServices(containers []*Container, services []*Service) {
L:
for _, service := range services {
for _, ctr := range containers {
if !ctr.OneOff && ctr.ServiceName == service.Name {
service.Container = ctr
continue L
}
}
service.Container = nil
}
}
// GetContainers gets the podman containers
func (c *PodmanCommand) GetContainers(existingContainers []*Container) ([]*Container, error) {
c.ContainerMutex.Lock()
defer c.ContainerMutex.Unlock()
ctx := context.Background()
containers, err := c.Runtime.ListContainers(ctx)
if err != nil {
return nil, err
}
ownContainers := make([]*Container, len(containers))
for i, ctr := range containers {
var newContainer *Container
// check if we already have data stored against the container
for _, existingContainer := range existingContainers {
if existingContainer.ID == ctr.ID {
newContainer = existingContainer
break
}
}
// initialise the container if it's completely new
if newContainer == nil {
newContainer = &Container{
ID: ctr.ID,
Runtime: c.Runtime,
OSCommand: c.OSCommand,
Log: c.Log,
PodmanCommand: c,
Tr: c.Tr,
}
}
newContainer.Summary = ctr
// if the container is made with a name label we will use that
if name, ok := ctr.Labels["name"]; ok {
newContainer.Name = name
} else {
if len(ctr.Names) > 0 {
newContainer.Name = strings.TrimLeft(ctr.Names[0], "/")
} else {
newContainer.Name = ctr.ID
}
}
// Compose labels work the same way with podman-compose
newContainer.ServiceName = ctr.Labels["com.docker.compose.service"]
newContainer.ProjectName = ctr.Labels["com.docker.compose.project"]
newContainer.ContainerNumber = ctr.Labels["com.docker.compose.container"]
newContainer.OneOff = ctr.Labels["com.docker.compose.oneoff"] == "True"
ownContainers[i] = newContainer
}
c.SetContainerDetails(ownContainers)
return ownContainers, nil
}
// GetServices gets services
func (c *PodmanCommand) GetServices() ([]*Service, error) {
if !c.InDockerComposeProject {
return nil, nil
}
composeCommand := c.Config.UserConfig.CommandTemplates.DockerCompose
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s config --services", composeCommand))
if err != nil {
return nil, err
}
// output looks like:
// service1
// service2
lines := utils.SplitLines(output)
services := make([]*Service, len(lines))
for i, str := range lines {
services[i] = &Service{
Name: str,
ID: str,
OSCommand: c.OSCommand,
Log: c.Log,
PodmanCommand: c,
}
}
return services, nil
}
func (c *PodmanCommand) RefreshContainerDetails(containers []*Container) error {
c.ContainerMutex.Lock()
defer c.ContainerMutex.Unlock()
c.SetContainerDetails(containers)
return nil
}
// SetContainerDetails attaches the details returned from container inspect to each of the containers
func (c *PodmanCommand) SetContainerDetails(containers []*Container) {
ctx := context.Background()
wg := sync.WaitGroup{}
for _, ctr := range containers {
ctr := ctr
wg.Add(1)
go func() {
defer wg.Done()
details, err := c.Runtime.InspectContainer(ctx, ctr.ID)
if err != nil {
c.Log.Error(err)
} else {
ctr.Details = details
}
}()
}
wg.Wait()
}
// ViewAllLogs attaches to a subprocess viewing all the logs from compose
func (c *PodmanCommand) ViewAllLogs() (*exec.Cmd, error) {
cmd := c.OSCommand.ExecutableFromString(
utils.ApplyTemplate(
c.OSCommand.Config.UserConfig.CommandTemplates.ViewAllLogs,
c.NewCommandObject(CommandObject{}),
),
)
c.OSCommand.PrepareForChildren(cmd)
return cmd, nil
}
// DockerComposeConfig returns the result of 'compose config'
func (c *PodmanCommand) DockerComposeConfig() string {
output, err := c.OSCommand.RunCommandWithOutput(
utils.ApplyTemplate(
c.OSCommand.Config.UserConfig.CommandTemplates.DockerComposeConfig,
c.NewCommandObject(CommandObject{}),
),
)
if err != nil {
output = err.Error()
}
return output
}
// PruneContainers prunes containers using the runtime
func (c *PodmanCommand) PruneContainers() error {
return c.Runtime.PruneContainers(context.Background())
}
// PruneImages prunes images using the runtime
func (c *PodmanCommand) PruneImages() error {
return c.Runtime.PruneImages(context.Background())
}
// PruneVolumes prunes volumes using the runtime
func (c *PodmanCommand) PruneVolumes() error {
return c.Runtime.PruneVolumes(context.Background())
}
// PruneNetworks prunes networks using the runtime
func (c *PodmanCommand) PruneNetworks() error {
return c.Runtime.PruneNetworks(context.Background())
}
// RefreshImages returns a slice of podman images
func (c *PodmanCommand) RefreshImages() ([]*Image, error) {
ctx := context.Background()
images, err := c.Runtime.ListImages(ctx)
if err != nil {
return nil, err
}
ownImages := make([]*Image, len(images))
for i, img := range images {
firstTag := ""
tags := img.RepoTags
if len(tags) > 0 {
firstTag = tags[0]
}
nameParts := strings.Split(firstTag, ":")
tag := ""
name := "none"
if len(nameParts) > 1 {
tag = nameParts[len(nameParts)-1]
name = strings.Join(nameParts[:len(nameParts)-1], ":")
for prefix, replacement := range c.Config.UserConfig.Replacements.ImageNamePrefixes {
if strings.HasPrefix(name, prefix) {
name = strings.Replace(name, prefix, replacement, 1)
break
}
}
}
ownImages[i] = &Image{
ID: img.ID,
Name: name,
Tag: tag,
Summary: img,
Runtime: c.Runtime,
OSCommand: c.OSCommand,
Log: c.Log,
PodmanCommand: c,
}
}
return ownImages, nil
}
// RefreshVolumes gets the volumes and stores them
func (c *PodmanCommand) RefreshVolumes() ([]*Volume, error) {
ctx := context.Background()
volumes, err := c.Runtime.ListVolumes(ctx)
if err != nil {
return nil, err
}
ownVolumes := make([]*Volume, len(volumes))
for i, vol := range volumes {
ownVolumes[i] = &Volume{
Name: vol.Name,
Summary: vol,
Runtime: c.Runtime,
OSCommand: c.OSCommand,
Log: c.Log,
PodmanCommand: c,
}
}
return ownVolumes, nil
}
// RefreshNetworks gets the networks and stores them
func (c *PodmanCommand) RefreshNetworks() ([]*Network, error) {
ctx := context.Background()
networks, err := c.Runtime.ListNetworks(ctx)
if err != nil {
return nil, err
}
ownNetworks := make([]*Network, len(networks))
for i, nw := range networks {
ownNetworks[i] = &Network{
Name: nw.Name,
Summary: nw,
Runtime: c.Runtime,
OSCommand: c.OSCommand,
Log: c.Log,
PodmanCommand: c,
}
}
return ownNetworks, nil
}

View file

@ -0,0 +1,48 @@
//go:build !windows
package commands
import (
"fmt"
"os"
)
// detectSocketPath determines the Podman socket path to use.
// It checks in the following order:
// 1. CONTAINER_HOST environment variable (Podman standard)
// 2. Rootless socket: /run/user/{uid}/podman/podman.sock
// 3. Rootful socket: /run/podman/podman.sock
// 4. Legacy Docker socket: /var/run/docker.sock (for compatibility)
func detectSocketPath() string {
// 1. Check CONTAINER_HOST environment variable (Podman standard)
if host := os.Getenv("CONTAINER_HOST"); host != "" {
return host
}
// Also check DOCKER_HOST for backward compatibility
if host := os.Getenv("DOCKER_HOST"); host != "" {
return host
}
// 2. Check for rootless socket
uid := os.Getuid()
rootlessPath := fmt.Sprintf("/run/user/%d/podman/podman.sock", uid)
if _, err := os.Stat(rootlessPath); err == nil {
return "unix://" + rootlessPath
}
// 3. Check for rootful socket
rootfulPath := "/run/podman/podman.sock"
if _, err := os.Stat(rootfulPath); err == nil {
return "unix://" + rootfulPath
}
// 4. Legacy Docker socket path (for compatibility)
dockerPath := "/var/run/docker.sock"
if _, err := os.Stat(dockerPath); err == nil {
return "unix://" + dockerPath
}
// Return empty string - will fall back to libpod if available
return ""
}

View file

@ -0,0 +1,25 @@
//go:build windows
package commands
import "os"
// detectSocketPath determines the Podman socket path to use on Windows.
// It checks in the following order:
// 1. CONTAINER_HOST environment variable (Podman standard)
// 2. Default Podman machine named pipe
func detectSocketPath() string {
// Check CONTAINER_HOST environment variable
if host := os.Getenv("CONTAINER_HOST"); host != "" {
return host
}
// Also check DOCKER_HOST for backward compatibility
if host := os.Getenv("DOCKER_HOST"); host != "" {
return host
}
// Default to Podman machine socket
// Podman machine on Windows uses named pipes
return "npipe:////./pipe/podman-machine-default"
}

48
pkg/commands/runtime.go Normal file
View file

@ -0,0 +1,48 @@
package commands
import "context"
// ContainerRuntime abstracts Podman operations for both socket and socket-less modes.
// This interface allows the application to work with either:
// - Socket mode: using pkg/bindings (stable REST API)
// - Socket-less mode: using libpod directly (requires Linux + CGO)
type ContainerRuntime interface {
// Container operations
ListContainers(ctx context.Context) ([]ContainerSummary, error)
InspectContainer(ctx context.Context, id string) (*ContainerDetails, error)
StartContainer(ctx context.Context, id string) error
StopContainer(ctx context.Context, id string, timeout *int) error
PauseContainer(ctx context.Context, id string) error
UnpauseContainer(ctx context.Context, id string) error
RestartContainer(ctx context.Context, id string, timeout *int) error
RemoveContainer(ctx context.Context, id string, force bool, volumes bool) error
ContainerTop(ctx context.Context, id string) ([]string, [][]string, error)
PruneContainers(ctx context.Context) error
ContainerStats(ctx context.Context, id string, stream bool) (<-chan ContainerStatsEntry, <-chan error)
// Image operations
ListImages(ctx context.Context) ([]ImageSummary, error)
InspectImage(ctx context.Context, id string) (*ImageDetails, error)
ImageHistory(ctx context.Context, id string) ([]ImageHistoryEntry, error)
RemoveImage(ctx context.Context, id string, force bool) error
PruneImages(ctx context.Context) error
// Volume operations
ListVolumes(ctx context.Context) ([]VolumeSummary, error)
RemoveVolume(ctx context.Context, name string, force bool) error
PruneVolumes(ctx context.Context) error
// Network operations
ListNetworks(ctx context.Context) ([]NetworkSummary, error)
RemoveNetwork(ctx context.Context, name string) error
PruneNetworks(ctx context.Context) error
// Events streams container/image/volume/network events
Events(ctx context.Context) (<-chan Event, <-chan error)
// Lifecycle
Close() error
// Mode returns "socket" or "libpod" to indicate which runtime mode is active
Mode() string
}

View file

@ -0,0 +1,517 @@
//go:build linux && cgo
package commands
import (
"context"
"time"
"github.com/containers/podman/v5/libpod"
"github.com/containers/podman/v5/libpod/define"
)
// LibpodRuntime implements ContainerRuntime using libpod directly.
// This mode does not require a Podman socket and works directly with the container runtime.
// It requires CGO and is only available on Linux.
type LibpodRuntime struct {
runtime *libpod.Runtime
}
// NewLibpodRuntime creates a new LibpodRuntime.
func NewLibpodRuntime() (*LibpodRuntime, error) {
runtime, err := libpod.NewRuntime(context.Background())
if err != nil {
return nil, err
}
return &LibpodRuntime{runtime: runtime}, nil
}
// Mode returns "libpod" to indicate this is the direct libpod runtime.
func (r *LibpodRuntime) Mode() string {
return "libpod"
}
// Close shuts down the libpod runtime.
func (r *LibpodRuntime) Close() error {
if r.runtime != nil {
_, err := r.runtime.Shutdown(false)
return err
}
return nil
}
// Container operations
// ListContainers returns all containers.
func (r *LibpodRuntime) ListContainers(ctx context.Context) ([]ContainerSummary, error) {
ctrs, err := r.runtime.GetAllContainers()
if err != nil {
return nil, err
}
return convertLibpodContainerList(ctrs)
}
// InspectContainer returns detailed information about a container.
func (r *LibpodRuntime) InspectContainer(ctx context.Context, id string) (*ContainerDetails, error) {
ctr, err := r.runtime.LookupContainer(id)
if err != nil {
return nil, err
}
data, err := ctr.Inspect(false)
if err != nil {
return nil, err
}
return convertLibpodContainerInspect(data), nil
}
// StartContainer starts a stopped container.
func (r *LibpodRuntime) StartContainer(ctx context.Context, id string) error {
ctr, err := r.runtime.LookupContainer(id)
if err != nil {
return err
}
return ctr.Start(ctx, false)
}
// StopContainer stops a running container.
func (r *LibpodRuntime) StopContainer(ctx context.Context, id string, timeout *int) error {
ctr, err := r.runtime.LookupContainer(id)
if err != nil {
return err
}
t := uint(10) // default timeout
if timeout != nil {
t = uint(*timeout)
}
return ctr.StopWithTimeout(t)
}
// PauseContainer pauses a running container.
func (r *LibpodRuntime) PauseContainer(ctx context.Context, id string) error {
ctr, err := r.runtime.LookupContainer(id)
if err != nil {
return err
}
return ctr.Pause()
}
// UnpauseContainer unpauses a paused container.
func (r *LibpodRuntime) UnpauseContainer(ctx context.Context, id string) error {
ctr, err := r.runtime.LookupContainer(id)
if err != nil {
return err
}
return ctr.Unpause()
}
// RestartContainer restarts a container.
func (r *LibpodRuntime) RestartContainer(ctx context.Context, id string, timeout *int) error {
ctr, err := r.runtime.LookupContainer(id)
if err != nil {
return err
}
t := uint(10) // default timeout
if timeout != nil {
t = uint(*timeout)
}
return ctr.RestartWithTimeout(ctx, t)
}
// RemoveContainer removes a container.
func (r *LibpodRuntime) RemoveContainer(ctx context.Context, id string, force bool, removeVolumes bool) error {
ctr, err := r.runtime.LookupContainer(id)
if err != nil {
return err
}
var timeout uint = 10
_, _, err = r.runtime.RemoveContainerAndDependencies(ctx, ctr, force, removeVolumes, &timeout)
return err
}
// ContainerTop returns process information from a container.
func (r *LibpodRuntime) ContainerTop(ctx context.Context, id string) ([]string, [][]string, error) {
ctr, err := r.runtime.LookupContainer(id)
if err != nil {
return nil, nil, err
}
result, err := ctr.Top([]string{})
if err != nil {
return nil, nil, err
}
if len(result) == 0 {
return nil, nil, nil
}
return result[0], result[1:], nil
}
// PruneContainers removes all stopped containers.
func (r *LibpodRuntime) PruneContainers(ctx context.Context) error {
_, err := r.runtime.PruneContainers(ctx, nil)
return err
}
// ContainerStats streams container statistics.
func (r *LibpodRuntime) ContainerStats(ctx context.Context, id string, stream bool) (<-chan ContainerStatsEntry, <-chan error) {
statsChan := make(chan ContainerStatsEntry)
errChan := make(chan error, 1)
go func() {
defer close(statsChan)
defer close(errChan)
ctr, err := r.runtime.LookupContainer(id)
if err != nil {
errChan <- err
return
}
for {
stats, err := ctr.GetContainerStats(nil)
if err != nil {
errChan <- err
return
}
entry := convertLibpodContainerStats(stats)
select {
case statsChan <- entry:
case <-ctx.Done():
return
}
if !stream {
return
}
time.Sleep(time.Second)
}
}()
return statsChan, errChan
}
// Image operations
// ListImages returns all images.
func (r *LibpodRuntime) ListImages(ctx context.Context) ([]ImageSummary, error) {
imgs, err := r.runtime.ImageRuntime().GetImages()
if err != nil {
return nil, err
}
return convertLibpodImageList(imgs), nil
}
// InspectImage returns detailed information about an image.
func (r *LibpodRuntime) InspectImage(ctx context.Context, id string) (*ImageDetails, error) {
img, _, err := r.runtime.ImageRuntime().LookupImage(id, nil)
if err != nil {
return nil, err
}
return convertLibpodImageInspect(img), nil
}
// ImageHistory returns the history of an image.
func (r *LibpodRuntime) ImageHistory(ctx context.Context, id string) ([]ImageHistoryEntry, error) {
img, _, err := r.runtime.ImageRuntime().LookupImage(id, nil)
if err != nil {
return nil, err
}
history, err := img.History(ctx)
if err != nil {
return nil, err
}
return convertLibpodImageHistory(history), nil
}
// RemoveImage removes an image.
func (r *LibpodRuntime) RemoveImage(ctx context.Context, id string, force bool) error {
img, _, err := r.runtime.ImageRuntime().LookupImage(id, nil)
if err != nil {
return err
}
_, err = r.runtime.ImageRuntime().RemoveImages(ctx, []string{img.ID()}, &libimage.RemoveImagesOptions{Force: force})
return err
}
// PruneImages removes unused images.
func (r *LibpodRuntime) PruneImages(ctx context.Context) error {
_, err := r.runtime.ImageRuntime().PruneImages(ctx, nil)
return err
}
// Volume operations
// ListVolumes returns all volumes.
func (r *LibpodRuntime) ListVolumes(ctx context.Context) ([]VolumeSummary, error) {
vols, err := r.runtime.GetAllVolumes()
if err != nil {
return nil, err
}
return convertLibpodVolumeList(vols), nil
}
// RemoveVolume removes a volume.
func (r *LibpodRuntime) RemoveVolume(ctx context.Context, name string, force bool) error {
vol, err := r.runtime.LookupVolume(name)
if err != nil {
return err
}
var timeout uint = 10
return r.runtime.RemoveVolume(ctx, vol, force, &timeout)
}
// PruneVolumes removes unused volumes.
func (r *LibpodRuntime) PruneVolumes(ctx context.Context) error {
_, err := r.runtime.PruneVolumes(ctx, nil)
return err
}
// Network operations
// ListNetworks returns all networks.
func (r *LibpodRuntime) ListNetworks(ctx context.Context) ([]NetworkSummary, error) {
nets, err := r.runtime.Network().NetworkList()
if err != nil {
return nil, err
}
return convertLibpodNetworkList(nets), nil
}
// RemoveNetwork removes a network.
func (r *LibpodRuntime) RemoveNetwork(ctx context.Context, name string) error {
_, err := r.runtime.Network().NetworkRemove(name)
return err
}
// PruneNetworks removes unused networks.
func (r *LibpodRuntime) PruneNetworks(ctx context.Context) error {
_, err := r.runtime.Network().NetworkPrune(nil)
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) {
eventChan := make(chan Event)
errChan := make(chan error, 1)
go func() {
defer close(eventChan)
defer close(errChan)
// Libpod events require more complex setup; for now use periodic polling
// by sending empty events that trigger refreshes
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
// Send a synthetic event to trigger refresh
event := Event{
Type: "refresh",
Action: "poll",
Time: time.Now().Unix(),
}
select {
case eventChan <- event:
case <-ctx.Done():
return
}
}
}
}()
return eventChan, errChan
}
// Conversion functions
func convertLibpodContainerList(ctrs []*libpod.Container) ([]ContainerSummary, error) {
result := make([]ContainerSummary, len(ctrs))
for i, ctr := range ctrs {
state, err := ctr.State()
if err != nil {
return nil, err
}
config := ctr.Config()
command := ""
if len(config.Command) > 0 {
command = config.Command[0]
}
result[i] = ContainerSummary{
ID: ctr.ID(),
Names: []string{ctr.Name()},
Image: config.RootfsImageName,
ImageID: config.RootfsImageID,
Command: command,
Created: config.CreatedTime.Unix(),
State: state.String(),
Status: state.String(),
Labels: config.Labels,
Pod: ctr.PodID(),
}
}
return result, nil
}
func convertLibpodContainerInspect(data *define.InspectContainerData) *ContainerDetails {
if data == nil {
return nil
}
details := &ContainerDetails{
ID: data.ID,
Name: data.Name,
Created: data.Created,
Path: data.Path,
Args: data.Args,
Image: data.Image,
ImageID: data.ImageName,
ResolvConfPath: data.ResolvConfPath,
HostnamePath: data.HostnamePath,
HostsPath: data.HostsPath,
LogPath: data.LogPath,
RestartCount: int(data.RestartCount),
Driver: data.Driver,
MountLabel: data.MountLabel,
ProcessLabel: data.ProcessLabel,
AppArmorProfile: data.AppArmorProfile,
}
if data.State != nil {
details.State = &ContainerState{
Status: data.State.Status,
Running: data.State.Running,
Paused: data.State.Paused,
Restarting: data.State.Restarting,
OOMKilled: data.State.OOMKilled,
Dead: data.State.Dead,
Pid: data.State.Pid,
ExitCode: int(data.State.ExitCode),
Error: data.State.Error,
StartedAt: data.State.StartedAt,
FinishedAt: data.State.FinishedAt,
}
}
if data.Config != nil {
details.Config = &ContainerConfig{
Hostname: data.Config.Hostname,
Domainname: data.Config.Domainname,
User: data.Config.User,
AttachStdin: data.Config.AttachStdin,
AttachStdout: data.Config.AttachStdout,
AttachStderr: data.Config.AttachStderr,
Tty: data.Config.Tty,
OpenStdin: data.Config.OpenStdin,
StdinOnce: data.Config.StdinOnce,
Env: data.Config.Env,
Cmd: data.Config.Cmd,
Image: data.Config.Image,
WorkingDir: data.Config.WorkingDir,
Entrypoint: data.Config.Entrypoint,
OnBuild: data.Config.OnBuild,
Labels: data.Config.Labels,
StopSignal: data.Config.StopSignal,
}
}
return details
}
func convertLibpodContainerStats(stats *define.ContainerStats) ContainerStatsEntry {
return ContainerStatsEntry{
Read: time.Now(),
PreRead: time.Now().Add(-time.Second),
CPUStats: CPUStats{
CPUUsage: CPUUsage{
TotalUsage: int64(stats.CPU),
},
SystemCPUUsage: int64(stats.SystemNano),
},
MemoryStats: MemoryStats{
Usage: int64(stats.MemUsage),
Limit: int64(stats.MemLimit),
},
PidsStats: PidsStats{
Current: int(stats.PIDs),
},
Name: stats.Name,
ID: stats.ContainerID,
}
}
func convertLibpodImageList(imgs []*libimage.Image) []ImageSummary {
result := make([]ImageSummary, len(imgs))
for i, img := range imgs {
result[i] = ImageSummary{
ID: img.ID(),
RepoTags: img.Names(),
Created: img.Created().Unix(),
Size: img.Size(),
Labels: img.Labels(),
}
}
return result
}
func convertLibpodImageInspect(img *libimage.Image) *ImageDetails {
if img == nil {
return nil
}
return &ImageDetails{
ID: img.ID(),
RepoTags: img.Names(),
Created: img.Created(),
Size: img.Size(),
Architecture: img.Architecture(),
Os: img.OS(),
}
}
func convertLibpodImageHistory(history []*libimage.ImageHistoryLayer) []ImageHistoryEntry {
result := make([]ImageHistoryEntry, len(history))
for i, layer := range history {
result[i] = ImageHistoryEntry{
ID: layer.ID,
Created: layer.Created.Unix(),
CreatedBy: layer.CreatedBy,
Tags: layer.Tags,
Size: layer.Size,
Comment: layer.Comment,
}
}
return result
}
func convertLibpodVolumeList(vols []*libpod.Volume) []VolumeSummary {
result := make([]VolumeSummary, len(vols))
for i, vol := range vols {
result[i] = VolumeSummary{
Name: vol.Name(),
Driver: vol.Driver(),
Mountpoint: vol.MountPoint(),
CreatedAt: vol.CreatedTime(),
Labels: vol.Labels(),
Scope: vol.Scope(),
Options: vol.Options(),
}
}
return result
}
func convertLibpodNetworkList(nets []types.Network) []NetworkSummary {
result := make([]NetworkSummary, len(nets))
for i, nw := range nets {
result[i] = NetworkSummary{
Name: nw.Name,
ID: nw.ID,
Created: nw.Created,
Driver: nw.Driver,
Labels: nw.Labels,
}
}
return result
}

View file

@ -0,0 +1,137 @@
//go:build !linux || !cgo
package commands
import (
"context"
"errors"
)
// LibpodRuntime is a stub for non-Linux platforms.
// The real implementation requires CGO and is only available on Linux.
type LibpodRuntime struct{}
// ErrLibpodNotAvailable is returned when libpod is not available on the current platform.
var ErrLibpodNotAvailable = errors.New("libpod runtime not available on this platform (requires Linux with CGO)")
// NewLibpodRuntime returns an error on non-Linux platforms.
func NewLibpodRuntime() (*LibpodRuntime, error) {
return nil, ErrLibpodNotAvailable
}
// Mode returns "libpod".
func (r *LibpodRuntime) Mode() string {
return "libpod"
}
// Close is a no-op on non-Linux platforms.
func (r *LibpodRuntime) Close() error {
return nil
}
// Container operations - all return ErrLibpodNotAvailable
func (r *LibpodRuntime) ListContainers(ctx context.Context) ([]ContainerSummary, error) {
return nil, ErrLibpodNotAvailable
}
func (r *LibpodRuntime) InspectContainer(ctx context.Context, id string) (*ContainerDetails, error) {
return nil, ErrLibpodNotAvailable
}
func (r *LibpodRuntime) StartContainer(ctx context.Context, id string) error {
return ErrLibpodNotAvailable
}
func (r *LibpodRuntime) StopContainer(ctx context.Context, id string, timeout *int) error {
return ErrLibpodNotAvailable
}
func (r *LibpodRuntime) PauseContainer(ctx context.Context, id string) error {
return ErrLibpodNotAvailable
}
func (r *LibpodRuntime) UnpauseContainer(ctx context.Context, id string) error {
return ErrLibpodNotAvailable
}
func (r *LibpodRuntime) RestartContainer(ctx context.Context, id string, timeout *int) error {
return ErrLibpodNotAvailable
}
func (r *LibpodRuntime) RemoveContainer(ctx context.Context, id string, force bool, volumes bool) error {
return ErrLibpodNotAvailable
}
func (r *LibpodRuntime) ContainerTop(ctx context.Context, id string) ([]string, [][]string, error) {
return nil, nil, ErrLibpodNotAvailable
}
func (r *LibpodRuntime) PruneContainers(ctx context.Context) error {
return ErrLibpodNotAvailable
}
func (r *LibpodRuntime) ContainerStats(ctx context.Context, id string, stream bool) (<-chan ContainerStatsEntry, <-chan error) {
errChan := make(chan error, 1)
errChan <- ErrLibpodNotAvailable
close(errChan)
return nil, errChan
}
// Image operations - all return ErrLibpodNotAvailable
func (r *LibpodRuntime) ListImages(ctx context.Context) ([]ImageSummary, error) {
return nil, ErrLibpodNotAvailable
}
func (r *LibpodRuntime) InspectImage(ctx context.Context, id string) (*ImageDetails, error) {
return nil, ErrLibpodNotAvailable
}
func (r *LibpodRuntime) ImageHistory(ctx context.Context, id string) ([]ImageHistoryEntry, error) {
return nil, ErrLibpodNotAvailable
}
func (r *LibpodRuntime) RemoveImage(ctx context.Context, id string, force bool) error {
return ErrLibpodNotAvailable
}
func (r *LibpodRuntime) PruneImages(ctx context.Context) error {
return ErrLibpodNotAvailable
}
// Volume operations - all return ErrLibpodNotAvailable
func (r *LibpodRuntime) ListVolumes(ctx context.Context) ([]VolumeSummary, error) {
return nil, ErrLibpodNotAvailable
}
func (r *LibpodRuntime) RemoveVolume(ctx context.Context, name string, force bool) error {
return ErrLibpodNotAvailable
}
func (r *LibpodRuntime) PruneVolumes(ctx context.Context) error {
return ErrLibpodNotAvailable
}
// Network operations - all return ErrLibpodNotAvailable
func (r *LibpodRuntime) ListNetworks(ctx context.Context) ([]NetworkSummary, error) {
return nil, ErrLibpodNotAvailable
}
func (r *LibpodRuntime) RemoveNetwork(ctx context.Context, name string) error {
return ErrLibpodNotAvailable
}
func (r *LibpodRuntime) PruneNetworks(ctx context.Context) error {
return ErrLibpodNotAvailable
}
// Events returns an error channel on non-Linux platforms.
func (r *LibpodRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
errChan := make(chan error, 1)
errChan <- ErrLibpodNotAvailable
close(errChan)
return nil, errChan
}

View file

@ -0,0 +1,535 @@
package commands
import (
"context"
"encoding/json"
"time"
"github.com/containers/podman/v5/pkg/bindings"
"github.com/containers/podman/v5/pkg/bindings/containers"
"github.com/containers/podman/v5/pkg/bindings/images"
"github.com/containers/podman/v5/pkg/bindings/network"
"github.com/containers/podman/v5/pkg/bindings/system"
"github.com/containers/podman/v5/pkg/bindings/volumes"
"github.com/containers/podman/v5/pkg/domain/entities"
)
// SocketRuntime implements ContainerRuntime using Podman's REST API bindings.
// This is the preferred mode when a Podman socket is available.
type SocketRuntime struct {
conn context.Context
}
// NewSocketRuntime creates a new SocketRuntime connected to the given socket path.
// The socketPath should be in the format "unix:///path/to/podman.sock".
func NewSocketRuntime(socketPath string) (*SocketRuntime, error) {
conn, err := bindings.NewConnection(context.Background(), socketPath)
if err != nil {
return nil, err
}
return &SocketRuntime{conn: conn}, nil
}
// Mode returns "socket" to indicate this is the socket-based runtime.
func (r *SocketRuntime) Mode() string {
return "socket"
}
// Close cleans up any resources. For socket mode, this is a no-op.
func (r *SocketRuntime) Close() error {
return nil
}
// Container operations
// ListContainers returns all containers.
func (r *SocketRuntime) ListContainers(ctx context.Context) ([]ContainerSummary, error) {
all := true
opts := &containers.ListOptions{
All: &all,
}
podmanContainers, err := containers.List(r.conn, opts)
if err != nil {
return nil, err
}
return convertPodmanContainerList(podmanContainers), nil
}
// InspectContainer returns detailed information about a container.
func (r *SocketRuntime) InspectContainer(ctx context.Context, id string) (*ContainerDetails, error) {
data, err := containers.Inspect(r.conn, id, nil)
if err != nil {
return nil, err
}
return convertPodmanContainerInspect(data), nil
}
// StartContainer starts a stopped container.
func (r *SocketRuntime) StartContainer(ctx context.Context, id string) error {
return containers.Start(r.conn, id, nil)
}
// StopContainer stops a running container.
func (r *SocketRuntime) StopContainer(ctx context.Context, id string, timeout *int) error {
opts := &containers.StopOptions{}
if timeout != nil {
t := uint(*timeout)
opts.Timeout = &t
}
return containers.Stop(r.conn, id, opts)
}
// PauseContainer pauses a running container.
func (r *SocketRuntime) PauseContainer(ctx context.Context, id string) error {
return containers.Pause(r.conn, id, nil)
}
// UnpauseContainer unpauses a paused container.
func (r *SocketRuntime) UnpauseContainer(ctx context.Context, id string) error {
return containers.Unpause(r.conn, id, nil)
}
// RestartContainer restarts a container.
func (r *SocketRuntime) RestartContainer(ctx context.Context, id string, timeout *int) error {
opts := &containers.RestartOptions{}
if timeout != nil {
t := *timeout
opts.Timeout = &t
}
return containers.Restart(r.conn, id, opts)
}
// RemoveContainer removes a container.
func (r *SocketRuntime) RemoveContainer(ctx context.Context, id string, force bool, removeVolumes bool) error {
opts := &containers.RemoveOptions{
Force: &force,
Volumes: &removeVolumes,
}
_, err := containers.Remove(r.conn, id, opts)
return err
}
// ContainerTop returns process information from a container.
func (r *SocketRuntime) ContainerTop(ctx context.Context, id string) ([]string, [][]string, error) {
result, err := containers.Top(r.conn, id, nil)
if err != nil {
return nil, nil, err
}
return result.Titles, result.Processes, nil
}
// PruneContainers removes all stopped containers.
func (r *SocketRuntime) PruneContainers(ctx context.Context) error {
_, err := containers.Prune(r.conn, nil)
return err
}
// ContainerStats streams container statistics.
func (r *SocketRuntime) ContainerStats(ctx context.Context, id string, stream bool) (<-chan ContainerStatsEntry, <-chan error) {
statsChan := make(chan ContainerStatsEntry)
errChan := make(chan error, 1)
go func() {
defer close(statsChan)
defer close(errChan)
opts := &containers.StatsOptions{
Stream: &stream,
}
reportChan, err := containers.Stats(r.conn, []string{id}, opts)
if err != nil {
errChan <- err
return
}
for report := range reportChan {
if report.Error != nil {
errChan <- report.Error
return
}
for _, stat := range report.Stats {
entry := convertPodmanContainerStats(stat)
select {
case statsChan <- entry:
case <-ctx.Done():
return
}
}
}
}()
return statsChan, errChan
}
// Image operations
// ListImages returns all images.
func (r *SocketRuntime) ListImages(ctx context.Context) ([]ImageSummary, error) {
opts := &images.ListOptions{}
podmanImages, err := images.List(r.conn, opts)
if err != nil {
return nil, err
}
return convertPodmanImageList(podmanImages), nil
}
// InspectImage returns detailed information about an image.
func (r *SocketRuntime) InspectImage(ctx context.Context, id string) (*ImageDetails, error) {
data, err := images.GetImage(r.conn, id, nil)
if err != nil {
return nil, err
}
return convertPodmanImageInspect(data), nil
}
// ImageHistory returns the history of an image.
func (r *SocketRuntime) ImageHistory(ctx context.Context, id string) ([]ImageHistoryEntry, error) {
history, err := images.History(r.conn, id, nil)
if err != nil {
return nil, err
}
return convertPodmanImageHistory(history), nil
}
// RemoveImage removes an image.
func (r *SocketRuntime) RemoveImage(ctx context.Context, id string, force bool) error {
opts := &images.RemoveOptions{
Force: &force,
}
_, errs := images.Remove(r.conn, []string{id}, opts)
if len(errs) > 0 {
return errs[0]
}
return nil
}
// PruneImages removes unused images.
func (r *SocketRuntime) PruneImages(ctx context.Context) error {
_, err := images.Prune(r.conn, nil)
return err
}
// Volume operations
// ListVolumes returns all volumes.
func (r *SocketRuntime) ListVolumes(ctx context.Context) ([]VolumeSummary, error) {
opts := &volumes.ListOptions{}
podmanVolumes, err := volumes.List(r.conn, opts)
if err != nil {
return nil, err
}
return convertPodmanVolumeList(podmanVolumes), nil
}
// RemoveVolume removes a volume.
func (r *SocketRuntime) RemoveVolume(ctx context.Context, name string, force bool) error {
opts := &volumes.RemoveOptions{
Force: &force,
}
return volumes.Remove(r.conn, name, opts)
}
// PruneVolumes removes unused volumes.
func (r *SocketRuntime) PruneVolumes(ctx context.Context) error {
_, err := volumes.Prune(r.conn, nil)
return err
}
// Network operations
// ListNetworks returns all network.
func (r *SocketRuntime) ListNetworks(ctx context.Context) ([]NetworkSummary, error) {
opts := &network.ListOptions{}
podmanNetworks, err := network.List(r.conn, opts)
if err != nil {
return nil, err
}
return convertPodmanNetworkList(podmanNetworks), nil
}
// RemoveNetwork removes a network.
func (r *SocketRuntime) RemoveNetwork(ctx context.Context, name string) error {
_, err := network.Remove(r.conn, name, nil)
return err
}
// PruneNetworks removes unused network.
func (r *SocketRuntime) PruneNetworks(ctx context.Context) error {
_, err := network.Prune(r.conn, nil)
return err
}
// Events streams container runtime events.
func (r *SocketRuntime) Events(ctx context.Context) (<-chan Event, <-chan error) {
eventChan := make(chan Event)
errChan := make(chan error, 1)
go func() {
defer close(eventChan)
defer close(errChan)
opts := &system.EventsOptions{}
podmanEventChan, err := system.Events(r.conn, nil, opts)
if err != nil {
errChan <- err
return
}
for podmanEvent := range podmanEventChan {
event := Event{
Type: string(podmanEvent.Type),
Action: string(podmanEvent.Action),
Actor: EventActor{
ID: podmanEvent.Actor.ID,
Attributes: podmanEvent.Actor.Attributes,
},
Time: podmanEvent.Time,
}
select {
case eventChan <- event:
case <-ctx.Done():
return
}
}
}()
return eventChan, errChan
}
// Conversion functions
func convertPodmanContainerList(podmanContainers []entities.ListContainer) []ContainerSummary {
result := make([]ContainerSummary, len(podmanContainers))
for i, c := range podmanContainers {
result[i] = ContainerSummary{
ID: c.ID,
Names: c.Names,
Image: c.Image,
ImageID: c.ImageID,
Command: commandToString(c.Command),
Created: c.Created.Unix(),
State: c.State,
Status: c.Status,
Ports: convertPodmanPorts(c.Ports),
Labels: c.Labels,
SizeRw: c.Size.RwSize,
SizeRootFs: c.Size.RootFsSize,
Pod: c.Pod,
PodName: c.PodName,
}
}
return result
}
func commandToString(cmd []string) string {
if len(cmd) == 0 {
return ""
}
// Return the command as JSON for consistency with Docker
data, _ := json.Marshal(cmd)
return string(data)
}
func convertPodmanPorts(ports []entities.PortMapping) []PortMapping {
result := make([]PortMapping, len(ports))
for i, p := range ports {
result[i] = PortMapping{
IP: p.HostIP,
PrivatePort: p.ContainerPort,
PublicPort: p.HostPort,
Type: p.Protocol,
}
}
return result
}
func convertPodmanContainerInspect(data *entities.InspectContainerData) *ContainerDetails {
if data == nil {
return nil
}
details := &ContainerDetails{
ID: data.ID,
Name: data.Name,
Created: data.Created,
Path: data.Path,
Args: data.Args,
Image: data.Image,
ImageID: data.ImageName,
ResolvConfPath: data.ResolvConfPath,
HostnamePath: data.HostnamePath,
HostsPath: data.HostsPath,
LogPath: data.LogPath,
RestartCount: int(data.RestartCount),
Driver: data.Driver,
MountLabel: data.MountLabel,
ProcessLabel: data.ProcessLabel,
AppArmorProfile: data.AppArmorProfile,
}
if data.State != nil {
details.State = &ContainerState{
Status: data.State.Status,
Running: data.State.Running,
Paused: data.State.Paused,
Restarting: data.State.Restarting,
OOMKilled: data.State.OOMKilled,
Dead: data.State.Dead,
Pid: data.State.Pid,
ExitCode: int(data.State.ExitCode),
Error: data.State.Error,
StartedAt: data.State.StartedAt,
FinishedAt: data.State.FinishedAt,
}
if data.State.Health != nil {
details.State.Health = &HealthState{
Status: data.State.Health.Status,
FailingStreak: data.State.Health.FailingStreak,
}
}
}
if data.Config != nil {
details.Config = &ContainerConfig{
Hostname: data.Config.Hostname,
Domainname: data.Config.Domainname,
User: data.Config.User,
AttachStdin: data.Config.AttachStdin,
AttachStdout: data.Config.AttachStdout,
AttachStderr: data.Config.AttachStderr,
Tty: data.Config.Tty,
OpenStdin: data.Config.OpenStdin,
StdinOnce: data.Config.StdinOnce,
Env: data.Config.Env,
Cmd: data.Config.Cmd,
Image: data.Config.Image,
WorkingDir: data.Config.WorkingDir,
Entrypoint: data.Config.Entrypoint,
OnBuild: data.Config.OnBuild,
Labels: data.Config.Labels,
StopSignal: data.Config.StopSignal,
}
}
return details
}
func convertPodmanContainerStats(stat entities.ContainerStats) ContainerStatsEntry {
return ContainerStatsEntry{
Read: time.Now(),
PreRead: time.Now().Add(-time.Second),
CPUStats: CPUStats{
CPUUsage: CPUUsage{
TotalUsage: int64(stat.CPU),
},
SystemCPUUsage: int64(stat.CPUSystemNano),
OnlineCpus: int(stat.CPUNano),
},
PreCPUStats: CPUStats{},
MemoryStats: MemoryStats{
Usage: int64(stat.MemUsage),
Limit: int64(stat.MemLimit),
},
PidsStats: PidsStats{
Current: int(stat.PIDs),
},
Name: stat.Name,
ID: stat.ContainerID,
}
}
func convertPodmanImageList(podmanImages []*entities.ImageSummary) []ImageSummary {
result := make([]ImageSummary, len(podmanImages))
for i, img := range podmanImages {
result[i] = ImageSummary{
ID: img.ID,
ParentID: img.ParentId,
RepoTags: img.RepoTags,
RepoDigests: img.RepoDigests,
Created: img.Created,
Size: img.Size,
SharedSize: img.SharedSize,
VirtualSize: img.VirtualSize,
Labels: img.Labels,
Containers: img.Containers,
}
}
return result
}
func convertPodmanImageInspect(data *entities.ImageInspectReport) *ImageDetails {
if data == nil {
return nil
}
details := &ImageDetails{
ID: data.ID,
RepoTags: data.RepoTags,
RepoDigests: data.RepoDigests,
Parent: data.Parent,
Comment: data.Comment,
Created: *data.Created,
Author: data.Author,
Architecture: data.Architecture,
Os: data.Os,
Size: data.Size,
VirtualSize: data.VirtualSize,
}
if data.RootFS != nil {
details.RootFS = RootFS{
Type: data.RootFS.Type,
Layers: data.RootFS.Layers,
}
}
return details
}
func convertPodmanImageHistory(history []*entities.ImageHistoryLayer) []ImageHistoryEntry {
result := make([]ImageHistoryEntry, len(history))
for i, layer := range history {
result[i] = ImageHistoryEntry{
ID: layer.ID,
Created: layer.Created.Unix(),
CreatedBy: layer.CreatedBy,
Tags: layer.Tags,
Size: layer.Size,
Comment: layer.Comment,
}
}
return result
}
func convertPodmanVolumeList(podmanVolumes []*entities.VolumeListReport) []VolumeSummary {
result := make([]VolumeSummary, len(podmanVolumes))
for i, vol := range podmanVolumes {
result[i] = VolumeSummary{
Name: vol.Name,
Driver: vol.Driver,
Mountpoint: vol.Mountpoint,
CreatedAt: vol.CreatedAt,
Labels: vol.Labels,
Scope: vol.Scope,
Options: vol.Options,
}
}
return result
}
func convertPodmanNetworkList(podmanNetworks []entities.NetworkListReport) []NetworkSummary {
result := make([]NetworkSummary, len(podmanNetworks))
for i, nw := range podmanNetworks {
result[i] = NetworkSummary{
Name: nw.Name,
ID: nw.ID,
Created: nw.Created,
Driver: nw.Driver,
Labels: nw.Labels,
}
}
return result
}

View file

@ -0,0 +1,426 @@
package commands
import "time"
// ContainerSummary provides runtime-agnostic container information.
// This replaces Docker's container.Summary type.
type ContainerSummary struct {
ID string
Names []string
Image string
ImageID string
Command string
Created int64
State string
Status string
Ports []PortMapping
Labels map[string]string
SizeRw int64
SizeRootFs int64
// Podman-specific fields
Pod string
PodName string
}
// PortMapping represents a container port mapping.
type PortMapping struct {
IP string
PrivatePort uint16
PublicPort uint16
Type string
}
// ContainerDetails provides full container inspection data.
// This replaces Docker's container.InspectResponse type.
type ContainerDetails struct {
ID string
Name string
Created time.Time
Path string
Args []string
State *ContainerState
Image string
ImageID string
ResolvConfPath string
HostnamePath string
HostsPath string
LogPath string
RestartCount int
Driver string
Platform string
MountLabel string
ProcessLabel string
AppArmorProfile string
Config *ContainerConfig
NetworkSettings *NetworkSettings
Mounts []Mount
}
// ContainerState represents the state of a container.
type ContainerState struct {
Status string
Running bool
Paused bool
Restarting bool
OOMKilled bool
Dead bool
Pid int
ExitCode int
Error string
StartedAt time.Time
FinishedAt time.Time
Health *HealthState
}
// HealthState represents the health check state of a container.
type HealthState struct {
Status string
FailingStreak int
Log []HealthLog
}
// HealthLog represents a health check log entry.
type HealthLog struct {
Start time.Time
End time.Time
ExitCode int
Output string
}
// ContainerConfig represents container configuration.
type ContainerConfig struct {
Hostname string
Domainname string
User string
AttachStdin bool
AttachStdout bool
AttachStderr bool
ExposedPorts map[string]struct{}
Tty bool
OpenStdin bool
StdinOnce bool
Env []string
Cmd []string
Image string
Volumes map[string]struct{}
WorkingDir string
Entrypoint []string
OnBuild []string
Labels map[string]string
StopSignal string
StopTimeout *int
Shell []string
}
// NetworkSettings represents container network settings.
type NetworkSettings struct {
Bridge string
SandboxID string
HairpinMode bool
LinkLocalIPv6Address string
LinkLocalIPv6PrefixLen int
Ports map[string][]PortBinding
SandboxKey string
Networks map[string]*EndpointSettings
}
// PortBinding represents a port binding.
type PortBinding struct {
HostIP string
HostPort string
}
// EndpointSettings represents network endpoint settings.
type EndpointSettings struct {
IPAMConfig *EndpointIPAMConfig
Links []string
Aliases []string
NetworkID string
EndpointID string
Gateway string
IPAddress string
IPPrefixLen int
IPv6Gateway string
GlobalIPv6Address string
GlobalIPv6PrefixLen int
MacAddress string
DriverOpts map[string]string
}
// EndpointIPAMConfig represents IPAM configuration for an endpoint.
type EndpointIPAMConfig struct {
IPv4Address string
IPv6Address string
LinkLocalIPs []string
}
// Mount represents a container mount.
type Mount struct {
Type string
Name string
Source string
Destination string
Driver string
Mode string
RW bool
Propagation string
}
// ContainerStatsEntry provides runtime-agnostic container stats.
// This is used for streaming stats from the runtime.
type ContainerStatsEntry struct {
Read time.Time
PreRead time.Time
CPUStats CPUStats
PreCPUStats CPUStats
MemoryStats MemoryStats
PidsStats PidsStats
Networks map[string]NetworkStats
BlkioStats BlkioStats
Name string
ID string
}
// CPUStats represents CPU usage statistics.
type CPUStats struct {
CPUUsage CPUUsage
SystemCPUUsage int64
OnlineCpus int
ThrottlingData ThrottlingData
}
// CPUUsage represents CPU usage breakdown.
type CPUUsage struct {
TotalUsage int64
PercpuUsage []int64
UsageInKernelmode int64
UsageInUsermode int64
}
// ThrottlingData represents CPU throttling data.
type ThrottlingData struct {
Periods int
ThrottledPeriods int
ThrottledTime int64
}
// MemoryStats represents memory usage statistics.
type MemoryStats struct {
Usage int64
MaxUsage int64
Limit int64
Stats MemoryStatsDetails
}
// MemoryStatsDetails provides detailed memory statistics.
type MemoryStatsDetails struct {
ActiveAnon int64
ActiveFile int64
Cache int64
Dirty int64
HierarchicalMemoryLimit int64
HierarchicalMemswLimit int64
InactiveAnon int64
InactiveFile int64
MappedFile int64
Pgfault int64
Pgmajfault int64
Pgpgin int64
Pgpgout int64
Rss int64
RssHuge int64
TotalActiveAnon int64
TotalActiveFile int64
TotalCache int64
TotalDirty int64
TotalInactiveAnon int64
TotalInactiveFile int64
TotalMappedFile int64
TotalPgfault int64
TotalPgmajfault int64
TotalPgpgin int64
TotalPgpgout int64
TotalRss int64
TotalRssHuge int64
TotalUnevictable int64
TotalWriteback int64
Unevictable int64
Writeback int64
}
// PidsStats represents PID statistics.
type PidsStats struct {
Current int
Limit int64
}
// NetworkStats represents network I/O statistics.
type NetworkStats struct {
RxBytes int64
RxPackets int64
RxErrors int64
RxDropped int64
TxBytes int64
TxPackets int64
TxErrors int64
TxDropped int64
}
// BlkioStats represents block I/O statistics.
type BlkioStats struct {
IoServiceBytesRecursive []BlkioStatEntry
IoServicedRecursive []BlkioStatEntry
IoQueueRecursive []BlkioStatEntry
IoServiceTimeRecursive []BlkioStatEntry
IoWaitTimeRecursive []BlkioStatEntry
IoMergedRecursive []BlkioStatEntry
IoTimeRecursive []BlkioStatEntry
SectorsRecursive []BlkioStatEntry
}
// BlkioStatEntry represents a single block I/O stat entry.
type BlkioStatEntry struct {
Major int64
Minor int64
Op string
Value int64
}
// ImageSummary provides runtime-agnostic image information.
// This replaces Docker's image.Summary type.
type ImageSummary struct {
ID string
ParentID string
RepoTags []string
RepoDigests []string
Created int64
Size int64
SharedSize int64
VirtualSize int64
Labels map[string]string
Containers int64
}
// ImageDetails provides full image inspection data.
type ImageDetails struct {
ID string
RepoTags []string
RepoDigests []string
Parent string
Comment string
Created time.Time
Container string
DockerVersion string
Author string
Config *ContainerConfig
Architecture string
Os string
OsVersion string
Size int64
VirtualSize int64
RootFS RootFS
Metadata ImageMetadata
}
// RootFS represents the root filesystem of an image.
type RootFS struct {
Type string
Layers []string
}
// ImageMetadata represents image metadata.
type ImageMetadata struct {
LastTagTime time.Time
}
// ImageHistoryEntry represents a layer in image history.
type ImageHistoryEntry struct {
ID string
Created int64
CreatedBy string
Tags []string
Size int64
Comment string
}
// VolumeSummary provides runtime-agnostic volume information.
// This replaces Docker's volume.Volume type.
type VolumeSummary struct {
Name string
Driver string
Mountpoint string
CreatedAt time.Time
Status map[string]interface{}
Labels map[string]string
Scope string
Options map[string]string
UsageData *VolumeUsageData
}
// VolumeUsageData represents volume usage statistics.
type VolumeUsageData struct {
Size int64
RefCount int64
}
// NetworkSummary provides runtime-agnostic network information.
// This replaces Docker's network.Inspect type.
type NetworkSummary struct {
Name string
ID string
Created time.Time
Scope string
Driver string
EnableIPv6 bool
IPAM IPAM
Internal bool
Attachable bool
Ingress bool
Containers map[string]EndpointResource
Options map[string]string
Labels map[string]string
}
// IPAM represents IP Address Management configuration.
type IPAM struct {
Driver string
Options map[string]string
Config []IPAMConfig
}
// IPAMConfig represents IPAM configuration for a network.
type IPAMConfig struct {
Subnet string
IPRange string
Gateway string
AuxAddress map[string]string
}
// EndpointResource represents a container endpoint on a network.
type EndpointResource struct {
Name string
EndpointID string
MacAddress string
IPv4Address string
IPv6Address string
}
// TopResponse represents the response from ContainerTop.
type TopResponse struct {
Titles []string
Processes [][]string
}
// Event represents a container runtime event (container start/stop, image pull, etc.)
type Event struct {
Type string // "container", "image", "volume", "network"
Action string // "start", "stop", "create", "delete", etc.
Actor EventActor
Time int64
}
// EventActor represents the actor (container, image, etc.) that triggered an event.
type EventActor struct {
ID string
Attributes map[string]string
}

View file

@ -4,24 +4,23 @@ import (
"context"
"os/exec"
"github.com/docker/docker/api/types/container"
"github.com/christophe-duc/lazypodman/pkg/utils"
"github.com/sirupsen/logrus"
)
// Service : A docker Service
// Service represents a compose service
type Service struct {
Name string
ID string
OSCommand *OSCommand
Log *logrus.Entry
Container *Container
DockerCommand LimitedDockerCommand
PodmanCommand LimitedPodmanCommand
}
// Remove removes the service's containers
func (s *Service) Remove(options container.RemoveOptions) error {
return s.Container.Remove(options)
func (s *Service) Remove(force bool, removeVolumes bool) error {
return s.Container.Remove(force, removeVolumes)
}
// Stop stops the service's containers
@ -47,7 +46,7 @@ func (s *Service) Start() error {
func (s *Service) runCommand(templateCmdStr string) error {
command := utils.ApplyTemplate(
templateCmdStr,
s.DockerCommand.NewCommandObject(CommandObject{Service: s}),
s.PodmanCommand.NewCommandObject(CommandObject{Service: s}),
)
return s.OSCommand.RunCommand(command)
}
@ -62,7 +61,7 @@ func (s *Service) ViewLogs() (*exec.Cmd, error) {
templateString := s.OSCommand.Config.UserConfig.CommandTemplates.ViewServiceLogs
command := utils.ApplyTemplate(
templateString,
s.DockerCommand.NewCommandObject(CommandObject{Service: s}),
s.PodmanCommand.NewCommandObject(CommandObject{Service: s}),
)
cmd := s.OSCommand.ExecutableFromString(command)
@ -76,7 +75,7 @@ func (s *Service) RenderTop(ctx context.Context) (string, error) {
templateString := s.OSCommand.Config.UserConfig.CommandTemplates.ServiceTop
command := utils.ApplyTemplate(
templateString,
s.DockerCommand.NewCommandObject(CommandObject{Service: s}),
s.PodmanCommand.NewCommandObject(CommandObject{Service: s}),
)
return s.OSCommand.RunCommandWithOutputContext(ctx, command)

View file

@ -3,54 +3,21 @@ package commands
import (
"context"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
"github.com/sirupsen/logrus"
)
// Volume : A docker Volume
// Volume represents a Podman volume
type Volume struct {
Name string
Volume *volume.Volume
Client *client.Client
Summary VolumeSummary
Runtime ContainerRuntime
OSCommand *OSCommand
Log *logrus.Entry
DockerCommand LimitedDockerCommand
}
// RefreshVolumes gets the volumes and stores them
func (c *DockerCommand) RefreshVolumes() ([]*Volume, error) {
result, err := c.Client.VolumeList(context.Background(), volume.ListOptions{})
if err != nil {
return nil, err
}
volumes := result.Volumes
ownVolumes := make([]*Volume, len(volumes))
for i, vol := range volumes {
ownVolumes[i] = &Volume{
Name: vol.Name,
Volume: vol,
Client: c.Client,
OSCommand: c.OSCommand,
Log: c.Log,
DockerCommand: c,
}
}
return ownVolumes, nil
}
// PruneVolumes prunes volumes
func (c *DockerCommand) PruneVolumes() error {
_, err := c.Client.VolumesPrune(context.Background(), filters.Args{})
return err
PodmanCommand LimitedPodmanCommand
}
// Remove removes the volume
func (v *Volume) Remove(force bool) error {
return v.Client.VolumeRemove(context.Background(), v.Name, force)
ctx := context.Background()
return v.Runtime.RemoveVolume(ctx, v.Name, force)
}

View file

@ -8,8 +8,6 @@ import (
"os/signal"
"time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/stdcopy"
"github.com/fatih/color"
"github.com/christophe-duc/lazypodman/pkg/commands"
"github.com/christophe-duc/lazypodman/pkg/tasks"
@ -105,48 +103,42 @@ func (gui *Gui) promptToReturn() {
}
func (gui *Gui) writeContainerLogs(ctr *commands.Container, ctx context.Context, writer io.Writer) error {
readCloser, err := gui.DockerCommand.Client.ContainerLogs(ctx, ctr.ID, container.LogsOptions{
ShowStdout: true,
ShowStderr: true,
Timestamps: gui.Config.UserConfig.Logs.Timestamps,
Since: gui.Config.UserConfig.Logs.Since,
Tail: gui.Config.UserConfig.Logs.Tail,
Follow: true,
})
if err != nil {
// Build podman logs command
args := []string{"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, ctr.ID)
cmd := ctr.OSCommand.NewCmd("podman", args...)
cmd.Stdout = writer
cmd.Stderr = writer
if err := cmd.Start(); err != nil {
gui.Log.Error(err)
return err
}
defer readCloser.Close()
if !ctr.DetailsLoaded() {
// loop until the details load or context is cancelled, using timer
ticker := time.NewTicker(time.Millisecond * 100)
defer ticker.Stop()
outer:
for {
select {
case <-ctx.Done():
return nil
case <-ticker.C:
if ctr.DetailsLoaded() {
break outer
}
}
// Wait for context cancellation or command completion
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
select {
case <-ctx.Done():
if err := ctr.OSCommand.Kill(cmd); err != nil {
gui.Log.Warn(err)
}
return nil
case err := <-done:
return err
}
if ctr.Details.Config.Tty {
_, err = io.Copy(writer, readCloser)
if err != nil {
return err
}
} else {
_, err = stdcopy.StdCopy(writer, writer, readCloser)
if err != nil {
return err
}
}
return nil
}

View file

@ -6,7 +6,6 @@ import (
"strings"
"time"
"github.com/docker/docker/api/types/container"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/christophe-duc/lazypodman/pkg/commands"
@ -255,7 +254,7 @@ func (gui *Gui) refreshContainersAndServices() error {
originalSelectedLineIdx := gui.Panels.Services.SelectedIdx
selectedService, isServiceSelected := gui.Panels.Services.List.TryGet(originalSelectedLineIdx)
containers, services, err := gui.DockerCommand.RefreshContainersAndServices(
containers, services, err := gui.PodmanCommand.RefreshContainersAndServices(
gui.Panels.Services.List.GetAllItems(),
gui.Panels.Containers.List.GetAllItems(),
)
@ -283,7 +282,7 @@ func (gui *Gui) refreshContainersAndServices() error {
}
func (gui *Gui) renderContainersAndServices() error {
if gui.DockerCommand.InDockerComposeProject {
if gui.PodmanCommand.InDockerComposeProject {
if err := gui.Panels.Services.RerenderList(); err != nil {
return err
}
@ -308,14 +307,13 @@ func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
return nil
}
handleMenuPress := func(configOptions container.RemoveOptions) error {
handleMenuPress := func(force bool, removeVolumes bool) error {
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
if err := ctr.Remove(configOptions); err != nil {
if err := ctr.Remove(force, removeVolumes); err != nil {
if commands.HasErrorCode(err, commands.MustStopContainer) {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.MustForceToRemoveContainer, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
configOptions.Force = true
return ctr.Remove(configOptions)
return ctr.Remove(true, removeVolumes)
})
}, nil)
}
@ -327,12 +325,12 @@ func (gui *Gui) handleContainersRemoveMenu(g *gocui.Gui, v *gocui.View) error {
menuItems := []*types.MenuItem{
{
LabelColumns: []string{gui.Tr.Remove, "docker rm " + ctr.ID[1:10]},
OnPress: func() error { return handleMenuPress(container.RemoveOptions{}) },
LabelColumns: []string{gui.Tr.Remove, "podman rm " + ctr.ID[1:10]},
OnPress: func() error { return handleMenuPress(false, false) },
},
{
LabelColumns: []string{gui.Tr.RemoveWithVolumes, "docker rm --volumes " + ctr.ID[1:10]},
OnPress: func() error { return handleMenuPress(container.RemoveOptions{RemoveVolumes: true}) },
LabelColumns: []string{gui.Tr.RemoveWithVolumes, "podman rm --volumes " + ctr.ID[1:10]},
OnPress: func() error { return handleMenuPress(false, true) },
},
}
@ -416,7 +414,7 @@ func (gui *Gui) handleContainerAttach(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handlePruneContainers() error {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneContainers, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error {
err := gui.DockerCommand.PruneContainers()
err := gui.PodmanCommand.PruneContainers()
if err != nil {
return gui.createErrorPanel(err.Error())
}
@ -446,7 +444,7 @@ func (gui *Gui) handleContainersExecShell(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) containerExecShell(container *commands.Container) error {
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{
Container: container,
})
@ -463,7 +461,7 @@ func (gui *Gui) handleContainersCustomCommand(g *gocui.Gui, v *gocui.View) error
return nil
}
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{
Container: ctr,
})
@ -490,7 +488,7 @@ func (gui *Gui) handleRemoveContainers() error {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmRemoveContainers, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.RemovingStatus, func() error {
for _, ctr := range gui.Panels.Containers.List.GetAllItems() {
if err := ctr.Remove(container.RemoveOptions{Force: true}); err != nil {
if err := ctr.Remove(true, false); err != nil {
gui.Log.Error(err)
}
}
@ -517,7 +515,7 @@ func (gui *Gui) handleContainersBulkCommand(g *gocui.Gui, v *gocui.View) error {
}
bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Containers...)
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{})
return gui.createBulkCommandMenu(bulkCommands, commandObject)
}

View file

@ -6,8 +6,6 @@ import (
"strings"
"time"
"github.com/docker/docker/api/types/events"
"github.com/go-errors/errors"
throttle "github.com/boz/go-throttle"
@ -27,7 +25,7 @@ import (
type Gui struct {
g *gocui.Gui
Log *logrus.Entry
DockerCommand *commands.DockerCommand
PodmanCommand *commands.PodmanCommand
OSCommand *commands.OSCommand
State guiState
Config *config.AppConfig
@ -125,7 +123,7 @@ func getScreenMode(config *config.AppConfig) WindowMaximisation {
}
// NewGui builds a new gui handler
func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand *commands.OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*Gui, error) {
func NewGui(log *logrus.Entry, podmanCommand *commands.PodmanCommand, oSCommand *commands.OSCommand, tr *i18n.TranslationSet, config *config.AppConfig, errorChan chan error) (*Gui, error) {
initialState := guiState{
Platform: *oSCommand.Platform,
Panels: &panelStates{
@ -141,7 +139,7 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
gui := &Gui{
Log: log,
DockerCommand: dockerCommand,
PodmanCommand: podmanCommand,
OSCommand: oSCommand,
State: initialState,
Config: config,
@ -292,7 +290,7 @@ func (gui *Gui) setPanels() {
}
func (gui *Gui) updateContainerDetails() error {
return gui.DockerCommand.RefreshContainerDetails(gui.Panels.Containers.List.GetAllItems())
return gui.PodmanCommand.RefreshContainerDetails(gui.Panels.Containers.List.GetAllItems())
}
func (gui *Gui) refresh() {
@ -328,50 +326,38 @@ func (gui *Gui) listenForEvents(ctx context.Context, refresh func()) {
onError := func(err error) {
if err != nil {
gui.ErrorChan <- errors.Errorf("Docker event stream returned error: %s\nRetry count: %d", err.Error(), errorCount)
gui.ErrorChan <- errors.Errorf("Podman event stream returned error: %s\nRetry count: %d", err.Error(), errorCount)
}
errorCount++
time.Sleep(time.Second * 2)
}
outer:
// Try to use the runtime's event stream if available
eventChan, errChan := gui.PodmanCommand.Runtime.Events(ctx)
for {
messageChan, errChan := gui.DockerCommand.Client.Events(context.Background(), events.ListOptions{})
if errorCount > 0 {
select {
case <-ctx.Done():
return
case err := <-errChan:
onError(err)
continue outer
default:
// If we're here then we lost connection to docker and we just got it back.
// The reason we do this refresh explicitly is because successfully
// reconnecting with docker does not mean it's going to send us a new
// event any time soon.
// Assuming the confirmation prompt currently holds the given error
_ = gui.closeConfirmationPrompt()
refresh()
errorCount = 0
}
}
for {
select {
case <-ctx.Done():
return
case message := <-messageChan:
select {
case <-ctx.Done():
return
case event := <-eventChan:
if event.Type != "" {
// We could be more granular about what events should trigger which refreshes.
// At the moment it's pretty efficient though, and it might not be worth
// the maintenance burden of mapping specific events to specific refreshes
refresh()
gui.Log.Infof("received event of type: %s", message.Type)
case err := <-errChan:
gui.Log.Infof("received event of type: %s", event.Type)
}
case err := <-errChan:
if err != nil {
onError(err)
continue outer
// Attempt to reconnect
if errorCount > 0 {
_ = gui.closeConfirmationPrompt()
refresh()
errorCount = 0
}
// Re-establish event stream
eventChan, errChan = gui.PodmanCommand.Runtime.Events(ctx)
}
}
}
@ -459,7 +445,7 @@ func (gui *Gui) ShouldRefresh(key string) bool {
}
func (gui *Gui) initiallyFocusedViewName() string {
if gui.DockerCommand.InDockerComposeProject {
if gui.PodmanCommand.InDockerComposeProject {
return "services"
}
return "containers"
@ -485,7 +471,7 @@ func (gui *Gui) monitorContainerStats(ctx context.Context) {
case <-ticker.C:
for _, container := range gui.Panels.Containers.List.GetAllItems() {
if !container.MonitoringStats {
go gui.DockerCommand.CreateClientStatMonitor(container)
go gui.PodmanCommand.CreateClientStatMonitor(container)
}
}
}

View file

@ -5,7 +5,6 @@ import (
"strings"
"time"
"github.com/docker/docker/api/types/image"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/christophe-duc/lazypodman/pkg/commands"
@ -101,7 +100,7 @@ func (gui *Gui) reloadImages() error {
}
func (gui *Gui) refreshStateImages() error {
images, err := gui.DockerCommand.RefreshImages()
images, err := gui.PodmanCommand.RefreshImages()
if err != nil {
return err
}
@ -121,9 +120,9 @@ func (gui *Gui) FilterString(view *gocui.View) string {
func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
type removeImageOption struct {
description string
command string
configOptions image.RemoveOptions
description string
command string
force bool
}
img, err := gui.Panels.Images.GetSelectedItem()
@ -133,27 +132,17 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
shortSha := img.ID[7:17]
// TODO: have a way of toggling in a menu instead of showing each permutation as a separate menu item
// Simplified menu - Podman's rmi handles pruning automatically
options := []*removeImageOption{
{
description: gui.Tr.Remove,
command: "docker image rm " + shortSha,
configOptions: image.RemoveOptions{PruneChildren: true, Force: false},
description: gui.Tr.Remove,
command: "podman image rm " + shortSha,
force: false,
},
{
description: gui.Tr.RemoveWithoutPrune,
command: "docker image rm --no-prune " + shortSha,
configOptions: image.RemoveOptions{PruneChildren: false, Force: false},
},
{
description: gui.Tr.RemoveWithForce,
command: "docker image rm --force " + shortSha,
configOptions: image.RemoveOptions{PruneChildren: true, Force: true},
},
{
description: gui.Tr.RemoveWithoutPruneWithForce,
command: "docker image rm --no-prune --force " + shortSha,
configOptions: image.RemoveOptions{PruneChildren: false, Force: true},
description: gui.Tr.RemoveWithForce,
command: "podman image rm --force " + shortSha,
force: true,
},
}
@ -164,7 +153,7 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
color.New(color.FgRed).Sprint(option.command),
},
OnPress: func() error {
if err := img.Remove(option.configOptions); err != nil {
if err := img.Remove(option.force); err != nil {
return gui.createErrorPanel(err.Error())
}
@ -182,7 +171,7 @@ func (gui *Gui) handleImagesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handlePruneImages() error {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneImages, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error {
err := gui.DockerCommand.PruneImages()
err := gui.PodmanCommand.PruneImages()
if err != nil {
return gui.createErrorPanel(err.Error())
}
@ -197,7 +186,7 @@ func (gui *Gui) handleImagesCustomCommand(g *gocui.Gui, v *gocui.View) error {
return nil
}
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{
Image: img,
})
@ -215,7 +204,7 @@ func (gui *Gui) handleImagesBulkCommand(g *gocui.Gui, v *gocui.View) error {
}
bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Images...)
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{})
return gui.createBulkCommandMenu(bulkCommands, commandObject)
}

View file

@ -89,7 +89,7 @@ func (gui *Gui) reloadNetworks() error {
}
func (gui *Gui) refreshStateNetworks() error {
networks, err := gui.DockerCommand.RefreshNetworks()
networks, err := gui.PodmanCommand.RefreshNetworks()
if err != nil {
return err
}
@ -140,7 +140,7 @@ func (gui *Gui) handleNetworksRemoveMenu(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handlePruneNetworks() error {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneNetworks, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error {
err := gui.DockerCommand.PruneNetworks()
err := gui.PodmanCommand.PruneNetworks()
if err != nil {
return gui.createErrorPanel(err.Error())
}
@ -155,7 +155,7 @@ func (gui *Gui) handleNetworksCustomCommand(g *gocui.Gui, v *gocui.View) error {
return nil
}
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{
Network: network,
})
@ -173,7 +173,7 @@ func (gui *Gui) handleNetworksBulkCommand(g *gocui.Gui, v *gocui.View) error {
}
bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Networks...)
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{})
return gui.createBulkCommandMenu(bulkCommands, commandObject)
}

View file

@ -23,7 +23,7 @@ func (gui *Gui) getProjectPanel() *panels.SideListPanel[*commands.Project] {
return &panels.SideListPanel[*commands.Project]{
ContextState: &panels.ContextState[*commands.Project]{
GetMainTabs: func() []panels.MainTab[*commands.Project] {
if gui.DockerCommand.InDockerComposeProject {
if gui.PodmanCommand.InDockerComposeProject {
return []panels.MainTab[*commands.Project]{
{
Key: "logs",
@ -79,7 +79,7 @@ func (gui *Gui) refreshProject() error {
func (gui *Gui) getProjectName() string {
projectName := path.Base(gui.Config.ProjectDir)
if gui.DockerCommand.InDockerComposeProject {
if gui.PodmanCommand.InDockerComposeProject {
for _, service := range gui.Panels.Services.List.GetAllItems() {
container := service.Container
if container != nil && container.DetailsLoaded() {
@ -122,7 +122,7 @@ func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
cmd := gui.OSCommand.RunCustomCommand(
utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.AllLogs,
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
gui.PodmanCommand.NewCommandObject(commands.CommandObject{}),
),
)
@ -146,7 +146,7 @@ func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
func (gui *Gui) renderDockerComposeConfig(_project *commands.Project) tasks.TaskFunc {
return gui.NewSimpleRenderStringTask(func() string {
return utils.ColoredYamlString(gui.DockerCommand.DockerComposeConfig())
return utils.ColoredYamlString(gui.PodmanCommand.DockerComposeConfig())
})
}
@ -173,7 +173,7 @@ func lazypodmanTitle() string {
// handleViewAllLogs switches to a subprocess viewing all the logs from docker-compose
func (gui *Gui) handleViewAllLogs(g *gocui.Gui, v *gocui.View) error {
c, err := gui.DockerCommand.ViewAllLogs()
c, err := gui.PodmanCommand.ViewAllLogs()
if err != nil {
return gui.createErrorPanel(err.Error())
}

View file

@ -78,7 +78,7 @@ func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {
return presentation.GetServiceDisplayStrings(&gui.Config.UserConfig.Gui, service)
},
Hide: func() bool {
return !gui.DockerCommand.InDockerComposeProject
return !gui.PodmanCommand.InDockerComposeProject
},
}
}
@ -292,7 +292,7 @@ func (gui *Gui) handleProjectUp(g *gocui.Gui, v *gocui.View) error {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmUpProject, func(g *gocui.Gui, v *gocui.View) error {
cmdStr := utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.Up,
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
gui.PodmanCommand.NewCommandObject(commands.CommandObject{}),
)
return gui.WithWaitingStatus(gui.Tr.UppingProjectStatus, func() error {
@ -307,12 +307,12 @@ func (gui *Gui) handleProjectUp(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handleProjectDown(g *gocui.Gui, v *gocui.View) error {
downCommand := utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.Down,
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
gui.PodmanCommand.NewCommandObject(commands.CommandObject{}),
)
downWithVolumesCommand := utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.DownWithVolumes,
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
gui.PodmanCommand.NewCommandObject(commands.CommandObject{}),
)
options := []*commandOption{
@ -363,12 +363,12 @@ func (gui *Gui) handleServiceRestartMenu(g *gocui.Gui, v *gocui.View) error {
rebuildCommand := utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.RebuildService,
gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
gui.PodmanCommand.NewCommandObject(commands.CommandObject{Service: service}),
)
recreateCommand := utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.RecreateService,
gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
gui.PodmanCommand.NewCommandObject(commands.CommandObject{Service: service}),
)
options := []*commandOption{
@ -376,7 +376,7 @@ func (gui *Gui) handleServiceRestartMenu(g *gocui.Gui, v *gocui.View) error {
description: gui.Tr.Restart,
command: utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.RestartService,
gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
gui.PodmanCommand.NewCommandObject(commands.CommandObject{Service: service}),
),
onPress: func() error {
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
@ -391,7 +391,7 @@ func (gui *Gui) handleServiceRestartMenu(g *gocui.Gui, v *gocui.View) error {
description: gui.Tr.Recreate,
command: utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.RecreateService,
gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
gui.PodmanCommand.NewCommandObject(commands.CommandObject{Service: service}),
),
onPress: func() error {
return gui.WithWaitingStatus(gui.Tr.RestartingStatus, func() error {
@ -406,7 +406,7 @@ func (gui *Gui) handleServiceRestartMenu(g *gocui.Gui, v *gocui.View) error {
description: gui.Tr.Rebuild,
command: utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.RebuildService,
gui.DockerCommand.NewCommandObject(commands.CommandObject{Service: service}),
gui.PodmanCommand.NewCommandObject(commands.CommandObject{Service: service}),
),
onPress: func() error {
return gui.runSubprocess(gui.OSCommand.RunCustomCommand(rebuildCommand))
@ -433,7 +433,7 @@ func (gui *Gui) handleServicesCustomCommand(g *gocui.Gui, v *gocui.View) error {
return nil
}
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{
Service: service,
Container: service.Container,
})
@ -466,7 +466,7 @@ L:
func (gui *Gui) handleServicesBulkCommand(g *gocui.Gui, v *gocui.View) error {
bulkCommands := gui.Config.UserConfig.BulkCommands.Services
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{})
return gui.createBulkCommandMenu(bulkCommands, commandObject)
}

View file

@ -128,7 +128,7 @@ func (gui *Gui) createAllViews() error {
gui.Views.Containers.Highlight = true
gui.Views.Containers.SelBgColor = selectedLineBgColor
if gui.Config.UserConfig.Gui.ShowAllContainers || !gui.DockerCommand.InDockerComposeProject {
if gui.Config.UserConfig.Gui.ShowAllContainers || !gui.PodmanCommand.InDockerComposeProject {
gui.Views.Containers.Title = gui.Tr.ContainersTitle
} else {
gui.Views.Containers.Title = gui.Tr.StandaloneContainersTitle

View file

@ -94,7 +94,7 @@ func (gui *Gui) reloadVolumes() error {
}
func (gui *Gui) refreshStateVolumes() error {
volumes, err := gui.DockerCommand.RefreshVolumes()
volumes, err := gui.PodmanCommand.RefreshVolumes()
if err != nil {
return err
}
@ -152,7 +152,7 @@ func (gui *Gui) handleVolumesRemoveMenu(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handlePruneVolumes() error {
return gui.createConfirmationPanel(gui.Tr.Confirm, gui.Tr.ConfirmPruneVolumes, func(g *gocui.Gui, v *gocui.View) error {
return gui.WithWaitingStatus(gui.Tr.PruningStatus, func() error {
err := gui.DockerCommand.PruneVolumes()
err := gui.PodmanCommand.PruneVolumes()
if err != nil {
return gui.createErrorPanel(err.Error())
}
@ -167,7 +167,7 @@ func (gui *Gui) handleVolumesCustomCommand(g *gocui.Gui, v *gocui.View) error {
return nil
}
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{
Volume: volume,
})
@ -185,7 +185,7 @@ func (gui *Gui) handleVolumesBulkCommand(g *gocui.Gui, v *gocui.View) error {
}
bulkCommands := append(baseBulkCommands, gui.Config.UserConfig.BulkCommands.Volumes...)
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{})
commandObject := gui.PodmanCommand.NewCommandObject(commands.CommandObject{})
return gui.createBulkCommandMenu(bulkCommands, commandObject)
}