Compare commits

..

No commits in common. "main" and "v1.7.0" have entirely different histories.
main ... v1.7.0

1055 changed files with 89269 additions and 19610 deletions

View File

@ -18,7 +18,7 @@ jobs:
runs-on: 'ubuntu-latest'
steps:
- name: Check out the repo
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5

View File

@ -10,7 +10,7 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: '>=1.23'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- run: |
go mod tidy
go mod vendor
@ -25,7 +25,7 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: '1.23'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- run: |
bash hack/install_dep.sh
make .install.golangci-lint
@ -36,7 +36,7 @@ jobs:
container:
image: fedora:latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- run: |
# Preparing and running unit tests #
dnf -y install git-core golang glibc-static git-core wget gcc make
@ -60,6 +60,6 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: '>=1.23'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- run: |
make all

View File

@ -6,7 +6,7 @@ jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: pre-commit/action@v3.0.1
@ -34,7 +34,7 @@ jobs:
codespell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- uses: codespell-project/actions-codespell@master
with:
check_filenames: true
@ -46,7 +46,7 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: '>=1.23'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- run: |
go mod tidy
go mod vendor
@ -61,7 +61,7 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: '1.23'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- run: |
bash hack/install_dep.sh
make .install.golangci-lint
@ -72,7 +72,7 @@ jobs:
container:
image: fedora:latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- run: |
# Preparing and running unit tests #
dnf -y install git-core golang glibc-static git-core wget gcc make
@ -87,7 +87,7 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: '>=1.23'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- run: |
make all

View File

@ -1,40 +1,33 @@
version: "2"
run:
modules-download-mode: readonly
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: disable
timeout: 10m
deadline: 5m
linters:
default: all
enable-all: true
disable:
- gochecknoinits
- gochecknoglobals
- exhaustruct
- varnamelen
- gochecknoinits
- wrapcheck
- funlen
- depguard
- wrapcheck
- wsl
settings:
errcheck:
check-blank: false
exclude-functions:
- fmt:.*
nolintlint:
require-specific: true
revive:
rules:
- name: package-comments
disabled: true
exclusions:
paths:
- test/
- ".*_test.go"
- nolintlint
# generics disabled
- wastedassign
- rowserrcheck
# deprecated
- gomoddirectives
- tenv
linters-settings:
# typecheck:
# enabled: false
errcheck:
check-blank: false
exclude-functions:
- fmt:.*
nolintlint:
require-specific: true
issues:
max-issues-per-linter: 0
max-same-issues: 0
exclude-files:
- ".*_test.go"

View File

@ -16,12 +16,13 @@ jobs:
- fedora-all-aarch64
- epel-9-x86_64
- epel-9-aarch64
- epel-10-x86_64
- epel-10-aarch64
# temporary disabled since epel10 running go v1.23.1 and we need 1.23.3
# - epel-10-x86_64
# - epel-10-aarch64
- centos-stream-9-x86_64
- centos-stream-9-aarch64
- centos-stream-10-x86_64
- centos-stream-10-aarch64
# - centos-stream-10-x86_64
# - centos-stream-10-aarch64
actions:
post-upstream-clone:

View File

@ -88,7 +88,7 @@ install.tools: .install.ginkgo .install.bats .install.pre-commit .install.codesp
.PHONY: .install.golangci-lint
.install.golangci-lint:
VERSION=2.3.1 ./hack/install_golangci.sh
VERSION=1.64.4 ./hack/install_golangci.sh
.PHONY: .install.codespell
.install.codespell:
@ -112,7 +112,6 @@ test-unit: ## Run unit tests
--coverprofile coverprofile \
--output-dir ${COVERAGE_PATH} \
--succinct
$(GO) tool cover -html=${COVERAGE_PATH}/coverprofile -o ${COVERAGE_PATH}/coverage.html
$(GO) tool cover -func=${COVERAGE_PATH}/coverprofile > ${COVERAGE_PATH}/functions
cat ${COVERAGE_PATH}/functions | sed -n 's/\(total:\).*\([0-9][0-9].[0-9]\)/\1 \2/p'

View File

@ -24,7 +24,6 @@ import (
// App represents main application struct.
type App struct {
*tview.Application
infoBar *infobar.InfoBar
pages *tview.Pages
pods *pods.Pods
@ -87,8 +86,7 @@ func NewApp(name string, version string) *App {
app.system.SetConnectionAddFunc(app.config.Add)
app.system.SetConnectionRemoveFunc(app.config.Remove)
app.system.SetAppFocusHandler(func() {
app.SetFocus(app.system)
app.Application.SetFocus(app.system)
app.fastRefreshChan <- true
})
@ -104,38 +102,32 @@ func NewApp(name string, version string) *App {
// set app set focus
app.containers.SetAppFocusHandler(func() {
app.SetFocus(app.containers)
app.Application.SetFocus(app.containers)
app.fastRefreshChan <- true
})
app.pods.SetAppFocusHandler(func() {
app.SetFocus(app.pods)
app.Application.SetFocus(app.pods)
app.fastRefreshChan <- true
})
app.images.SetAppFocusHandler(func() {
app.SetFocus(app.images)
app.Application.SetFocus(app.images)
app.fastRefreshChan <- true
})
app.volumes.SetAppFocusHandler(func() {
app.SetFocus(app.volumes)
app.Application.SetFocus(app.volumes)
app.fastRefreshChan <- true
})
app.networks.SetAppFocusHandler(func() {
app.SetFocus(app.networks)
app.Application.SetFocus(app.networks)
app.fastRefreshChan <- true
})
app.secrets.SetAppFocusHandler(func() {
app.SetFocus(app.secrets)
app.Application.SetFocus(app.secrets)
app.fastRefreshChan <- true
})
@ -280,8 +272,7 @@ func (app *App) Run() error { //nolint:cyclop
// start fast refresh loop
go app.fastRefresh()
err := app.SetRoot(flex, true).SetFocus(app.system).EnableMouse(false).Run()
if err != nil {
if err := app.SetRoot(flex, true).SetFocus(app.system).EnableMouse(false).Run(); err != nil {
return err
}

View File

@ -6,7 +6,6 @@ import (
"github.com/containers/podman-tui/ui/style"
"github.com/rivo/tview"
"github.com/rs/zerolog/log"
)
func newMenu(menuItems [][]string) *tview.TextView {
@ -28,10 +27,7 @@ func newMenu(menuItems [][]string) *tview.TextView {
menuList = append(menuList, key+item)
}
_, err := fmt.Fprintf(menu, "%s", strings.Join(menuList, " "))
if err != nil {
log.Warn().Msgf("failed to create new menu: %s", err.Error())
}
fmt.Fprintf(menu, "%s", strings.Join(menuList, " "))
return menu
}

View File

@ -45,7 +45,7 @@ func (app *App) refresh() {
app.initInfoBar()
app.infoBar.UpdateConnStatus(connStatus)
app.Draw()
app.Application.Draw()
}
}
@ -97,7 +97,7 @@ func (app *App) fastRefresh() {
for {
refresh := <-app.fastRefreshChan
if refresh {
app.Draw()
app.Application.Draw()
}
}
}

View File

@ -88,21 +88,21 @@ func (app *App) switchToNextScreen() {
func (app *App) setPageFocus(page string) {
switch page {
case app.help.GetTitle():
app.SetFocus(app.help)
app.Application.SetFocus(app.help)
case app.system.GetTitle():
app.SetFocus(app.system)
app.Application.SetFocus(app.system)
case app.pods.GetTitle():
app.SetFocus(app.pods)
app.Application.SetFocus(app.pods)
case app.containers.GetTitle():
app.SetFocus(app.containers)
app.Application.SetFocus(app.containers)
case app.networks.GetTitle():
app.SetFocus(app.networks)
app.Application.SetFocus(app.networks)
case app.images.GetTitle():
app.SetFocus(app.images)
app.Application.SetFocus(app.images)
case app.volumes.GetTitle():
app.SetFocus(app.volumes)
app.Application.SetFocus(app.volumes)
case app.secrets.GetTitle():
app.SetFocus(app.secrets)
app.Application.SetFocus(app.secrets)
}
}

View File

@ -29,7 +29,7 @@ func Execute() {
cobra.CheckErr(rootCmd.Execute())
}
func run(cmd *cobra.Command, args []string) error { //nolint:cyclop
func run(cmd *cobra.Command, args []string) error { //nolint:cyclop,revive
var (
logOutput = io.Discard
runLog = fmt.Sprintf("starting %s version %s", appName, appVersion)
@ -54,12 +54,12 @@ func run(cmd *cobra.Command, args []string) error { //nolint:cyclop
return err
}
logFD, err := os.OpenFile(logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm) //nolint:gosec
logFD, err := os.OpenFile(logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm)
if err != nil {
return err
}
defer logFD.Close() //nolint:errcheck
defer logFD.Close()
logOutput = logFD
}
@ -95,11 +95,9 @@ func run(cmd *cobra.Command, args []string) error { //nolint:cyclop
}
app := app.NewApp(appName, appVersion)
err = app.Run()
if err != nil {
if err := app.Run(); err != nil {
if setSSHIdentityPassphrase {
os.Unsetenv("CONTAINER_PASSPHRASE") //nolint:errcheck
os.Unsetenv("CONTAINER_PASSPHRASE")
}
return err
@ -108,10 +106,7 @@ func run(cmd *cobra.Command, args []string) error { //nolint:cyclop
// unset CONTAINER_PASSPHRASE environment variable if we have set it
// after application exits
if setSSHIdentityPassphrase {
err := os.Unsetenv("CONTAINER_PASSPHRASE")
if err != nil {
log.Error().Msgf("failed to unset env var CONTAINER_PASSPHRASE: %s", err.Error())
}
os.Unsetenv("CONTAINER_PASSPHRASE")
}
return nil

View File

@ -7,7 +7,7 @@ import (
)
const (
appVersion = "1.8.0-dev"
appVersion = "1.7.0"
)
// versionCmd represents the version command.
@ -15,7 +15,7 @@ var versionCmd = &cobra.Command{
Use: "version",
Short: fmt.Sprintf("Display %s version and exit.\n", appName),
Long: fmt.Sprintf("Display %s version and exit.\n", appName),
Run: func(cmd *cobra.Command, args []string) {
Run: func(cmd *cobra.Command, args []string) { //nolint:revive
fmt.Printf("%s v%s\n", appName, appVersion) //nolint:forbidigo
},
}

View File

@ -2,6 +2,7 @@ package pconfig
import (
"slices"
"sort"
cconfig "github.com/containers/common/pkg/config"
"github.com/containers/podman-tui/config/utils"
@ -54,6 +55,8 @@ func (c *Config) RemoteConnections() []registry.Connection {
})
}
sort.Sort(utils.ConnectionListSortedName{rconn}) //nolint:govet
return rconn
}

View File

@ -20,13 +20,11 @@ func (c *Config) Add(name string, uri string, identity string) error {
Default: false,
}
err = c.add(name, conn)
if err != nil {
if err := c.add(name, conn); err != nil {
return err
}
err = c.write()
if err != nil {
if err := c.write(); err != nil {
return err
}

View File

@ -3,6 +3,7 @@ package tconfig
import (
"errors"
"os"
"sort"
"sync"
"github.com/containers/podman-tui/config/utils"
@ -53,14 +54,14 @@ func NewConfig() (*Config, error) {
newConfig := &Config{}
newConfig.Connection.Connections = make(map[string]RemoteConnection)
_, err = os.Stat(path)
if err == nil {
err := newConfig.readConfigFromFile(path)
if err != nil {
if _, err := os.Stat(path); err == nil {
if err := newConfig.readConfigFromFile(path); err != nil {
return nil, err
}
} else {
if !os.IsNotExist(err) {
return nil, err
}
} else if !os.IsNotExist(err) {
return nil, err
}
newConfig.addLocalHostIfEmptyConfig()
@ -68,6 +69,18 @@ func NewConfig() (*Config, error) {
return newConfig, nil
}
func (c *Config) addLocalHostIfEmptyConfig() {
if len(c.Connection.Connections) > 0 {
return
}
c.Connection.Connections = make(map[string]RemoteConnection)
c.Connection.Connections["localhost"] = RemoteConnection{
URI: utils.LocalNodeUnixSocket(),
Default: true,
}
}
// RemoteConnections returns list of available connections.
func (c *Config) RemoteConnections() []registry.Connection {
rconn := make([]registry.Connection, 0)
@ -84,17 +97,7 @@ func (c *Config) RemoteConnections() []registry.Connection {
})
}
sort.Sort(utils.ConnectionListSortedName{rconn}) //nolint:govet
return rconn
}
func (c *Config) addLocalHostIfEmptyConfig() {
if len(c.Connection.Connections) > 0 {
return
}
c.Connection.Connections = make(map[string]RemoteConnection)
c.Connection.Connections["localhost"] = RemoteConnection{
URI: utils.LocalNodeUnixSocket(),
Default: true,
}
}

View File

@ -10,36 +10,17 @@ import (
func (c *Config) SetDefaultConnection(name string) error {
log.Debug().Msgf("config: set %s as default connection", name)
err := c.setDef(name)
if err != nil {
if err := c.setDef(name); err != nil {
return err
}
err = c.write()
if err != nil {
if err := c.write(); err != nil {
return err
}
return c.reload()
}
func (c *Config) GetDefaultConnection() registry.Connection {
c.mu.Lock()
defer c.mu.Unlock()
for connName, conn := range c.Connection.Connections {
if conn.Default {
return registry.Connection{
Name: connName,
Identity: conn.Identity,
URI: conn.URI,
}
}
}
return registry.Connection{}
}
func (c *Config) setDef(name string) error {
c.mu.Lock()
defer c.mu.Unlock()
@ -56,3 +37,20 @@ func (c *Config) setDef(name string) error {
return utils.ErrConnectionNotFound
}
func (c *Config) GetDefaultConnection() registry.Connection {
c.mu.Lock()
defer c.mu.Unlock()
for connName, conn := range c.Connection.Connections {
if conn.Default {
return registry.Connection{
Name: connName,
Identity: conn.Identity,
URI: conn.URI,
}
}
}
return registry.Connection{}
}

View File

@ -16,7 +16,7 @@ func (c *Config) readConfigFromFile(path string) error {
c.mu.Lock()
defer c.mu.Unlock()
cfgFile, err := os.Open(path) //nolint:gosec
cfgFile, err := os.Open(path)
if err != nil {
return fmt.Errorf("config: %w open configuration %q", err, path)
}
@ -37,8 +37,7 @@ func (c *Config) reload() error {
return err
}
err = c.readConfigFromFile(path)
if err != nil {
if err := c.readConfigFromFile(path); err != nil {
return err
}

View File

@ -8,8 +8,7 @@ func (c *Config) Remove(name string) error {
c.remove(name)
err := c.write()
if err != nil {
if err := c.write(); err != nil {
return err
}

View File

@ -24,30 +24,23 @@ func (c *Config) write() error {
log.Debug().Msgf("config: write configuration file %q", path)
err = os.MkdirAll(filepath.Dir(path), 0o750) //nolint:mnd
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { //nolint:mnd
return err
}
configFile, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0o640) //nolint:mnd
if err != nil {
return err
}
configFile, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0o600) //nolint:mnd,gosec
if err != nil {
return err
}
defer func() {
err := configFile.Close()
if err != nil {
log.Error().Msgf("failed to close config file after write: %s", err.Error())
}
}()
defer configFile.Close()
jsonData, err := json.Marshal(c.Connection)
if err != nil {
return fmt.Errorf("config: configuration json marshal %w", err)
}
_, err = configFile.Write(jsonData)
if err != nil {
if _, err := configFile.Write(jsonData); err != nil {
return fmt.Errorf("config: %w write configuration %q", err, path)
}

View File

@ -34,8 +34,7 @@ func ValidateNewConnection(name string, dest string, identity string) (string, e
return "", ErrEmptyURIDestination
}
match, err := regexp.Match("^[A-Za-z][A-Za-z0-9+.-]*://", []byte(dest)) //nolint:mirror
if err != nil {
if match, err := regexp.Match("^[A-Za-z][A-Za-z0-9+.-]*://", []byte(dest)); err != nil { //nolint:mirror
return "", fmt.Errorf("%w invalid destition", err)
} else if !match {
dest = "ssh://" + dest
@ -49,8 +48,7 @@ func ValidateNewConnection(name string, dest string, identity string) (string, e
switch uri.Scheme {
case "ssh":
if uri.User.Username() == "" {
uri.User, err = getUserInfo(uri)
if err != nil {
if uri.User, err = getUserInfo(uri); err != nil {
return "", err
}
}
@ -69,8 +67,7 @@ func ValidateNewConnection(name string, dest string, identity string) (string, e
}
if uri.Path == "" || uri.Path == "/" {
uri.Path, err = getUDS(uri, connIdentity)
if err != nil {
if uri.Path, err = getUDS(uri, connIdentity); err != nil {
return "", err
}
}

14
config/utils/sort.go Normal file
View File

@ -0,0 +1,14 @@
package utils
import "github.com/containers/podman-tui/pdcs/registry"
type ConnSort []registry.Connection
func (a ConnSort) Len() int { return len(a) }
func (a ConnSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type ConnectionListSortedName struct{ ConnSort }
func (a ConnectionListSortedName) Less(i, j int) bool {
return a.ConnSort[i].Name < a.ConnSort[j].Name
}

View File

@ -91,7 +91,7 @@ func getUserInfo(uri *url.URL) (*url.Userinfo, error) {
}
// most of the codes are from https://github.com/containers/podman/blob/main/cmd/podman/system/connection/add.go.
func getUDS(uri *url.URL, iden string) (string, error) { //nolint:cyclop
func getUDS(uri *url.URL, iden string) (string, error) {
cfg, err := validateAndConfigure(uri, iden)
if err != nil {
return "", fmt.Errorf("%w failed to validate", err)
@ -102,24 +102,14 @@ func getUDS(uri *url.URL, iden string) (string, error) { //nolint:cyclop
return "", fmt.Errorf("%w failed to connect", err)
}
defer func() {
err := dial.Close()
if err != nil {
log.Error().Msgf("failed to close SSH tcp connection: %s", err.Error())
}
}()
defer dial.Close()
session, err := dial.NewSession()
if err != nil {
return "", fmt.Errorf("%w failed to create new ssh session on %q", err, uri.Host)
}
defer func() {
err := session.Close()
if err != nil {
log.Error().Msgf("failed to close SSH session: %s", err.Error())
}
}()
defer session.Close()
// Override podman binary for testing etc
podman := "podman"
@ -133,9 +123,7 @@ func getUDS(uri *url.URL, iden string) (string, error) { //nolint:cyclop
}
var info define.Info
err = json.Unmarshal(infoJSON, &info)
if err != nil {
if err := json.Unmarshal(infoJSON, &info); err != nil {
return "", fmt.Errorf("%w failed to parse 'podman info' results", err)
}
@ -179,7 +167,7 @@ func validateAndConfigure(uri *url.URL, iden string) (*ssh.ClientConfig, error)
if sock, found := os.LookupEnv("SSH_AUTH_SOCK"); found {
log.Debug().Msgf("config: Found SSH_AUTH_SOCK %q, ssh-agent signer enabled", sock)
c, err := net.Dial("unix", sock) //nolint:noctx
c, err := net.Dial("unix", sock)
if err != nil {
return nil, err
}
@ -245,12 +233,7 @@ func execRemoteCommand(dial *ssh.Client, run string) ([]byte, error) {
return nil, err
}
defer func() {
err := sess.Close()
if err != nil {
log.Error().Msgf("failed to close exec session: %s", err.Error())
}
}()
defer sess.Close()
var (
buffer bytes.Buffer
@ -260,8 +243,7 @@ func execRemoteCommand(dial *ssh.Client, run string) ([]byte, error) {
sess.Stdout = &buffer // output from client funneled into buffer
sess.Stderr = &bufferErr // err form client funneled into buffer
err = sess.Run(run)
if err != nil { // run the command on the ssh client
if err := sess.Run(run); err != nil { // run the command on the ssh client
return nil, fmt.Errorf("%w %s", err, bufferErr.String())
}

80
go.mod
View File

@ -3,34 +3,35 @@ module github.com/containers/podman-tui
go 1.23.3
require (
github.com/containers/buildah v1.41.3
github.com/containers/common v0.64.1
github.com/containers/podman/v5 v5.6.0
github.com/containers/storage v1.59.1
github.com/containers/buildah v1.40.1
github.com/containers/common v0.63.1
github.com/containers/podman/v5 v5.5.2
github.com/containers/storage v1.58.0
github.com/distribution/reference v0.6.0
github.com/docker/go-units v0.5.0
github.com/gdamore/tcell/v2 v2.9.0
github.com/gdamore/tcell/v2 v2.8.1
github.com/hashicorp/go-multierror v1.1.1
github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02
github.com/navidys/tvxwidgets v0.4.1
github.com/onsi/ginkgo/v2 v2.23.4
github.com/onsi/gomega v1.38.0
github.com/onsi/gomega v1.37.0
github.com/pkg/errors v0.9.1
github.com/rivo/tview v0.42.0
github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8
github.com/rs/zerolog v1.34.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1
golang.org/x/crypto v0.41.0
golang.org/x/crypto v0.40.0
)
require (
dario.cat/mergo v1.0.2 // indirect
dario.cat/mergo v1.0.1 // 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/Microsoft/hcsshim v0.13.0 // indirect
github.com/Microsoft/hcsshim v0.12.9 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/containerd/cgroups/v3 v3.0.5 // indirect
@ -40,7 +41,7 @@ require (
github.com/containerd/platforms v1.0.0-rc.1 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/containers/image/v5 v5.36.1 // indirect
github.com/containers/image/v5 v5.35.0 // indirect
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
github.com/containers/ocicrypt v1.2.1 // indirect
github.com/containers/psgo v1.9.0 // indirect
@ -49,15 +50,25 @@ require (
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/disiqueira/gotree/v3 v3.0.2 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v28.3.2+incompatible // indirect
github.com/docker/docker v28.1.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gdamore/encoding v1.0.1 // 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/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/errors v0.22.1 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/loads v0.22.0 // indirect
github.com/go-openapi/runtime v0.28.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/strfmt v0.23.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/godbus/dbus/v5 v5.1.1-0.20241109141217-c266b19b28e9 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@ -73,6 +84,7 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
@ -80,6 +92,7 @@ require (
github.com/kr/fs v0.1.0 // indirect
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
@ -87,6 +100,7 @@ require (
github.com/mattn/go-sqlite3 v1.14.28 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/sys/capability v0.4.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
@ -97,51 +111,55 @@ require (
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.4 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/cgroups v0.0.1 // 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.0 // indirect
github.com/opencontainers/runc v1.2.6 // 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/runtime-tools v0.9.1-0.20250303011046-260e151b8552 // indirect
github.com/opencontainers/selinux v1.12.0 // indirect
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
github.com/pkg/sftp v1.13.9 // indirect
github.com/proglottis/gpgme v0.1.4 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
github.com/sigstore/fulcio v1.6.6 // indirect
github.com/sigstore/protobuf-specs v0.4.1 // indirect
github.com/sigstore/sigstore v1.9.5 // indirect
github.com/sigstore/rekor v1.3.10 // indirect
github.com/sigstore/sigstore v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/smallstep/pkcs7 v0.1.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect
github.com/sylabs/sif/v2 v2.21.1 // indirect
github.com/tchap/go-patricia/v2 v2.3.3 // indirect
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
github.com/tchap/go-patricia/v2 v2.3.2 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/vbatts/tar-split v0.12.1 // indirect
github.com/vbauerster/mpb/v8 v8.10.2 // indirect
github.com/vbauerster/mpb/v8 v8.9.3 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/term v0.34.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/term v0.33.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.35.0 // indirect
golang.org/x/tools v0.34.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
google.golang.org/grpc v1.72.2 // indirect
google.golang.org/grpc v1.71.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
tags.cncf.io/container-device-interface v1.0.1 // indirect
)

211
go.sum
View File

@ -1,6 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg=
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0=
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 v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@ -8,12 +10,14 @@ 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/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
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/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
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=
@ -48,22 +52,22 @@ github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRcc
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
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/containers/buildah v1.41.3 h1:L6wyRXGR+Cejx+/LMsEryi1JmuwbAICZEje+tQ7ihMs=
github.com/containers/buildah v1.41.3/go.mod h1:vVIYC6f5gbPNfhprdMZh9lkOJzzM7lta0romUtBFSw0=
github.com/containers/common v0.64.1 h1:E8vSiL+B84/UCsyVSb70GoxY9cu+0bseLujm4EKF6GE=
github.com/containers/common v0.64.1/go.mod h1:CtfQNHoCAZqWeXMwdShcsxmMJSeGRgKKMqAwRKmWrHE=
github.com/containers/image/v5 v5.36.1 h1:6zpXBqR59UcAzoKpa/By5XekeqFV+htWYfr65+Cgjqo=
github.com/containers/image/v5 v5.36.1/go.mod h1:b4GMKH2z/5t6/09utbse2ZiLK/c72GuGLFdp7K69eA4=
github.com/containers/buildah v1.40.1 h1:RW+Fbelwblzg1mJfKfyGZPS4Nbc5QtT866fJ9pYFtYo=
github.com/containers/buildah v1.40.1/go.mod h1:1UCQBc3LZrT4u5R/u7igGgUQxeDlJmn/OyYDQ9mumFk=
github.com/containers/common v0.63.1 h1:6g02gbW34PaRVH4Heb2Pk11x0SdbQ+8AfeKKeQGqYBE=
github.com/containers/common v0.63.1/go.mod h1:+3GCotSqNdIqM3sPs152VvW7m5+Mg8Kk+PExT3G9hZw=
github.com/containers/image/v5 v5.35.0 h1:T1OeyWp3GjObt47bchwD9cqiaAm/u4O4R9hIWdrdrP8=
github.com/containers/image/v5 v5.35.0/go.mod h1:8vTsgb+1gKcBL7cnjyNOInhJQfTUQjJoO2WWkKDoebM=
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/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpVhSmM=
github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ=
github.com/containers/podman/v5 v5.6.0 h1:tBs3B4RJHeCJsYPK90DkQYSCupa0vau/8JmBNH/Kz9k=
github.com/containers/podman/v5 v5.6.0/go.mod h1:Qxcq3VrAbVqdw7C5MPtGoGxTNpfnJFACL5+flxNgf/Y=
github.com/containers/podman/v5 v5.5.2 h1:H9C6hRs+Aa9g5x/wApHoVqT0fxFr0DH0tgs1h1/ktHc=
github.com/containers/podman/v5 v5.5.2/go.mod h1:mxncFv2XqD6ZlXZnBk4JpGmQfkx1+k7KyuBTy1d4Xfc=
github.com/containers/psgo v1.9.0 h1:eJ74jzSaCHnWt26OlKZROSyUyRcGDf+gYBdXnxrMW4g=
github.com/containers/psgo v1.9.0/go.mod h1:0YoluUm43Mz2UnBIh1P+6V6NWcbpTL5uRtXyOcH0B5A=
github.com/containers/storage v1.59.1 h1:11Zu68MXsEQGBBd+GadPrHPpWeqjKS8hJDGiAHgIqDs=
github.com/containers/storage v1.59.1/go.mod h1:KoAYHnAjP3/cTsRS+mmWZGkufSY2GACiKQ4V3ZLQnR0=
github.com/containers/storage v1.58.0 h1:Q7SyyCCjqgT3wYNgRNIL8o/wUS92heIj2/cc8Sewvcc=
github.com/containers/storage v1.58.0/go.mod h1:w7Jl6oG+OpeLGLzlLyOZPkmUso40kjpzgrHUk5tyBlo=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09 h1:OoRAFlvDGCUqDLampLQjk0yeeSGdF9zzst/3G9IkBbc=
github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09/go.mod h1:m2r/smMKsKwgMSAoFKHaa68ImdCSNuKE1MxvQ64xuCQ=
@ -82,12 +86,12 @@ github.com/disiqueira/gotree/v3 v3.0.2 h1:ik5iuLQQoufZBNPY518dXhiO5056hyNBIK9lWh
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 v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS3hP2huFsY=
github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A=
github.com/docker/cli v28.0.4+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.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA=
github.com/docker/docker v28.3.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I=
github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
@ -105,17 +109,39 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
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/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=
github.com/gdamore/tcell/v2 v2.9.0 h1:N6t+eqK7/xwtRPwxzs1PXeRWnm0H9l02CrgJ7DLn1ys=
github.com/gdamore/tcell/v2 v2.9.0/go.mod h1:8/ZoqM9rxzYphT9tH/9LnunhV9oPBqwS8WHGYm5nrmo=
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04=
github.com/gdamore/tcell/v2 v2.8.1 h1:KPNxyqclpWpWQlPLx6Xui1pMk8S+7+R37h3g07997NU=
github.com/gdamore/tcell/v2 v2.8.1/go.mod h1:bj8ori1BG3OYMjmb3IklZVWfZUJ1UBQt9JXrOCOhGWw=
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.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
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/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
github.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU=
github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=
github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=
github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
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=
@ -148,6 +174,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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=
@ -181,6 +208,8 @@ 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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
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.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
@ -201,6 +230,8 @@ github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec h1:2tTW6cDth2T
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/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
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=
@ -209,6 +240,7 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.13/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-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
@ -217,6 +249,8 @@ 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.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPnkFiU=
github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
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/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
@ -246,24 +280,28 @@ github.com/navidys/tvxwidgets v0.4.1 h1:abITHN2R0AN1G5XYBDCeTBfR+E1FRsDKN5j1FKI8
github.com/navidys/tvxwidgets v0.4.1/go.mod h1:VJRhOCt9q4cuqN1UebaWRAc0MG4pmpHMBWL3tRSoqdI=
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY=
github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o=
github.com/opencontainers/cgroups v0.0.4 h1:XVj8P/IHVms/j+7eh8ggdkTLAxjz84ZzuFyGoE28DR4=
github.com/opencontainers/cgroups v0.0.4/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/opencontainers/cgroups v0.0.1 h1:MXjMkkFpKv6kpuirUa4USFBas573sSAY082B4CiHEVA=
github.com/opencontainers/cgroups v0.0.1/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs=
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.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runc v1.3.0 h1:cvP7xbEvD0QQAs0nZKLzkVog2OPZhI/V2w3WmTmUSXI=
github.com/opencontainers/runc v1.3.0/go.mod h1:9wbWt42gV+KRxKRVVugNP6D5+PQciRbenB4fLVsqGPs=
github.com/opencontainers/runc v1.2.6 h1:P7Hqg40bsMvQGCS4S7DJYhUZOISMLJOB2iGX5COWiPk=
github.com/opencontainers/runc v1.2.6/go.mod h1:dOQeFo29xZKBNeRBI0B19mJtfHv68YgCTh1X+YphA+4=
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/runtime-tools v0.9.1-0.20250303011046-260e151b8552 h1:CkXngT0nixZqQUPDVfwVs3GiuhfTqCMk0V+OoHpxIvA=
github.com/opencontainers/runtime-tools v0.9.1-0.20250303011046-260e151b8552/go.mod h1:T487Kf80NeF2i0OyVXHiylg217e0buz8pQsa0T791RA=
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f h1:/UDgs8FGMqwnHagNDPGOlts35QkhAZ8by3DR7nMih7M=
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
@ -275,18 +313,19 @@ github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/proglottis/gpgme v0.1.4 h1:3nE7YNA70o2aLjcg63tXMOhPD7bplfE5CBdV+hLAm2M=
github.com/proglottis/gpgme v0.1.4/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_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
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/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
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/tview v0.42.0 h1:b/ftp+RxtDsHSaynXTbJb+/n/BxDEi+W3UfF5jILK6c=
github.com/rivo/tview v0.42.0/go.mod h1:cSfIYfhpSGCjp3r/ECJb+GKS7cGJnqV8vfjQPwoXyfY=
github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 h1:xe+mmCnDN82KhC010l3NfYlA8ZbOuzbXAzSYBa6wbMc=
github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8/go.mod h1:WIfMkQNY+oq/mWwtsjOYHIZBuwthioY2srOmljJkTnk=
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=
@ -295,8 +334,8 @@ github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
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/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY=
github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc=
@ -307,8 +346,10 @@ github.com/sigstore/fulcio v1.6.6 h1:XaMYX6TNT+8n7Npe8D94nyZ7/ERjEsNGFC+REdi/wzw
github.com/sigstore/fulcio v1.6.6/go.mod h1:BhQ22lwaebDgIxVBEYOOqLRcN5+xOV+C9bh/GUXRhOk=
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/sigstore/rekor v1.3.10 h1:/mSvRo4MZ/59ECIlARhyykAlQlkmeAQpvBPlmJtZOCU=
github.com/sigstore/rekor v1.3.10/go.mod h1:JvryKJ40O0XA48MdzYUPu0y4fyvqt0C4iSY7ri9iu3A=
github.com/sigstore/sigstore v1.9.3 h1:y2qlTj+vh+Or3ictKuR3JUFawZPdDxAjrWkeFhon0OQ=
github.com/sigstore/sigstore v1.9.3/go.mod h1:VwYkiw0G0dRtwL25KSs04hCyVFF6CYMd/qvNeYrl7EQ=
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.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
@ -333,16 +374,18 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/sylabs/sif/v2 v2.21.1 h1:GZ0b5//AFAqJEChd8wHV/uSKx/l1iuGYwjR8nx+4wPI=
github.com/sylabs/sif/v2 v2.21.1/go.mod h1:YoqEGQnb5x/ItV653bawXHZJOXQaEWpGwHsSD3YePJI=
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/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tchap/go-patricia/v2 v2.3.2 h1:xTHFutuitO2zqKAQ5rCROYgUb7Or/+IC3fts9/Yc7nM=
github.com/tchap/go-patricia/v2 v2.3.2/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
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/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/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/vbauerster/mpb/v8 v8.9.3 h1:PnMeF+sMvYv9u23l6DO6Q3+Mdj408mjLRXIzmUmU2Z8=
github.com/vbauerster/mpb/v8 v8.9.3/go.mod h1:hxS8Hz4C6ijnppDSIX6LjG8FYJSoPo9iIOcE53Zik0c=
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=
@ -352,34 +395,32 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ
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.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
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.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8=
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.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg=
go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY=
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.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
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=
@ -389,8 +430,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
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.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -418,8 +459,8 @@ 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.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -440,6 +481,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/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-20210309074719-68d13333faf2/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=
@ -455,10 +497,13 @@ 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.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.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
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-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/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=
@ -466,10 +511,12 @@ 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.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
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.6/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=
@ -477,8 +524,8 @@ 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.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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
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=
@ -493,8 +540,8 @@ 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.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
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=
@ -513,8 +560,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -538,7 +585,7 @@ gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
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=

View File

@ -254,8 +254,7 @@ func Create(opts CreateOptions, run bool) ([]string, string, error) { //nolint:c
}
// add healthcheck options
err = containerHealthOptions(&createOptions, opts)
if err != nil {
if err := containerHealthOptions(&createOptions, opts); err != nil {
return warningResponse, containerID, err
}
@ -390,9 +389,7 @@ func Create(opts CreateOptions, run bool) ([]string, string, error) { //nolint:c
// generate spec
s := specgen.NewSpecGenerator(opts.Name, false)
err = specgenutil.FillOutSpecGen(s, &createOptions, nil)
if err != nil {
if err := specgenutil.FillOutSpecGen(s, &createOptions, nil); err != nil {
return warningResponse, containerID, err
}
@ -406,8 +403,7 @@ func Create(opts CreateOptions, run bool) ([]string, string, error) { //nolint:c
}
// validate spec
err = s.Validate()
if err != nil {
if err := s.Validate(); err != nil {
return warningResponse, containerID, err
}

View File

@ -95,8 +95,7 @@ func Exec(sessionID string, opts ExecOption) { //nolint:cyclop
conn, err := registry.GetConnection()
if err != nil {
_, err := opts.OutputStream.Write([]byte(fmt.Sprintf("%v", err))) //nolint:staticcheck
if err != nil {
if _, err := opts.OutputStream.Write([]byte(fmt.Sprintf("%v", err))); err != nil {
log.Error().Msgf("%v", err)
}
@ -117,12 +116,10 @@ func Exec(sessionID string, opts ExecOption) { //nolint:cyclop
execStartAttachOpts.InputStream = opts.InputStream
}
err := containers.ExecStartAndAttach(conn, sessionID, execStartAttachOpts)
if err != nil {
if err := containers.ExecStartAndAttach(conn, sessionID, execStartAttachOpts); err != nil {
log.Error().Msgf("pdcs: podman session (%s) exec error %v", sessionID, err)
_, err := opts.OutputStream.Write([]byte(fmt.Sprintf("%v", err))) //nolint:staticcheck
if err != nil {
if _, err := opts.OutputStream.Write([]byte(fmt.Sprintf("%v", err))); err != nil {
log.Error().Msgf("%v", err)
}
}
@ -132,12 +129,10 @@ func Exec(sessionID string, opts ExecOption) { //nolint:cyclop
return
}
err = containers.ExecStart(conn, sessionID, &containers.ExecStartOptions{})
if err != nil {
if err := containers.ExecStart(conn, sessionID, &containers.ExecStartOptions{}); err != nil {
log.Error().Msgf("pdcs: podman session (%s) exec error %v", sessionID, err)
_, err := opts.OutputStream.Write([]byte(fmt.Sprintf("%v", err))) //nolint:staticcheck
if err != nil {
if _, err := opts.OutputStream.Write([]byte(fmt.Sprintf("%v", err))); err != nil {
log.Error().Msgf("%v", err)
}
@ -146,23 +141,19 @@ func Exec(sessionID string, opts ExecOption) { //nolint:cyclop
log.Debug().Msgf("pdcs: podman session (%s) exec finished successfully", sessionID)
_, err = opts.OutputStream.Write([]byte(fmt.Sprintf("session_id ...... : %s\r\n", sessionID))) //nolint:staticcheck
if err != nil {
if _, err := opts.OutputStream.Write([]byte(fmt.Sprintf("session_id ...... : %s\r\n", sessionID))); err != nil {
log.Error().Msgf("%v", err)
}
_, err = opts.OutputStream.Write([]byte(fmt.Sprintf("exec_mode ...... : %s\r\n", "detached"))) //nolint:staticcheck
if err != nil {
if _, err := opts.OutputStream.Write([]byte(fmt.Sprintf("exec_mode ...... : %s\r\n", "detached"))); err != nil {
log.Error().Msgf("%v", err)
}
_, err = opts.OutputStream.Write([]byte(fmt.Sprintf("exec_command .... : %s\r\n", strings.Join(opts.Cmd, " ")))) //nolint:staticcheck,lll
if err != nil {
if _, err := opts.OutputStream.Write([]byte(fmt.Sprintf("exec_command .... : %s\r\n", strings.Join(opts.Cmd, " ")))); err != nil { //nolint:lll
log.Error().Msgf("%v", err)
}
_, err = opts.OutputStream.Write([]byte(fmt.Sprintf("exec_status ..... : %s\r\n", "OK"))) //nolint:staticcheck
if err != nil {
if _, err := opts.OutputStream.Write([]byte(fmt.Sprintf("exec_status ..... : %s\r\n", "OK"))); err != nil {
log.Error().Msgf("%v", err)
}
}
@ -173,7 +164,7 @@ func genExecCreateConfig(opts ExecOption) (*handlers.ExecCreateConfig, error) {
createCfg := &handlers.ExecCreateConfig{}
createCfg.Cmd = opts.Cmd
createCfg.Tty = opts.Tty
createCfg.Detach = opts.Detach //nolint:staticcheck
createCfg.Detach = opts.Detach
createCfg.WorkingDir = opts.WorkDir
createCfg.User = opts.User

View File

@ -1,6 +1,8 @@
package containers
import (
"sort"
"github.com/containers/podman-tui/pdcs/registry"
"github.com/containers/podman/v5/pkg/bindings/containers"
"github.com/containers/podman/v5/pkg/domain/entities"
@ -21,7 +23,20 @@ func List() ([]entities.ListContainer, error) {
return nil, err
}
sort.Sort(containerListSortedName{response})
log.Debug().Msgf("pdcs: %v", response)
return response, nil
}
type lprSort []entities.ListContainer
func (a lprSort) Len() int { return len(a) }
func (a lprSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type containerListSortedName struct{ lprSort }
func (a containerListSortedName) Less(i, j int) bool {
return a.lprSort[i].Names[0] < a.lprSort[j].Names[0]
}

View File

@ -44,7 +44,6 @@ func Logs(id string) ([]string, error) {
if err != nil {
return logs, err
}
done <- true
// logs = append(logs, <-logout)
// logs = append(logs, <-logerr)

View File

@ -43,9 +43,9 @@ type conReporter struct {
}
func (con conReporter) ports() string {
if len(con.Ports) < 1 {
if len(con.ListContainer.Ports) < 1 {
return ""
}
return utils.PortsToString(con.Ports)
return utils.PortsToString(con.ListContainer.Ports)
}

View File

@ -20,13 +20,11 @@ func RunInitAttach(cntID string, stdin io.Reader, stdout io.Writer, attachReady
attachOptions := new(containers.AttachOptions)
attachOptions.WithDetachKeys(detachKey)
err = containers.ContainerInit(conn, cntID, new(containers.InitOptions))
if err != nil {
if err := containers.ContainerInit(conn, cntID, new(containers.InitOptions)); err != nil {
return err
}
err = containers.Attach(conn, cntID, stdin, stdout, stdout, attachReady, attachOptions)
if err != nil {
if err := containers.Attach(conn, cntID, stdin, stdout, stdout, attachReady, attachOptions); err != nil {
return err
}

View File

@ -40,12 +40,7 @@ func Import(opts ImageImportOptions) (string, error) {
return "", err
}
defer func() {
err := tarFile.Close()
if err != nil {
log.Error().Msgf("failed to close tar file: %s", err.Error())
}
}()
defer tarFile.Close()
reader = bufio.NewReader(tarFile)
}

View File

@ -2,10 +2,11 @@ package images
import (
"fmt"
"sort"
"github.com/containers/podman-tui/pdcs/registry"
"github.com/containers/podman/v5/pkg/bindings/images"
"github.com/containers/podman/v5/pkg/domain/entities/types"
"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/distribution/reference"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
@ -29,7 +30,7 @@ func List() ([]ImageListReporter, error) {
return nil, err
}
imgs, err := imageListReporter(response)
imgs, err := sortImages(response)
if err != nil {
return nil, err
}
@ -41,13 +42,12 @@ func List() ([]ImageListReporter, error) {
// ImageListReporter image list report.
type ImageListReporter struct {
types.ImageSummary
Repository string `json:"repository,omitempty"`
Tag string `json:"tag,omitempty"`
entities.ImageSummary
}
func imageListReporter(imageS []*types.ImageSummary) ([]ImageListReporter, error) {
func sortImages(imageS []*entities.ImageSummary) ([]ImageListReporter, error) {
imgs := make([]ImageListReporter, 0, len(imageS))
var err error
@ -88,9 +88,17 @@ func imageListReporter(imageS []*types.ImageSummary) ([]ImageListReporter, error
}
}
sort.Slice(imgs, sortFunc(imgs))
return imgs, err
}
func sortFunc(data []ImageListReporter) func(i, j int) bool {
return func(i, j int) bool {
return data[i].Repository < data[j].Repository
}
}
func tokenRepoTag(ref string) (string, string, error) {
tagRef := fmt.Sprintf("%s:%s", noneTag, noneTag)
if ref == tagRef {

View File

@ -44,13 +44,7 @@ func Save(imageID string, opts ImageSaveOptions) error { //nolint:cyclop
return err
}
defer func() {
err := outputFile.Close()
if err != nil {
log.Error().Msgf("failed to close image save output file: %s", err.Error())
}
}()
defer outputFile.Close()
cancelChan := make(chan bool, 1)
writerChan := make(chan []byte, 1024) //nolint:mnd
@ -68,8 +62,7 @@ func Save(imageID string, opts ImageSaveOptions) error { //nolint:cyclop
return
case data := <-writerChan:
_, err := outputFile.Write(data)
if err != nil {
if _, err := outputFile.Write(data); err != nil {
outputErrors = append(outputErrors, err)
}
}
@ -84,7 +77,6 @@ func Save(imageID string, opts ImageSaveOptions) error { //nolint:cyclop
saveOpts.WithOciAcceptUncompressedLayers(opts.OciAcceptUncompressedLayers)
err = images.Export(conn, []string{imageID}, outputWriter, &saveOpts)
cancelChan <- true
if err != nil {

View File

@ -1,6 +1,8 @@
package networks
import (
"sort"
"github.com/containers/common/libnetwork/types"
"github.com/containers/podman-tui/pdcs/registry"
"github.com/containers/podman/v5/pkg/bindings/network"
@ -8,10 +10,10 @@ import (
)
// List returns list of podman networks.
func List() ([]types.Network, error) {
func List() ([][]string, error) {
log.Debug().Msg("pdcs: podman network ls")
report := make([]types.Network, 0)
report := make([][]string, 0)
conn, err := registry.GetConnection()
if err != nil {
@ -23,7 +25,26 @@ func List() ([]types.Network, error) {
return report, err
}
log.Debug().Msgf("pdcs: %v", response)
sort.Sort(netListSortedName{response})
return response, nil
for _, item := range response {
report = append(report, []string{
item.ID,
item.Name,
item.Driver,
})
}
log.Debug().Msgf("pdcs: %v", report)
return report, nil
}
type lprSort []types.Network
func (a lprSort) Len() int { return len(a) }
func (a lprSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type netListSortedName struct{ lprSort }
func (a netListSortedName) Less(i, j int) bool { return a.lprSort[i].Name < a.lprSort[j].Name }

View File

@ -224,8 +224,7 @@ func Create(opts CreateOptions) error { //nolint:cyclop,gocognit,gocyclo
}
// validate spec
err = podSpec.Validate()
if err != nil {
if err := podSpec.Validate(); err != nil {
errList = append(errList, err)
}

View File

@ -1,6 +1,8 @@
package pods
import (
"sort"
"github.com/containers/podman-tui/pdcs/registry"
"github.com/containers/podman/v5/pkg/bindings/pods"
"github.com/containers/podman/v5/pkg/domain/entities"
@ -21,7 +23,18 @@ func List() ([]*entities.ListPodsReport, error) {
return nil, err
}
sort.Sort(podPsSortedName{response})
log.Debug().Msgf("pdcs: %v", response)
return response, nil
}
type lprSort []*entities.ListPodsReport
func (a lprSort) Len() int { return len(a) }
func (a lprSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type podPsSortedName struct{ lprSort }
func (a podPsSortedName) Less(i, j int) bool { return a.lprSort[i].Name < a.lprSort[j].Name }

View File

@ -54,7 +54,6 @@ func sortStats(podSReport []*entities.PodStatsReport, sortBy int) []StatReporter
for _, item := range podSReport {
var reporterItem StatReporter
reporterItem.Pod = item.Pod
reporterItem.CID = item.CID
reporterItem.Name = item.Name

View File

@ -49,17 +49,14 @@ func SetConnectionStatus(status ConnStatus) {
pdcsRegistry.mu.Lock()
defer pdcsRegistry.mu.Unlock()
pdcsRegistry.connection.Status = status
}
// SetConnection sets registry connection.
func SetConnection(connection Connection) {
log.Debug().Msgf("pdcs: registry set connection %v", connection)
pdcsRegistry.mu.Lock()
defer pdcsRegistry.mu.Unlock()
pdcsRegistry.connection = connection
pdcsRegistry.connectionIsSet = true
}

View File

@ -77,13 +77,7 @@ func Create(opts *SecretCreateOptions) error { //nolint:cyclop
return err
}
defer func() {
err := file.Close()
if err != nil {
log.Error().Msgf("failed to close secret file input: %s", err.Error())
}
}()
defer file.Close()
reader = file
}
@ -96,8 +90,7 @@ func Create(opts *SecretCreateOptions) error { //nolint:cyclop
return err
}
_, err = secrets.Create(conn, reader, createOpts)
if err != nil {
if _, err := secrets.Create(conn, reader, createOpts); err != nil {
return err
}

View File

@ -1,6 +1,8 @@
package volumes
import (
"sort"
"github.com/containers/podman-tui/pdcs/registry"
"github.com/containers/podman/v5/pkg/bindings/volumes"
"github.com/containers/podman/v5/pkg/domain/entities"
@ -21,7 +23,17 @@ func List() ([]*entities.VolumeListReport, error) {
return nil, err
}
sort.Sort(volumeListSortedName{response})
log.Debug().Msgf("pdcs: %v", response)
return response, nil
}
type lprSort []*entities.VolumeListReport
func (a lprSort) Len() int { return len(a) }
func (a lprSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type volumeListSortedName struct{ lprSort }
func (a volumeListSortedName) Less(i, j int) bool { return a.lprSort[i].Name < a.lprSort[j].Name }

View File

@ -15,8 +15,8 @@
%global git0 https://%{import_path}
Name: podman-tui
Version: 1.8.0
Release: dev.1%{?dist}
Version: 1.7.0
Release: 1%{?dist}
Summary: Podman Terminal User Interface
License: ASL 2.0
URL: %{git0}
@ -60,8 +60,6 @@ install -p ./bin/%{name} %{buildroot}%{_bindir}
%{_bindir}/%{name}
%changelog
* Fri Jul 11 2025 Navid Yaghoobi <navidys@fedoraproject.org> 1.8.0-dev-1
* Fri Jul 11 2025 Navid Yaghoobi <navidys@fedoraproject.org> 1.7.0-1
- Bump golang.org/x/crypto from 0.39.0 to 0.40.0
- Remove unused BUILDTAGS

View File

@ -15,10 +15,9 @@ type apiConn struct {
// ConnStatus returns connection status.
func (conn *apiConn) ConnStatus() (registry.ConnStatus, string) {
var (
status registry.ConnStatus
message string
)
var status registry.ConnStatus
message := ""
conn.mu.Lock()
status = conn.connStaus

View File

@ -40,8 +40,7 @@ func (engine *Engine) startEventStreamer() {
go engine.eventReader()
go func() {
err := sysinfo.Events(engine.sysEvents.eventChan, engine.sysEvents.eventCancelChan)
if err != nil {
if err := sysinfo.Events(engine.sysEvents.eventChan, engine.sysEvents.eventCancelChan); err != nil {
log.Error().Msgf("health check: event streamer %v", err)
engine.sysEvents.cancelChan <- true
@ -56,7 +55,6 @@ func (engine *Engine) eventReader() {
select {
case <-engine.sysEvents.cancelChan:
log.Debug().Msg("health check: event reader stopped")
engine.sysEvents.eventCancelChan <- true
engine.sysEvents.mu.Lock()
@ -96,10 +94,10 @@ func (engine *Engine) GetEventMessages() []string {
// HasNewEvent returns true if there is new event added to event buffer.
func (engine *Engine) HasNewEvent() bool {
hasEvent := false
engine.sysEvents.mu.Lock()
hasEvent := engine.sysEvents.hasNewEvent
hasEvent = engine.sysEvents.hasNewEvent
engine.sysEvents.mu.Unlock()
return hasEvent
@ -107,7 +105,6 @@ func (engine *Engine) HasNewEvent() bool {
func (engine *Engine) addEventMessage(msg string) {
engine.sysEvents.mu.Lock()
if len(engine.sysEvents.messageBuffer) == engine.sysEvents.messageBufferSize {
// empty first 10 entries
engine.sysEvents.messageBuffer = engine.sysEvents.messageBuffer[20:]

View File

@ -29,6 +29,7 @@ load helpers_tui
@test "image save" {
podman image pull docker.io/library/busybox || echo done
image_index=$(podman image ls --sort repository --noheading | nl -v 0 | grep 'busybox ' | awk '{print $1}')
[ -f "${TEST_IMAGE_SAVE_PATH}" ] && /bin/rm -rf $TEST_IMAGE_SAVE_PATH
# switch to images view
@ -39,7 +40,7 @@ load helpers_tui
# go to save button and press enter
podman_tui_set_view "images"
podman_tui_select_item 0
podman_tui_select_item $image_index
podman_tui_select_image_cmd "save"
podman_tui_send_inputs $TEST_IMAGE_SAVE_PATH "Tab"
@ -103,12 +104,14 @@ load helpers_tui
}
@test "image diff" {
image_index=$(podman image ls --sort repository --noheading | nl -v 0 | grep 'busybox ' | awk '{print $1}')
# switch to images view
# select busybox image from list
# select diff command from image commands dialog
# close busybox image diff result message dialog
podman_tui_set_view "images"
podman_tui_select_item 2
podman_tui_select_item $image_index
podman_tui_select_image_cmd "diff"
sleep $TEST_TIMEOUT_LOW
podman_tui_send_inputs "Tab" "Enter"
@ -118,12 +121,14 @@ load helpers_tui
}
@test "image history" {
image_index=$(podman image ls --sort repository --noheading | nl -v 0 | grep 'busybox ' | awk '{print $1}')
image_id=$(podman image ls --sort repository --filter "reference=docker.io/library/busybox" --format "{{ .ID }}")
# switch to images view
# select busybox image from list
# select history command from image commands dialog
# close busybox image history result message dialog
podman_tui_set_view "images"
podman_tui_select_item 2
podman_tui_select_item $image_index
podman_tui_select_image_cmd "history"
sleep $TEST_TIMEOUT_LOW
podman_tui_send_inputs "Tab" "Enter"
@ -133,6 +138,7 @@ load helpers_tui
}
@test "image inspect" {
image_index=$(podman image ls --sort repository --noheading | nl -v 0 | grep 'busybox ' | awk '{print $1}')
image_id=$(podman image ls --sort repository --noheading | nl -v 0 | grep 'busybox ' | awk '{print $4}')
# switch to images view
@ -140,7 +146,7 @@ load helpers_tui
# select inspect command from image commands dialog
# close busybox image inspect result message dialog
podman_tui_set_view "images"
podman_tui_select_item 2
podman_tui_select_item $image_index
podman_tui_select_image_cmd "inspect"
sleep $TEST_TIMEOUT_LOW
podman_tui_send_inputs "Enter"
@ -150,11 +156,13 @@ load helpers_tui
}
@test "image tag" {
busybox_index=$(podman image ls --sort repository --noheading | nl -v 0 | grep 'busybox ' | awk '{print $1}')
# switch to images view
# select busybox image from list
# select tag command from image commands dialog
podman_tui_set_view "images"
podman_tui_select_item 2
podman_tui_select_item $busyboxIndex
podman_tui_select_image_cmd "tag"
podman_tui_send_inputs "$TEST_IMAGE_TAG_NAME" "Tab" "Tab" "Enter"
sleep $TEST_TIMEOUT_LOW
@ -164,12 +172,14 @@ load helpers_tui
}
@test "image untag" {
busybox_tagindex=$(podman image ls --sort repository --noheading | nl -v 0 | grep "$TEST_IMAGE_TAG_NAME " | awk '{print $1}')
# switch to images view
# select busybox image from list
# select untag command from image commands dialog
# press "Tab" 2 times and "Enter" to untag busybox image
podman_tui_set_view "images"
podman_tui_select_item 2
podman_tui_select_item $busybox_tagindex
sleep $TEST_TIMEOUT_LOW
podman_tui_select_image_cmd "untag"
podman_tui_send_inputs "Tab" "Tab" "Enter"
@ -183,6 +193,7 @@ load helpers_tui
@test "image remove" {
run_helper podman image ls --format "'{{ .Repository }}'"
before="$output"
untagged_image=$(podman image ls --sort repository --noheading | nl -v 0 | grep '<none> ' | awk '{print $1}')
# switch to images view
# select <none> image from list
@ -191,7 +202,7 @@ load helpers_tui
# wait for image removal operation
# close image removal result message dialog
podman_tui_set_view "images"
podman_tui_select_item 2
podman_tui_select_item $untagged_image
podman_tui_select_image_cmd "remove"
podman_tui_send_inputs "Enter"
sleep $TEST_TIMEOUT_LOW

View File

@ -25,12 +25,14 @@ load helpers_tui
}
@test "volume inspect" {
vol_index=$(podman volume ls -q | nl -v 0 | grep "$TEST_VOLUME_NAME" | awk '{print $1}')
# switch to volumes view
# select test volume from list
# select inspect command from volume commands dialog
# close volume inspect result message dialog
podman_tui_set_view "volumes"
podman_tui_select_item 0
podman_tui_select_item $vol_index
podman_tui_select_volume_cmd "inspect"
sleep $TEST_TIMEOUT_LOW
podman_tui_send_inputs "Enter"
@ -41,11 +43,13 @@ load helpers_tui
}
@test "volume remove" {
vol_index=$(podman volume ls -q | nl -v 0 | grep "$TEST_VOLUME_NAME" | awk '{print $1}')
# switch to volumes view
# select test volume from list
# select remove command from volume commands dialog
podman_tui_set_view "volumes"
podman_tui_select_item 0
podman_tui_select_item $vol_index
podman_tui_select_volume_cmd "remove"
podman_tui_send_inputs "Enter"
sleep $TEST_TIMEOUT_LOW

View File

@ -17,7 +17,6 @@ load helpers_tui
# select connect button
podman_tui_set_view "networks"
podman_tui_select_item 1
podman_tui_select_network_cmd "connect"
sleep $TEST_TIMEOUT_LOW
podman_tui_send_inputs "Tab"
@ -39,7 +38,6 @@ load helpers_tui
# select disconnect button
podman_tui_set_view "networks"
podman_tui_select_item 1
podman_tui_select_network_cmd "disconnect"
sleep $TEST_TIMEOUT_LOW
podman_tui_send_inputs "Tab" "Tab" "Enter"
@ -70,12 +68,14 @@ load helpers_tui
}
@test "network inspect" {
net_index=$(podman network ls -q | nl -v 0 | grep "$TEST_NETWORK_NAME" | awk '{print $1}')
# switch to networks view
# select test network from list
# select inspect command from network commands dialog
# close network inspect result message dialog
podman_tui_set_view "networks"
podman_tui_select_item 1
podman_tui_select_item $net_index
podman_tui_select_network_cmd "inspect"
sleep $TEST_TIMEOUT_LOW
podman_tui_send_inputs "Enter"
@ -86,11 +86,13 @@ load helpers_tui
}
@test "network remove" {
net_index=$(podman network ls -q | nl -v 0 | grep "$TEST_NETWORK_NAME" | awk '{print $1}')
# switch to networks view
# select test network from list
# select remove command from network commands dialog
podman_tui_set_view "networks"
podman_tui_select_item 1
podman_tui_select_item $net_index
podman_tui_select_network_cmd "remove"
podman_tui_send_inputs "Enter"
sleep $TEST_TIMEOUT_LOW

View File

@ -43,6 +43,7 @@ load helpers_tui
podman image pull pause:3.5 || echo done
podman network create $TEST_POD_NETWORK_NAME || echo done
net_index=$(podman network ls -q | nl -v 1 | grep "$TEST_POD_NETWORK_NAME" | awk '{print $1}')
# switch to pods view
# select create command from pod commands dialog
# fillout name field
@ -58,7 +59,7 @@ load helpers_tui
podman_tui_send_inputs $TEST_POD_NAME "Tab" "Tab" $TEST_LABEL
podman_tui_send_inputs "Tab" "Tab" "Tab" "Down" "Down" "Down" "Tab"
podman_tui_send_inputs "Tab" "Tab" "Tab" "Tab" "Down"
podman_tui_select_item 1
podman_tui_select_item $net_index
podman_tui_send_inputs "Enter"
podman_tui_send_inputs "Tab" "Tab" "Tab" "Tab" "Down" "Tab"
podman_tui_send_inputs "disable"
@ -76,11 +77,13 @@ load helpers_tui
}
@test "pod start" {
pod_index=$(podman pod ls --sort name --format "{{ .Name }}" | nl -v 0 | grep "$TEST_POD_NAME" | awk '{print $1}')
# switch to pods view
# select test pod from list
# select start command from pod commands dialog
podman_tui_set_view "pods"
podman_tui_select_item 0
podman_tui_select_item $pod_index
podman_tui_select_pod_cmd "start"
sleep $TEST_TIMEOUT_LOW
@ -89,11 +92,13 @@ load helpers_tui
}
@test "pod pause" {
pod_index=$(podman pod ls --sort name --format "{{ .Name }}" | nl -v 0 | grep "$TEST_POD_NAME" | awk '{print $1}')
# switch to pods view
# select test pod from list
# select pause command from pod commands dialog
podman_tui_set_view "pods"
podman_tui_select_item 0
podman_tui_select_item $pod_index
podman_tui_select_pod_cmd "pause"
sleep $TEST_TIMEOUT_LOW
@ -102,11 +107,13 @@ load helpers_tui
}
@test "pod unpause" {
pod_index=$(podman pod ls --sort name --format "{{ .Name }}" | nl -v 0 | grep "$TEST_POD_NAME" | awk '{print $1}')
# switch to pods view
# select test pod from list
# select unpause command from pod commands dialog
podman_tui_set_view "pods"
podman_tui_select_item 0
podman_tui_select_item $pod_index
podman_tui_select_pod_cmd "unpause"
sleep $TEST_TIMEOUT_LOW
@ -115,11 +122,13 @@ load helpers_tui
}
@test "pod stop" {
pod_index=$(podman pod ls --sort name --format "{{ .Name }}" | nl -v 0 | grep "$TEST_POD_NAME" | awk '{print $1}')
# switch to pods view
# select test pod from list
# select stop command from pod commands dialog
podman_tui_set_view "pods"
podman_tui_select_item 0
podman_tui_select_item $pod_index
podman_tui_select_pod_cmd "stop"
sleep $TEST_TIMEOUT_LOW
@ -128,11 +137,13 @@ load helpers_tui
}
@test "pod restart" {
pod_index=$(podman pod ls --sort name --format "{{ .Name }}" | nl -v 0 | grep "$TEST_POD_NAME" | awk '{print $1}')
# switch to pods view
# select test pod from list
# select restart command from pod commands dialog
podman_tui_set_view "pods"
podman_tui_select_item 0
podman_tui_select_item $pod_index
podman_tui_select_pod_cmd "restart"
sleep $TEST_TIMEOUT_LOW
@ -141,11 +152,13 @@ load helpers_tui
}
@test "pod kill" {
pod_index=$(podman pod ls --sort name --format "{{ .Name }}" | nl -v 0 | grep "$TEST_POD_NAME" | awk '{print $1}')
# switch to pods view
# select test pod from list
# select kill command from pod commands dialog
podman_tui_set_view "pods"
podman_tui_select_item 0
podman_tui_select_item $pod_index
podman_tui_select_pod_cmd "kill"
sleep $TEST_TIMEOUT_LOW
@ -154,12 +167,14 @@ load helpers_tui
}
@test "pod inspect" {
pod_index=$(podman pod ls --sort name --format "{{ .Name }}" | nl -v 0 | grep "$TEST_POD_NAME" | awk '{print $1}')
# switch to pods view
# select test pod from list
# select inspect command from pod commands dialog
# close pod inspect result message dialog
podman_tui_set_view "pods"
podman_tui_select_item 0
podman_tui_select_item $pod_index
podman_tui_select_pod_cmd "inspect"
sleep $TEST_TIMEOUT_LOW
podman_tui_send_inputs "Enter"
@ -169,12 +184,14 @@ load helpers_tui
}
@test "pod remove" {
pod_index=$(podman pod ls --sort name --format "{{ .Name }}" | nl -v 0 | grep "$TEST_POD_NAME" | awk '{print $1}')
# switch to pods view
# select test pod from list
# select remove command from pod commands dialog
# confirm the operation on warnings dialog
podman_tui_set_view "pods"
podman_tui_select_item 0
podman_tui_select_item $pod_index
podman_tui_select_pod_cmd "remove"
podman_tui_send_inputs "Enter"
podman_tui_send_inputs "Enter"

View File

@ -14,11 +14,13 @@ load helpers_tui
podman image pull docker.io/library/busybox
fi
image_index=$(podman image ls --sort repository --noheading | nl -v 1 | grep 'busybox ' | awk '{print $1}')
podman_tui_set_view "containers"
podman_tui_select_container_cmd "run"
podman_tui_send_inputs $TEST_CONTAINER_NAME "Tab" "$TEST_CONTAINER_RUN_CMD" "Tab"
podman_tui_send_inputs "Down"
podman_tui_select_item 1
podman_tui_select_item $image_index
podman_tui_send_inputs "Enter"
podman_tui_send_inputs "Tab" "Tab" "Tab" "Tab" "Space"
podman_tui_send_inputs "Tab" "Tab" "Space"
@ -45,6 +47,8 @@ load helpers_tui
podman image pull docker.io/library/busybox
fi
image_index=$(podman image ls --sort repository --noheading | nl -v 1 | grep 'busybox ' | awk '{print $1}')
# switch to containers view
# select create command from container commands dialog
podman_tui_set_view "containers"
@ -56,7 +60,7 @@ load helpers_tui
# set timeout to 10
podman_tui_send_inputs $TEST_CONTAINER_NAME "Tab" "Tab"
podman_tui_send_inputs "Down"
podman_tui_select_item 1
podman_tui_select_item $image_index
podman_tui_send_inputs "Enter" "Tab" "Tab" "Tab"
podman_tui_send_inputs "Space" "Tab" "Space" "Tab" "$TEST_CONTAINER_TIMEOUT"
podman_tui_send_inputs "Tab" "Tab" "Tab"
@ -74,7 +78,7 @@ load helpers_tui
assert "$cnt_timeout" =~ "$TEST_CONTAINER_TIMEOUT" "expected container config timeout to match: $TEST_CONTAINER_TIMEOUT"
}
@test "container create (environment)" {
@test "container create (environment page)" {
podman container rm -f $TEST_CONTAINER_NAME || echo done
buysbox_image=$(podman image ls --sort repository --format "{{ .Repository }}" --filter "reference=docker.io/library/busybox")
@ -82,6 +86,8 @@ load helpers_tui
podman image pull docker.io/library/busybox
fi
image_index=$(podman image ls --sort repository --noheading | nl -v 1 | grep 'busybox ' | awk '{print $1}')
# switch to containers view
# select create command from container commands dialog
podman_tui_set_view "containers"
@ -91,7 +97,7 @@ load helpers_tui
# select image from dropdown widget
podman_tui_send_inputs $TEST_CONTAINER_NAME "Tab" "Tab"
podman_tui_send_inputs "Down"
podman_tui_select_item 1
podman_tui_select_item $image_index
podman_tui_send_inputs "Enter" "Tab" "Tab" "Tab" "Tab" "Tab" "Tab" "Tab" "Tab" "Tab"
sleep $TEST_TIMEOUT_LOW
@ -116,7 +122,7 @@ load helpers_tui
assert "$cnt_vars" =~ "$TEST_CONTAINER_ENV2" "expected container env to match: $TEST_CONTAINER_ENV2"
}
@test "container create (resource)" {
@test "container create (resource page)" {
podman container rm -f $TEST_CONTAINER_NAME || echo done
buysbox_image=$(podman image ls --sort repository --format "{{ .Repository }}" --filter "reference=docker.io/library/busybox")
@ -124,6 +130,8 @@ load helpers_tui
podman image pull docker.io/library/busybox
fi
image_index=$(podman image ls --sort repository --noheading | nl -v 1 | grep 'busybox ' | awk '{print $1}')
# switch to containers view
# select create command from container commands dialog
podman_tui_set_view "containers"
@ -133,7 +141,7 @@ load helpers_tui
# select image from dropdown widget
podman_tui_send_inputs $TEST_CONTAINER_NAME "Tab" "Tab"
podman_tui_send_inputs "Down"
podman_tui_select_item 1
podman_tui_select_item $image_index
podman_tui_send_inputs "Enter" "Tab" "Tab" "Tab" "Tab" "Tab" "Tab" "Tab" "Tab" "Tab"
sleep $TEST_TIMEOUT_LOW
@ -190,6 +198,12 @@ load helpers_tui
podman pod create --name $TEST_CONTAINER_POD_NAME --network $TEST_CONTAINER_NETWORK_NAME --publish $TEST_CONTAINER_PORT || echo done
sleep $TEST_TIMEOUT_LOW
# get required pod, image, network and volume index for number of KeyDown stroke
pod_index=$(podman pod ls --sort name --format "{{ .Name }}" | nl -v 1 | grep "$TEST_CONTAINER_POD_NAME" | awk '{print $1}')
image_index=$(podman image ls --sort repository --noheading | nl -v 1 | grep 'httpd ' | awk '{print $1}')
net_index=$(podman network ls -q | nl -v 1 | grep "$TEST_CONTAINER_NETWORK_NAME" | awk '{print $1}')
# switch to containers view
# select create command from container commands dialog
podman_tui_set_view "containers"
@ -201,10 +215,10 @@ load helpers_tui
# fillout label field
podman_tui_send_inputs $TEST_CONTAINER_NAME "Tab" "Tab"
podman_tui_send_inputs "Down"
podman_tui_select_item 2
podman_tui_select_item $image_index
podman_tui_send_inputs "Enter" "Tab"
podman_tui_send_inputs "Down"
podman_tui_select_item 1
podman_tui_select_item $pod_index
podman_tui_send_inputs "Enter" "Tab"
podman_tui_send_inputs $TEST_LABEL "Tab" "Tab" "Tab" "Tab" "Tab" "Tab"
sleep $TEST_TIMEOUT_LOW
@ -279,13 +293,15 @@ load helpers_tui
}
@test "container commit" {
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select container from the list
# select commit command from container commands dialog
# fillout image input field
# go to commit button and press enter
podman_tui_set_view "containers"
podman_tui_select_item 0
podman_tui_select_item $container_index
podman_tui_select_container_cmd "commit"
podman_tui_send_inputs $TEST_CONTAINER_COMMIT_IMAGE_NAME
@ -298,11 +314,13 @@ load helpers_tui
}
@test "container start" {
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select start command from container commands dialog
podman_tui_set_view "containers"
podman_tui_select_item 0
podman_tui_select_item $container_index
podman_tui_select_container_cmd "start"
sleep $TEST_TIMEOUT_LOW
@ -314,6 +332,9 @@ load helpers_tui
@test "container checkpoint" {
podman container create --name ${TEST_CONTAINER_CHECKPOINT_NAME} docker.io/library/httpd
podman container start ${TEST_CONTAINER_CHECKPOINT_NAME}
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_CHECKPOINT_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select checkpoint command from container commands dialog
@ -321,7 +342,7 @@ load helpers_tui
# go to checkpoint button and Enter
podman_tui_set_view "containers"
podman_tui_select_item 0
podman_tui_select_item $container_index
podman_tui_select_container_cmd "checkpoint"
podman_tui_send_inputs "Tab"
@ -359,6 +380,8 @@ load helpers_tui
}
@test "container exec" {
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select exec command from container commands dialog
@ -369,7 +392,7 @@ load helpers_tui
# type "echo test > a.txt"
# go to Close button and Enter
podman_tui_set_view "containers"
podman_tui_select_item 2
podman_tui_select_item $container_index
podman_tui_select_container_cmd "exec"
podman_tui_send_inputs "/bin/bash"
podman_tui_send_inputs "Tab" "Space" "Tab"
@ -386,11 +409,13 @@ load helpers_tui
}
@test "container inspect" {
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select inspect command from container commands dialog
podman_tui_set_view "containers"
podman_tui_select_item 2
podman_tui_select_item $container_index
podman_tui_select_container_cmd "inspect"
sleep $TEST_TIMEOUT_LOW
@ -400,11 +425,13 @@ load helpers_tui
}
@test "container diff" {
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select diff command from container commands dialog
podman_tui_set_view "containers"
podman_tui_select_item 2
podman_tui_select_item $container_index
podman_tui_select_container_cmd "diff"
sleep $TEST_TIMEOUT_MEDIUM
@ -413,11 +440,13 @@ load helpers_tui
}
@test "container top" {
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select top command from container commands dialog
podman_tui_set_view "containers"
podman_tui_select_item 2
podman_tui_select_item $container_index
podman_tui_select_container_cmd "top"
sleep $TEST_TIMEOUT_LOW
@ -426,11 +455,13 @@ load helpers_tui
}
@test "container port" {
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select port command from container commands dialog
podman_tui_set_view "containers"
podman_tui_select_item 2
podman_tui_select_item $container_index
podman_tui_select_container_cmd "port"
sleep $TEST_TIMEOUT_LOW
@ -440,11 +471,13 @@ load helpers_tui
}
@test "container pause" {
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select pause command from container commands dialog
podman_tui_set_view "containers"
podman_tui_select_item 2
podman_tui_select_item $container_index
podman_tui_select_container_cmd "pause"
sleep $TEST_TIMEOUT_LOW
@ -453,11 +486,13 @@ load helpers_tui
}
@test "container unpause" {
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select unpause command from container commands dialog
podman_tui_set_view "containers"
podman_tui_select_item 2
podman_tui_select_item $container_index
podman_tui_select_container_cmd "unpause"
sleep $TEST_TIMEOUT_LOW
@ -466,11 +501,13 @@ load helpers_tui
}
@test "container stop" {
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select stop command from container commands dialog
podman_tui_set_view "containers"
podman_tui_select_item 2
podman_tui_select_item $container_index
podman_tui_select_container_cmd "stop"
sleep $TEST_TIMEOUT_LOW
@ -480,11 +517,13 @@ load helpers_tui
@test "container kill" {
podman container start $TEST_CONTAINER_NAME || echo done
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select kill command from container commands dialog
podman_tui_set_view "containers"
podman_tui_select_item 2
podman_tui_select_item $container_index
podman_tui_select_container_cmd "kill"
sleep $TEST_TIMEOUT_LOW
@ -493,11 +532,13 @@ load helpers_tui
}
@test "container remove" {
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select remove command from container commands dialog
podman_tui_set_view "containers"
podman_tui_select_item 2
podman_tui_select_item $container_index
podman_tui_select_container_cmd "remove"
podman_tui_send_inputs "Enter"
sleep $TEST_TIMEOUT_LOW
@ -507,14 +548,15 @@ load helpers_tui
}
@test "container rename" {
podman container rm $TEST_CONTAINER_NAME || echo done
podman container create --name $TEST_CONTAINER_NAME httpd
container_index=$(podman container ls --all --format "{{ .Names }}" | sort | nl -v 0 | grep "$TEST_CONTAINER_NAME" | awk '{print $1}')
# switch to containers view
# select test container from list
# select rename command from container commands dialog
podman_tui_set_view "containers"
podman_tui_select_item 0
podman_tui_select_item $container_index
podman_tui_select_container_cmd "rename"
podman_tui_send_inputs ${TEST_CONTAINER_NAME}_renamed
podman_tui_send_inputs "Tab" "Tab" "Enter"

View File

@ -3,9 +3,9 @@
TEST_IMAGE_TAG_NAME="podman_tui_busybox_tag01"
TEST_NAME="ptui_test"
TEST_VOLUME_NAME="${TEST_NAME}_vol01"
TEST_NETWORK_NAME="zz_${TEST_NAME}_net01"
TEST_NETWORK_CONNECT="zz_${TEST_NAME}_net0_connect"
TEST_NETWORK_CONNECT_ALIAS="zz_${TEST_NAME}_net0_connect_alias"
TEST_NETWORK_NAME="${TEST_NAME}_net01"
TEST_NETWORK_CONNECT="net_connect_${TEST_NAME}"
TEST_NETWORK_CONNECT_ALIAS="net_connect_alias"
TEST_POD_NAME="${TEST_NAME}_pod01"
TEST_POD_NETWORK_NAME="${TEST_NAME}_pod01_net"
TEST_CONTAINER_NAME="${TEST_NAME}_cnt01"
@ -15,7 +15,7 @@ TEST_CONTAINER_HEALTH_INTERVAL="30s"
TEST_CONTAINER_HEALTH_TIMEOUT="30s"
TEST_CONTAINER_HEALTH_RETRIES="3"
TEST_CONTAINER_HEALTH_ONFAILURE="restart"
TEST_CONTAINER_POD_NAME="${TEST_POD_NAME}"
TEST_CONTAINER_POD_NAME="${TEST_NAME}_cnt01_pod"
TEST_CONTAINER_NETWORK_NAME="${TEST_NAME}_cnt01_net"
TEST_CONTAINER_VOLUME_NAME="${TEST_NAME}_cnt01_vol"
TEST_CONTAINER_VOLUME_MOUNT_POINT="/data_mount01"

View File

@ -35,7 +35,6 @@ const (
// ContainerCheckpointDialog implements container checkpoint dialog primitive.
type ContainerCheckpointDialog struct {
*tview.Box
layout *tview.Flex
containerInfo *tview.InputField
createImage *tview.InputField
@ -83,7 +82,7 @@ func NewContainerCheckpointDialog() *ContainerCheckpointDialog {
// containerInfo
dialog.containerInfo.SetBackgroundColor(style.DialogBgColor)
dialog.containerInfo.SetLabel(fmt.Sprintf("[::b]%s:", utils.ContainerIDLabel))
dialog.containerInfo.SetLabel("[::b]CONTAINER ID:")
dialog.containerInfo.SetLabelWidth(labelWidth)
dialog.containerInfo.SetFieldBackgroundColor(style.DialogBgColor)
dialog.containerInfo.SetLabelStyle(tcell.StyleDefault.
@ -454,9 +453,9 @@ func (d *ContainerCheckpointDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
@ -482,6 +481,31 @@ func (d *ContainerCheckpointDialog) SetCancelFunc(handler func()) *ContainerChec
return d
}
func (d *ContainerCheckpointDialog) setFocusElement() { //nolint:cyclop
switch d.focusElement {
case cntCheckpointImageFocus:
d.focusElement = cntCheckpointExportFocus
case cntCheckpointExportFocus:
d.focusElement = cntCheckpointFileLockFocus
case cntCheckpointFileLockFocus:
d.focusElement = cntCheckpointIgnoreRootFsFocus
case cntCheckpointIgnoreRootFsFocus:
d.focusElement = cntCheckpointTCPEstablishedFocus
case cntCheckpointTCPEstablishedFocus:
d.focusElement = cntCheckpointPreCheckpointFocus
case cntCheckpointPreCheckpointFocus:
d.focusElement = cntCheckpointPrintStatsFocus
case cntCheckpointPrintStatsFocus:
d.focusElement = cntCheckpointKeepFocus
case cntCheckpointKeepFocus:
d.focusElement = cntCheckpointLeaveRunningFocus
case cntCheckpointLeaveRunningFocus:
d.focusElement = cntCheckpointWithPreviousFocus
case cntCheckpointWithPreviousFocus:
d.focusElement = cntCheckpointFormFocus
}
}
// SetContainerInfo sets selected container ID and name information.
func (d *ContainerCheckpointDialog) SetContainerInfo(id string, name string) {
d.containerID = id
@ -514,28 +538,3 @@ func (d *ContainerCheckpointDialog) GetCheckpointOptions() containers.CntCheckPo
return opts
}
func (d *ContainerCheckpointDialog) setFocusElement() { //nolint:cyclop
switch d.focusElement {
case cntCheckpointImageFocus:
d.focusElement = cntCheckpointExportFocus
case cntCheckpointExportFocus:
d.focusElement = cntCheckpointFileLockFocus
case cntCheckpointFileLockFocus:
d.focusElement = cntCheckpointIgnoreRootFsFocus
case cntCheckpointIgnoreRootFsFocus:
d.focusElement = cntCheckpointTCPEstablishedFocus
case cntCheckpointTCPEstablishedFocus:
d.focusElement = cntCheckpointPreCheckpointFocus
case cntCheckpointPreCheckpointFocus:
d.focusElement = cntCheckpointPrintStatsFocus
case cntCheckpointPrintStatsFocus:
d.focusElement = cntCheckpointKeepFocus
case cntCheckpointKeepFocus:
d.focusElement = cntCheckpointLeaveRunningFocus
case cntCheckpointLeaveRunningFocus:
d.focusElement = cntCheckpointWithPreviousFocus
case cntCheckpointWithPreviousFocus:
d.focusElement = cntCheckpointFormFocus
}
}

View File

@ -33,7 +33,6 @@ const (
// ContainerCommitDialog represents container commit dialog primitive.
type ContainerCommitDialog struct {
*tview.Box
layout *tview.Flex
cntInfo *tview.InputField
image *tview.InputField
@ -72,9 +71,11 @@ func NewContainerCommitDialog() *ContainerCommitDialog {
labelWidth := 9
// container info input field
cntInfoLabel := "CONTAINER ID:" //nolint:goconst
dialog.cntInfo.SetBackgroundColor(style.DialogBgColor)
dialog.cntInfo.SetLabel("[::b]" + utils.ContainerIDLabel)
dialog.cntInfo.SetLabelWidth(len(utils.ContainerIDLabel) + 1)
dialog.cntInfo.SetLabel("[::b]" + cntInfoLabel)
dialog.cntInfo.SetLabelWidth(len(cntInfoLabel) + 1)
dialog.cntInfo.SetFieldBackgroundColor(style.DialogBgColor)
dialog.cntInfo.SetLabelStyle(tcell.StyleDefault.
Background(style.DialogBorderColor).
@ -365,6 +366,25 @@ func (d *ContainerCommitDialog) InputHandler() func(event *tcell.EventKey, setFo
})
}
func (d *ContainerCommitDialog) setFocusElement() {
switch d.focusElement {
case cntCommitImageFocus:
d.focusElement = cntCommitAuthorFocus
case cntCommitAuthorFocus:
d.focusElement = cntCommitChangeFocus
case cntCommitChangeFocus:
d.focusElement = cntCommitFormatFocus
case cntCommitFormatFocus:
d.focusElement = cntCommitSquashFocus
case cntCommitSquashFocus:
d.focusElement = cntCommitPauseFocus
case cntCommitPauseFocus:
d.focusElement = cntCommitMessageFocus
case cntCommitMessageFocus:
d.focusElement = cntCommitFormFocus
}
}
// SetRect set rects for this primitive.
func (d *ContainerCommitDialog) SetRect(x, y, width, height int) {
if width > cntCommitDialogMaxWidth {
@ -388,9 +408,9 @@ func (d *ContainerCommitDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
@ -445,22 +465,3 @@ func (d *ContainerCommitDialog) GetContainerCommitOptions() containers.CntCommit
return opts
}
func (d *ContainerCommitDialog) setFocusElement() {
switch d.focusElement {
case cntCommitImageFocus:
d.focusElement = cntCommitAuthorFocus
case cntCommitAuthorFocus:
d.focusElement = cntCommitChangeFocus
case cntCommitChangeFocus:
d.focusElement = cntCommitFormatFocus
case cntCommitFormatFocus:
d.focusElement = cntCommitSquashFocus
case cntCommitSquashFocus:
d.focusElement = cntCommitPauseFocus
case cntCommitPauseFocus:
d.focusElement = cntCommitMessageFocus
case cntCommitMessageFocus:
d.focusElement = cntCommitFormFocus
}
}

File diff suppressed because it is too large Load Diff

View File

@ -90,8 +90,6 @@ var _ = Describe("container create", Ordered, func() {
createAction = createWants
}
createDialog.SetHandlerFunc(createFunc)
createDialog.Hide()
createDialog.Display()
createDialog.focusElement = createContainerFormFocus
createDialogApp.SetFocus(createDialog)
createDialogApp.Draw()

View File

@ -36,7 +36,6 @@ const (
// ContainerExecDialog represents container exec dialog primitive.
type ContainerExecDialog struct {
*tview.Box
layout *tview.Flex
cntInfo *tview.InputField
command *tview.InputField
@ -78,9 +77,11 @@ func NewContainerExecDialog() *ContainerExecDialog {
inputFieldBgColor := style.InputFieldBgColor
// label (container ID and Name)
cntInfoLabel := "CONTAINER ID:"
dialog.cntInfo.SetBackgroundColor(style.DialogBgColor)
dialog.cntInfo.SetLabel("[::b]" + utils.ContainerIDLabel)
dialog.cntInfo.SetLabelWidth(len(utils.ContainerIDLabel) + 1)
dialog.cntInfo.SetLabel("[::b]" + cntInfoLabel)
dialog.cntInfo.SetLabelWidth(len(cntInfoLabel) + 1)
dialog.cntInfo.SetFieldBackgroundColor(style.DialogBgColor)
dialog.cntInfo.SetLabelStyle(tcell.StyleDefault.
Background(style.DialogBorderColor).
@ -605,9 +606,9 @@ func (d *ContainerExecDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)

View File

@ -48,7 +48,6 @@ const (
// ContainerRestoreDialog implements container restore dialog primitive.
type ContainerRestoreDialog struct {
*tview.Box
layout *tview.Flex
containers *tview.DropDown
pods *tview.DropDown
@ -435,9 +434,9 @@ func (d *ContainerRestoreDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
@ -463,6 +462,36 @@ func (d *ContainerRestoreDialog) SetCancelFunc(handler func()) *ContainerRestore
return d
}
func (d *ContainerRestoreDialog) setFocusElement() {
if d.focusElement < cntRestoreFormFocus {
d.focusElement++
return
}
d.focusElement = cntRestoreContainersFocus
}
func (d *ContainerRestoreDialog) getInnerPrimitives() []tview.Primitive {
// the item sort is important to be same as focus element number
return []tview.Primitive{
d.containers,
d.pods,
d.name,
d.publishPorts,
d.importArchive,
d.keep,
d.ignoreStaticIP,
d.ignoreStaticMAC,
d.fileLocks,
d.printStats,
d.tcpEstablished,
d.ignoreVolumes,
d.ignoreRootFS,
d.form,
}
}
// SetContainers sets containers dropdown options.
func (d *ContainerRestoreDialog) SetContainers(cnts [][]string) {
emptyOptions := fmt.Sprintf("%*s", cntRestoreDialogSingleFieldWidth, " ")
@ -527,33 +556,3 @@ func (d *ContainerRestoreDialog) GetRestoreOptions() containers.CntRestoreOption
return opts
}
func (d *ContainerRestoreDialog) setFocusElement() {
if d.focusElement < cntRestoreFormFocus {
d.focusElement++
return
}
d.focusElement = cntRestoreContainersFocus
}
func (d *ContainerRestoreDialog) getInnerPrimitives() []tview.Primitive {
// the item sort is important to be same as focus element number
return []tview.Primitive{
d.containers,
d.pods,
d.name,
d.publishPorts,
d.importArchive,
d.keep,
d.ignoreStaticIP,
d.ignoreStaticMAC,
d.fileLocks,
d.printStats,
d.tcpEstablished,
d.ignoreVolumes,
d.ignoreRootFS,
d.form,
}
}

View File

@ -17,7 +17,6 @@ import (
// ContainerStatsDialog implements the containers stats dialog primitive.
type ContainerStatsDialog struct {
*tview.Box
layout *tview.Flex
form *tview.Form
table *tview.Table
@ -49,9 +48,11 @@ func NewContainerStatsDialog() *ContainerStatsDialog {
statsDialog.initTableUI()
// container info text view
cntInfoLabel := "CONTAINER ID:"
statsDialog.containerInfo.SetBackgroundColor(style.DialogBgColor)
statsDialog.containerInfo.SetLabel("[::b]" + utils.ContainerIDLabel)
statsDialog.containerInfo.SetLabelWidth(len(utils.ContainerIDLabel) + 1)
statsDialog.containerInfo.SetLabel("[::b]" + cntInfoLabel)
statsDialog.containerInfo.SetLabelWidth(len(cntInfoLabel) + 1)
statsDialog.containerInfo.SetFieldBackgroundColor(style.DialogBgColor)
statsDialog.containerInfo.SetLabelStyle(tcell.StyleDefault.
Background(style.DialogBorderColor).
@ -120,9 +121,7 @@ func (d *ContainerStatsDialog) Hide() {
d.setContainerBlockOutput(0)
d.setContainerNetInput(0)
d.setContainerNetOutput(0)
d.mu.Lock()
defer d.mu.Unlock()
*d.statsStream = false
@ -169,8 +168,8 @@ func (d *ContainerStatsDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}

View File

@ -49,9 +49,7 @@ func (w *writer) Write(b []byte) (int, error) {
}
buf := make([]byte, len(b))
copy(buf, b)
w.ch <- buf
return len(b), nil

View File

@ -32,9 +32,8 @@ const (
// VtermDialog implements virtual terminal that can be used during
// exec, attach, run activity.
type VtermDialog struct {
type VtermDialog struct { //nolint:revive
*tview.Box
layout *tview.Flex
form *tview.Form
containerInfo *tview.InputField
@ -87,6 +86,73 @@ func NewVtermDialog() *VtermDialog {
return dialog
}
func (d *VtermDialog) initLayoutUI() {
bgColor := style.DialogBgColor
borderColor := style.DialogBorderColor
terminalBgColor := style.TerminalBgColor
terminalBorderColor := style.TerminalBorderColor
// container information field
// label
cntInfoLabel := "CONTAINER ID:"
d.containerInfo.SetBackgroundColor(bgColor)
d.containerInfo.SetLabel("[::b]" + cntInfoLabel)
d.containerInfo.SetLabelWidth(len(cntInfoLabel) + 1)
d.containerInfo.SetFieldBackgroundColor(bgColor)
d.containerInfo.SetLabelStyle(tcell.StyleDefault.
Background(borderColor).
Foreground(style.DialogFgColor))
// form fields
d.form.AddButton("Cancel", nil).
SetButtonsAlign(tview.AlignRight)
d.form.SetBackgroundColor(bgColor)
d.form.SetButtonBackgroundColor(style.ButtonBgColor)
// terminal screen
d.termScreen.SetBackgroundColor(terminalBgColor)
d.termScreen.SetBorder(true)
d.termScreen.SetBorderColor(terminalBorderColor)
// layout setup
layout := tview.NewFlex().SetDirection(tview.FlexRow)
termLayout := tview.NewFlex().SetDirection(tview.FlexColumn)
layout.SetBackgroundColor(bgColor)
layout.SetBorder(false)
layout.AddItem(d.containerInfo, 1, 0, true)
// layout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
layout.AddItem(d.termScreen, 0, 1, true)
termLayout.SetBackgroundColor(bgColor)
termLayout.SetBorder(false)
termLayout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, false)
termLayout.AddItem(layout, 0, 1, false)
termLayout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, false)
// main layout
d.layout.SetDirection(tview.FlexRow)
d.layout.SetBorder(true)
d.layout.SetBorderColor(borderColor)
d.layout.SetBackgroundColor(bgColor)
// d.layout.SetTitle("CONTAINER TERMINAL")
d.layout.AddItem(termLayout, 0, 1, true)
d.layout.AddItem(d.form, dialogs.DialogFormHeight, 0, true)
}
func (d *VtermDialog) initChannelsCommon() {
d.sessionOutputDoneChan = make(chan bool, 2) //nolint:mnd
d.vtTerminal = vt10x.New()
sessionStdinPipeIn, sessionStdinPipeOut := io.Pipe()
d.sessionStdin = bufio.NewReader(sessionStdinPipeIn)
d.sessionStdinWriter = bufio.NewWriter(sessionStdinPipeOut)
d.vtTermPipeReader, d.vtTermPipeWriter = io.Pipe()
d.vtTermBuffer = bufio.NewReader(d.vtTermPipeReader)
}
// InitChannels will init buffers and channels for attach.
func (d *VtermDialog) InitAttachChannels() (io.Reader, io.Writer) {
log.Debug().Msg("view: container terminal dialog init channels (attach)")
@ -128,10 +194,9 @@ func (d *VtermDialog) Display() {
return
}
switch d.sessionMode {
case sessionModeAttach:
if d.sessionMode == sessionModeAttach {
go d.sessionOutputStreamer()
case sessionModeExec:
} else if d.sessionMode == sessionModeExec {
go d.execSessionOutputStreamer()
}
@ -184,21 +249,11 @@ func (d *VtermDialog) Hide() {
}
if d.sessionMode == sessionModeExec {
err := d.execSessionStdout.Close()
if err != nil {
log.Error().Msgf("failed to close vterm exec stdout session: %s", err.Error())
}
d.execSessionStdout.Close()
}
err := d.vtTermPipeReader.Close()
if err != nil {
log.Error().Msgf("failed to close vterm pipe reader: %s", err.Error())
}
err = d.vtTermPipeWriter.Close()
if err != nil {
log.Error().Msgf("failed to close vterm pipe writer: %s", err.Error())
}
d.vtTermPipeReader.Close()
d.vtTermPipeWriter.Close()
}
// HasFocus returns true if terminal dialog has focus.
@ -299,8 +354,8 @@ func (d *VtermDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
@ -335,6 +390,23 @@ func (d *VtermDialog) Draw(screen tcell.Screen) {
}
}
func (d *VtermDialog) setTTYSize(width int, height int) {
if width < 0 || height < 0 {
return
}
d.ttyWidth = width
d.ttyHeight = height
d.vtTerminal.Resize(width, height)
if d.sessionMode == sessionModeAttach {
go containers.ResizeContainerTTY(d.containerID, width, height) //nolint:errcheck
} else if d.sessionMode == sessionModeExec {
go containers.ResizeExecTty(d.sessionID, d.ttyHeight, d.ttyWidth)
}
}
// SetCancelFunc sets form close button selected function.
func (d *VtermDialog) SetCancelFunc(handler func()) *VtermDialog {
d.cancelHandler = handler
@ -382,31 +454,28 @@ func (d *VtermDialog) SetFastRefreshHandler(handler func()) {
func (d *VtermDialog) writeToSession(event *tcell.EventKey) {
switch event.Key() { //nolint:exhaustive
case tcell.KeyUp:
d.writeToStdinSessionWriter(rune(27)) //nolint:mnd
d.writeToStdinSessionWriter(rune(91)) //nolint:mnd
d.writeToStdinSessionWriter(rune(65)) //nolint:mnd
d.sessionStdinWriter.WriteRune(rune(27)) //nolint:errcheck,mnd
d.sessionStdinWriter.WriteRune(rune(91)) //nolint:errcheck,mnd
d.sessionStdinWriter.WriteRune(rune(65)) //nolint:errcheck,mnd
case tcell.KeyDown:
d.writeToStdinSessionWriter(rune(27)) //nolint:mnd
d.writeToStdinSessionWriter(rune(91)) //nolint:mnd
d.writeToStdinSessionWriter(rune(66)) //nolint:mnd
d.sessionStdinWriter.WriteRune(rune(27)) //nolint:errcheck,mnd
d.sessionStdinWriter.WriteRune(rune(91)) //nolint:errcheck,mnd
d.sessionStdinWriter.WriteRune(rune(66)) //nolint:errcheck,mnd
case tcell.KeyRight:
d.writeToStdinSessionWriter(rune(27)) //nolint:mnd
d.writeToStdinSessionWriter(rune(91)) //nolint:mnd
d.writeToStdinSessionWriter(rune(67)) //nolint:mnd
d.sessionStdinWriter.WriteRune(rune(27)) //nolint:errcheck,mnd
d.sessionStdinWriter.WriteRune(rune(91)) //nolint:errcheck,mnd
d.sessionStdinWriter.WriteRune(rune(67)) //nolint:errcheck,mnd
case tcell.KeyLeft:
d.writeToStdinSessionWriter(rune(27)) //nolint:mnd
d.writeToStdinSessionWriter(rune(91)) //nolint:mnd
d.writeToStdinSessionWriter(rune(68)) //nolint:mnd
d.sessionStdinWriter.WriteRune(rune(27)) //nolint:errcheck,mnd
d.sessionStdinWriter.WriteRune(rune(91)) //nolint:errcheck,mnd
d.sessionStdinWriter.WriteRune(rune(68)) //nolint:errcheck,mnd
case tcell.KeyEsc:
d.writeToStdinSessionWriter(rune(27)) //nolint:mnd
d.sessionStdinWriter.WriteRune(rune(27)) //nolint:errcheck,mnd
default:
d.writeToStdinSessionWriter(event.Rune())
d.sessionStdinWriter.WriteRune(event.Rune()) //nolint:errcheck
}
err := d.sessionStdinWriter.Flush()
if err != nil {
log.Error().Msgf("failed to flush vterm stdin writer session: %s", err.Error())
}
d.sessionStdinWriter.Flush()
}
func (d *VtermDialog) sendDetachToSession() {
@ -415,13 +484,10 @@ func (d *VtermDialog) sendDetachToSession() {
keys := d.detachKeys.keys()
for i := range keys {
d.writeToStdinSessionWriter(rune(keys[i]))
d.sessionStdinWriter.WriteRune(rune(keys[i])) //nolint:errcheck
}
err := d.sessionStdinWriter.Flush()
if err != nil {
log.Error().Msgf("failed to flush vterm stdin writer session: %s", err.Error())
}
d.sessionStdinWriter.Flush()
}
// vtContent returns current content and cursor location of vt10x terminal.
@ -464,11 +530,7 @@ func (d *VtermDialog) execSessionOutputStreamer() {
return
case data := <-d.execSessionStdout.Chan():
_, err := d.vtTermPipeWriter.Write(data)
if err != nil {
log.Error().Msgf("failed to write %s to vterm pipe writer: %s", data, err.Error())
}
d.vtTermPipeWriter.Write(data) //nolint:errcheck
d.fastRefreshHandler()
}
}
@ -487,11 +549,7 @@ func (d *VtermDialog) sessionOutputStreamer() {
dataString := string(data)
dataString = strings.ReplaceAll(dataString, "\n", "\r\n")
_, err := d.vtTermPipeWriter.Write([]byte(dataString))
if err != nil {
log.Error().Msgf("failed to write %s to vterm pipe writer: %s", []byte(dataString), err.Error())
}
d.vtTermPipeWriter.Write([]byte(dataString)) //nolint:errcheck
d.fastRefreshHandler()
}
}
@ -509,93 +567,3 @@ func (dkey termDetachKeys) string() string {
func (dkey termDetachKeys) keys() []tcell.Key {
return dkey.tcellKeys
}
func (d *VtermDialog) initLayoutUI() {
bgColor := style.DialogBgColor
borderColor := style.DialogBorderColor
terminalBgColor := style.TerminalBgColor
terminalBorderColor := style.TerminalBorderColor
// container information field
// label
d.containerInfo.SetBackgroundColor(bgColor)
d.containerInfo.SetLabel("[::b]" + utils.ContainerIDLabel)
d.containerInfo.SetLabelWidth(len(utils.ContainerIDLabel) + 1)
d.containerInfo.SetFieldBackgroundColor(bgColor)
d.containerInfo.SetLabelStyle(tcell.StyleDefault.
Background(borderColor).
Foreground(style.DialogFgColor))
// form fields
d.form.AddButton("Cancel", nil).
SetButtonsAlign(tview.AlignRight)
d.form.SetBackgroundColor(bgColor)
d.form.SetButtonBackgroundColor(style.ButtonBgColor)
// terminal screen
d.termScreen.SetBackgroundColor(terminalBgColor)
d.termScreen.SetBorder(true)
d.termScreen.SetBorderColor(terminalBorderColor)
// layout setup
layout := tview.NewFlex().SetDirection(tview.FlexRow)
termLayout := tview.NewFlex().SetDirection(tview.FlexColumn)
layout.SetBackgroundColor(bgColor)
layout.SetBorder(false)
layout.AddItem(d.containerInfo, 1, 0, true)
// layout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
layout.AddItem(d.termScreen, 0, 1, true)
termLayout.SetBackgroundColor(bgColor)
termLayout.SetBorder(false)
termLayout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, false)
termLayout.AddItem(layout, 0, 1, false)
termLayout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, false)
// main layout
d.layout.SetDirection(tview.FlexRow)
d.layout.SetBorder(true)
d.layout.SetBorderColor(borderColor)
d.layout.SetBackgroundColor(bgColor)
// d.layout.SetTitle("CONTAINER TERMINAL")
d.layout.AddItem(termLayout, 0, 1, true)
d.layout.AddItem(d.form, dialogs.DialogFormHeight, 0, true)
}
func (d *VtermDialog) initChannelsCommon() {
d.sessionOutputDoneChan = make(chan bool, 2) //nolint:mnd
d.vtTerminal = vt10x.New()
sessionStdinPipeIn, sessionStdinPipeOut := io.Pipe()
d.sessionStdin = bufio.NewReader(sessionStdinPipeIn)
d.sessionStdinWriter = bufio.NewWriter(sessionStdinPipeOut)
d.vtTermPipeReader, d.vtTermPipeWriter = io.Pipe()
d.vtTermBuffer = bufio.NewReader(d.vtTermPipeReader)
}
func (d *VtermDialog) setTTYSize(width int, height int) {
if width < 0 || height < 0 {
return
}
d.ttyWidth = width
d.ttyHeight = height
d.vtTerminal.Resize(width, height)
switch d.sessionMode {
case sessionModeAttach:
go containers.ResizeContainerTTY(d.containerID, width, height) //nolint:errcheck
case sessionModeExec:
go containers.ResizeExecTty(d.sessionID, d.ttyHeight, d.ttyWidth)
}
}
func (d *VtermDialog) writeToStdinSessionWriter(val rune) {
_, err := d.sessionStdinWriter.WriteRune(val)
if err != nil {
log.Error().Msgf("failed to write value %d to vterm session writer rune: %s", val, err.Error())
}
}

View File

@ -8,7 +8,6 @@ import (
"github.com/containers/podman-tui/pdcs/pods"
"github.com/containers/podman-tui/ui/dialogs"
"github.com/containers/podman-tui/ui/style"
"github.com/containers/podman-tui/ui/utils"
bcontainers "github.com/containers/podman/v5/pkg/bindings/containers"
"github.com/rs/zerolog/log"
)
@ -37,7 +36,7 @@ func (cnt *Containers) runCommand(cmd string) { //nolint:cyclop
cnt.logs()
case "pause":
cnt.pause()
case utils.PruneCommandLabel:
case "prune": //nolint:goconst
cnt.cprune()
case "rename":
cnt.rename()
@ -334,7 +333,7 @@ func (cnt *Containers) stats() {
}
if cntStatus != "running" {
cnt.displayError("", fmt.Errorf("container (%s) status improper", cntID)) //nolint:err113
cnt.displayError("", fmt.Errorf("container (%s) status improper", cntID)) //nolint:goerr113
return
}
@ -451,8 +450,7 @@ func (cnt *Containers) runDetach(runOpts containers.CreateOptions) {
return
}
err = containers.Start(cntID)
if err != nil {
if err := containers.Start(cntID); err != nil {
cnt.progressDialog.Hide()
cnt.displayError("CONTAINER RUN ERROR", err)
cnt.appFocusHandler()
@ -527,8 +525,7 @@ func (cnt *Containers) runAttach(runOpts containers.CreateOptions) {
cnt.progressDialog.Hide()
if isReady {
err := containers.Start(cntID)
if err != nil {
if err := containers.Start(cntID); err != nil {
cnt.displayError("CONTAINER RUN ERROR", err)
cnt.appFocusHandler()
@ -771,7 +768,7 @@ func (cnt *Containers) prune() {
}
if len(errData) > 0 {
cnt.displayError("CONTAINER PRUNE ERROR", fmt.Errorf("%v", errData)) //nolint:err113
cnt.displayError("CONTAINER PRUNE ERROR", fmt.Errorf("%v", errData)) //nolint:goerr113
}
}
@ -790,8 +787,8 @@ func (cnt *Containers) rename() {
fgColor := style.GetColorHex(style.DialogFgColor)
bgColor := fmt.Sprintf("#%x", style.DialogBorderColor.Hex())
containerInfo := fmt.Sprintf("%s (%s)", cnt.selectedID, cnt.selectedName)
description := fmt.Sprintf("[%s:%s:b]%s[:-:-] %s",
fgColor, bgColor, utils.ContainerIDLabel, containerInfo)
description := fmt.Sprintf("[%s:%s:b]CONTAINER ID:[:-:-] %s",
fgColor, bgColor, containerInfo)
cnt.cmdInputDialog.SetDescription(description)
cnt.cmdInputDialog.SetSelectButtonLabel("rename")
@ -841,7 +838,7 @@ func (cnt *Containers) rm() {
cnt.confirmData = "rm"
bgColor := style.GetColorHex(style.DialogBorderColor)
fgColor := style.GetColorHex(style.DialogFgColor)
containerItem := fmt.Sprintf("[%s:%s:b]%s[:-:-] %s(%s)", fgColor, bgColor, utils.ContainerIDLabel, cntID, cntName)
containerItem := fmt.Sprintf("[%s:%s:b]CONTAINER ID:[:-:-] %s(%s)", fgColor, bgColor, cntID, cntName)
description := fmt.Sprintf("%s\n\nAre you sure you want to remove the selected container ?", //nolint:perfsprint
containerItem)
@ -870,7 +867,7 @@ func (cnt *Containers) remove() {
if len(errData) > 0 {
title := fmt.Sprintf("CONTAINER (%s) REMOVE ERROR", cnt.selectedID)
cnt.displayError(title, fmt.Errorf("%v", errData)) //nolint:err113
cnt.displayError(title, fmt.Errorf("%v", errData)) //nolint:goerr113
cnt.appFocusHandler()
}
}

View File

@ -10,7 +10,6 @@ import (
"github.com/containers/podman-tui/ui/containers/cntdialogs/vterm"
"github.com/containers/podman-tui/ui/dialogs"
"github.com/containers/podman-tui/ui/style"
"github.com/containers/podman-tui/ui/utils"
"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/rivo/tview"
)
@ -50,7 +49,6 @@ var (
// Containers implements the containers page primitive.
type Containers struct {
*tview.Box
title string
headers []string
table *tview.Table
@ -60,16 +58,15 @@ type Containers struct {
confirmDialog *dialogs.ConfirmDialog
messageDialog *dialogs.MessageDialog
progressDialog *dialogs.ProgressDialog
sortDialog *dialogs.SortDialog
topDialog *dialogs.TopDialog
createDialog *cntdialogs.ContainerCreateDialog
runDialog *cntdialogs.ContainerCreateDialog
execDialog *cntdialogs.ContainerExecDialog
terminalDialog *vterm.VtermDialog
statsDialog *cntdialogs.ContainerStatsDialog
commitDialog *cntdialogs.ContainerCommitDialog
checkpointDialog *cntdialogs.ContainerCheckpointDialog
restoreDialog *cntdialogs.ContainerRestoreDialog
terminalDialog *vterm.VtermDialog
containersList containerListReport
selectedID string
selectedName string
@ -79,10 +76,8 @@ type Containers struct {
}
type containerListReport struct {
mu sync.Mutex
report []entities.ListContainer
sortBy string
ascending bool
mu sync.Mutex
report []entities.ListContainer
}
// NewContainers returns containers page view.
@ -97,18 +92,15 @@ func NewContainers() *Containers {
progressDialog: dialogs.NewProgressDialog(),
confirmDialog: dialogs.NewConfirmDialog(),
topDialog: dialogs.NewTopDialog(),
sortDialog: dialogs.NewSortDialog([]string{"name", "pod", "image", "created", "status"}, 3), //nolint:mnd
createDialog: cntdialogs.NewContainerCreateDialog(cntdialogs.ContainerCreateOnlyDialogMode),
runDialog: cntdialogs.NewContainerCreateDialog(cntdialogs.ContainerCreateAndRunDialogMode),
execDialog: cntdialogs.NewContainerExecDialog(),
terminalDialog: vterm.NewVtermDialog(),
statsDialog: cntdialogs.NewContainerStatsDialog(),
commitDialog: cntdialogs.NewContainerCommitDialog(),
checkpointDialog: cntdialogs.NewContainerCheckpointDialog(),
restoreDialog: cntdialogs.NewContainerRestoreDialog(),
terminalDialog: vterm.NewVtermDialog(),
containersList: containerListReport{sortBy: "created", ascending: true},
}
containers.topDialog.SetTitle("podman container top")
containers.cmdDialog = dialogs.NewCommandDialog([][]string{
@ -178,7 +170,7 @@ func NewContainers() *Containers {
containers.confirmDialog.Hide()
switch containers.confirmData {
case utils.PruneCommandLabel:
case "prune":
containers.prune()
case "rm":
containers.remove()
@ -232,10 +224,6 @@ func NewContainers() *Containers {
containers.restoreDialog.SetRestoreFunc(containers.restore)
containers.restoreDialog.SetCancelFunc(containers.restoreDialog.Hide)
// set sort dialog functions
containers.sortDialog.SetSelectFunc(containers.SortView)
containers.sortDialog.SetCancelFunc(containers.sortDialog.Hide)
return containers
}
@ -283,11 +271,7 @@ func (cnt *Containers) HasFocus() bool { //nolint:cyclop
return true
}
if cnt.sortDialog.HasFocus() || cnt.Box.HasFocus() {
return true
}
return false
return cnt.Box.HasFocus()
}
// SubDialogHasFocus returns whether or not sub dialog primitive has focus.
@ -320,11 +304,7 @@ func (cnt *Containers) SubDialogHasFocus() bool { //nolint:cyclop
return true
}
if cnt.sortDialog.HasFocus() || cnt.runDialog.HasFocus() {
return true
}
return false
return cnt.runDialog.HasFocus()
}
// Focus is called when this primitive receives focus.
@ -427,14 +407,24 @@ func (cnt *Containers) Focus(delegate func(p tview.Primitive)) { //nolint:cyclop
return
}
// sort dialog
if cnt.sortDialog.IsDisplay() {
delegate(cnt.sortDialog)
delegate(cnt.table)
}
return
func (cnt *Containers) getSelectedItem() (string, string) {
var (
cntID string
cntName string
)
if cnt.table.GetRowCount() <= 1 {
return cntID, cntName
}
delegate(cnt.table)
row, _ := cnt.table.GetSelection()
cntID = cnt.table.GetCell(row, viewContainersIDColIndex).Text
cntName = cnt.table.GetCell(row, viewContainersNamesColIndex).Text
return cntID, cntName
}
// SetFastRefreshChannel sets channel for fastRefresh func.
@ -503,25 +493,4 @@ func (cnt *Containers) HideAllDialogs() { //nolint:cyclop
if cnt.terminalDialog.IsDisplay() {
cnt.terminalDialog.Hide()
}
if cnt.sortDialog.IsDisplay() {
cnt.sortDialog.Hide()
}
}
func (cnt *Containers) getSelectedItem() (string, string) {
var (
cntID string
cntName string
)
if cnt.table.GetRowCount() <= 1 {
return cntID, cntName
}
row, _ := cnt.table.GetSelection()
cntID = cnt.table.GetCell(row, viewContainersIDColIndex).Text
cntName = cnt.table.GetCell(row, viewContainersNamesColIndex).Text
return cntID, cntName
}

View File

@ -2,7 +2,6 @@ package containers
import (
"fmt"
"sort"
"strings"
"time"
@ -15,18 +14,6 @@ import (
"github.com/rs/zerolog/log"
)
// SortView sorts data view called from sort dialog.
func (cnt *Containers) SortView(option string, ascending bool) {
log.Debug().Msgf("view: containers sort by %s", option)
cnt.containersList.mu.Lock()
defer cnt.containersList.mu.Unlock()
cnt.containersList.sortBy = option
cnt.containersList.ascending = ascending
sort.Sort(containerListSorted{cnt.containersList.report, option, ascending})
}
// UpdateData retrieves containers list data.
func (cnt *Containers) UpdateData() {
cntList, err := containers.List()
@ -41,8 +28,6 @@ func (cnt *Containers) UpdateData() {
cnt.containersList.mu.Lock()
defer cnt.containersList.mu.Unlock()
sort.Sort(containerListSorted{cntList, cnt.containersList.sortBy, cnt.containersList.ascending})
cnt.containersList.report = cntList
}
@ -91,7 +76,7 @@ func (con conReporter) names() string {
func (con conReporter) state() string {
var state string
switch con.State {
switch con.ListContainer.State {
case "running":
t := units.HumanDuration(time.Since(time.Unix(con.StartedAt, 0)))
state = "Up " + t + " ago"
@ -101,14 +86,14 @@ func (con conReporter) state() string {
t := units.HumanDuration(time.Since(time.Unix(con.ExitedAt, 0)))
state = fmt.Sprintf("Exited (%d) %s ago", con.ExitCode, t)
default:
state = con.State
state = con.ListContainer.State
}
return state
}
func (con conReporter) status() string {
hc := con.Status
hc := con.ListContainer.Status
if hc != "" {
return con.state() + " (" + hc + ")"
}
@ -117,56 +102,9 @@ func (con conReporter) status() string {
}
func (con conReporter) ports() string {
if len(con.Ports) < 1 {
if len(con.ListContainer.Ports) < 1 {
return ""
}
return putils.PortsToString(con.Ports)
}
type lprSort []entities.ListContainer
func (a lprSort) Len() int { return len(a) }
func (a lprSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type containerListSorted struct {
lprSort
option string
ascending bool
}
func (a containerListSorted) Less(i, j int) bool {
switch a.option {
case "pod":
if a.ascending {
return a.lprSort[i].Pod < a.lprSort[j].Pod
}
return a.lprSort[i].Pod > a.lprSort[j].Pod
case "image":
if a.ascending {
return a.lprSort[i].Image < a.lprSort[j].Image
}
return a.lprSort[i].Image > a.lprSort[j].Image
case "status":
if a.ascending {
return a.lprSort[i].State < a.lprSort[j].State
}
return a.lprSort[i].State > a.lprSort[j].State
case "created":
if a.ascending {
return a.lprSort[i].Created.After(a.lprSort[j].Created)
}
return a.lprSort[i].Created.Before(a.lprSort[j].Created)
}
if a.ascending {
return a.lprSort[i].Names[0] < a.lprSort[j].Names[0]
}
return a.lprSort[i].Names[0] > a.lprSort[j].Names[0]
return putils.PortsToString(con.ListContainer.Ports)
}

View File

@ -6,8 +6,8 @@ import (
// Draw draws this primitive onto the screen.
func (cnt *Containers) Draw(screen tcell.Screen) { //nolint:cyclop
cnt.DrawForSubclass(screen, cnt)
cnt.SetBorder(false)
cnt.Box.DrawForSubclass(screen, cnt)
cnt.Box.SetBorder(false)
cntViewX, cntViewY, cntViewW, cntViewH := cnt.GetInnerRect()
@ -140,12 +140,4 @@ func (cnt *Containers) Draw(screen tcell.Screen) { //nolint:cyclop
return
}
// sort dialog
if cnt.sortDialog.IsDisplay() {
cnt.sortDialog.SetRect(cntViewX, cntViewY, cntViewW, cntViewH)
cnt.sortDialog.Draw(screen)
return
}
}

View File

@ -107,13 +107,6 @@ func (cnt *Containers) InputHandler() func(event *tcell.EventKey, setFocus func(
}
}
// container sort dialog handler
if cnt.sortDialog.HasFocus() {
if cntSortDialogHandler := cnt.sortDialog.InputHandler(); cntSortDialogHandler != nil {
cntSortDialogHandler(event, setFocus)
}
}
// container terminal dialog handler
if cnt.terminalDialog.HasFocus() {
if cntTerminalDialogHandler := cnt.terminalDialog.InputHandler(); cntTerminalDialogHandler != nil {
@ -124,7 +117,6 @@ func (cnt *Containers) InputHandler() func(event *tcell.EventKey, setFocus func(
// table handlers
if cnt.table.HasFocus() { //nolint:nestif
cnt.selectedID, cnt.selectedName = cnt.getSelectedItem()
// display command menu
if event.Rune() == utils.CommandMenuKey.Rune() {
if cnt.cmdDialog.GetCommandCount() <= 1 {
return
@ -136,14 +128,6 @@ func (cnt *Containers) InputHandler() func(event *tcell.EventKey, setFocus func(
return
}
// display sort menu
if event.Rune() == utils.SortMenuKey.Rune() {
cnt.sortDialog.Display()
setFocus(cnt)
return
}
if event.Key() == utils.DeleteKey.EventKey() {
cnt.rm()
setFocus(cnt)

View File

@ -30,7 +30,6 @@ func (cnt *Containers) refresh(maxWidth int) {
SetSelectable(false))
}
currentSelectedRow, _ := cnt.table.GetSelection()
rowIndex := 1
cntList := cnt.getData()
@ -117,11 +116,4 @@ func (cnt *Containers) refresh(maxWidth int) {
rowIndex++
}
if currentSelectedRow > len(cntList) {
currentSelectedRow--
if currentSelectedRow >= 0 {
cnt.table.Select(currentSelectedRow, -1)
}
}
}

View File

@ -22,7 +22,6 @@ const (
// CommandDialog is a commands list dialog.
type CommandDialog struct {
*tview.Box
layout *tview.Flex
table *tview.Table
form *tview.Form
@ -38,12 +37,6 @@ type CommandDialog struct {
// NewCommandDialog returns a command list primitive.
func NewCommandDialog(options [][]string) *CommandDialog {
var cmdWidth int
// command table items
col1Width := 0
col2Width := 0
form := tview.NewForm().
AddButton("Cancel", nil).
SetButtonsAlign(tview.AlignRight)
@ -54,6 +47,7 @@ func NewCommandDialog(options [][]string) *CommandDialog {
cmdsTable := tview.NewTable()
cmdsTable.SetBackgroundColor(style.DialogBgColor)
cmdWidth := 0
// command table header
cmdsTable.SetCell(0, 0,
tview.NewTableCell(fmt.Sprintf("[%s::b]COMMAND", style.GetColorHex(style.TableHeaderFgColor))).
@ -71,6 +65,10 @@ func NewCommandDialog(options [][]string) *CommandDialog {
SetAlign(tview.AlignCenter).
SetSelectable(false))
// command table items
col1Width := 0
col2Width := 0
for i := range options {
cmdsTable.SetCell(i+1, 0,
tview.NewTableCell(options[i][0]).
@ -275,7 +273,7 @@ func (cmd *CommandDialog) SetRect(x, y, width, height int) {
cmd.Box.SetRect(x+ws, dy, bWidth, bHeight)
x, y, width, height = cmd.GetInnerRect()
x, y, width, height = cmd.Box.GetInnerRect()
cmd.layout.SetRect(x, y, width, height)
}
@ -286,7 +284,7 @@ func (cmd *CommandDialog) Draw(screen tcell.Screen) {
return
}
cmd.DrawForSubclass(screen, cmd)
cmd.Box.DrawForSubclass(screen, cmd)
cmd.layout.Draw(screen)
}

View File

@ -13,7 +13,6 @@ import (
// ConfirmDialog is a simple confirmation dialog primitive.
type ConfirmDialog struct {
*tview.Box
layout *tview.Flex
textview *tview.TextView
form *tview.Form
@ -113,56 +112,6 @@ func (d *ConfirmDialog) SetRect(x, y, width, height int) {
d.setRect()
}
// Draw draws this primitive onto the screen.
func (d *ConfirmDialog) Draw(screen tcell.Screen) {
if !d.display {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
// InputHandler returns input handler function for this primitive.
func (d *ConfirmDialog) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
log.Debug().Msgf("confirm dialog: event %v received", event)
if event.Key() == tcell.KeyEsc {
d.cancelHandler()
return
}
if formHandler := d.form.InputHandler(); formHandler != nil {
formHandler(event, setFocus)
return
}
})
}
// SetCancelFunc sets form cancel button selected function.
func (d *ConfirmDialog) SetCancelFunc(handler func()) *ConfirmDialog {
d.cancelHandler = handler
cancelButton := d.form.GetButton(d.form.GetButtonCount() - 2) //nolint:mnd
cancelButton.SetSelectedFunc(handler)
return d
}
// SetSelectedFunc sets form select button selected function.
func (d *ConfirmDialog) SetSelectedFunc(handler func()) *ConfirmDialog {
d.selectHandler = handler
enterButton := d.form.GetButton(d.form.GetButtonCount() - 1)
enterButton.SetSelectedFunc(handler)
return d
}
func (d *ConfirmDialog) setRect() {
maxHeight := d.height
maxWidth := d.width
@ -207,3 +156,53 @@ func (d *ConfirmDialog) setRect() {
d.Box.SetRect(d.x, d.y, d.width, d.height)
}
// Draw draws this primitive onto the screen.
func (d *ConfirmDialog) Draw(screen tcell.Screen) {
if !d.display {
return
}
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
// InputHandler returns input handler function for this primitive.
func (d *ConfirmDialog) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
log.Debug().Msgf("confirm dialog: event %v received", event)
if event.Key() == tcell.KeyEsc {
d.cancelHandler()
return
}
if formHandler := d.form.InputHandler(); formHandler != nil {
formHandler(event, setFocus)
return
}
})
}
// SetCancelFunc sets form cancel button selected function.
func (d *ConfirmDialog) SetCancelFunc(handler func()) *ConfirmDialog {
d.cancelHandler = handler
cancelButton := d.form.GetButton(d.form.GetButtonCount() - 2) //nolint:mnd
cancelButton.SetSelectedFunc(handler)
return d
}
// SetSelectedFunc sets form select button selected function.
func (d *ConfirmDialog) SetSelectedFunc(handler func()) *ConfirmDialog {
d.selectHandler = handler
enterButton := d.form.GetButton(d.form.GetButtonCount() - 1)
enterButton.SetSelectedFunc(handler)
return d
}

View File

@ -12,7 +12,6 @@ import (
// ErrorDialog is an error dialog primitive.
type ErrorDialog struct {
*tview.Box
modal *tview.Modal
title string
message string
@ -30,7 +29,7 @@ func NewErrorDialog() *ErrorDialog {
dialog.modal.SetButtonBackgroundColor(style.ErrorDialogButtonBgColor)
dialog.modal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
dialog.modal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { //nolint:revive
dialog.Hide()
})
@ -117,7 +116,7 @@ func (d *ErrorDialog) Draw(screen tcell.Screen) {
// SetDoneFunc sets modal done function.
func (d *ErrorDialog) SetDoneFunc(handler func()) *ErrorDialog {
d.modal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
d.modal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { //nolint:revive
handler()
})

View File

@ -23,7 +23,6 @@ const (
// SimpleInputDialog is an input dialog primitive.
type SimpleInputDialog struct {
*tview.Box
height int
layout *tview.Flex
textview *tview.TextView
@ -96,16 +95,44 @@ func (d *SimpleInputDialog) Hide() {
d.display = false
}
func (d *SimpleInputDialog) setLayout(haveDesc bool) {
d.layout.Clear()
descHeight := siDescHeight
if !haveDesc {
descHeight = 1
d.height = siDialogHeight - 3 //nolint:mnd
} else {
d.height = siDialogHeight
}
d.layout.AddItem(
tview.NewFlex().SetDirection(tview.FlexColumn).
AddItem(utils.EmptyBoxSpace(style.DialogBgColor), 1, 0, false).
AddItem(d.textview, 0, 1, false).
AddItem(utils.EmptyBoxSpace(style.DialogBgColor), 1, 0, false),
descHeight, 0, true)
d.layout.AddItem(tview.NewFlex().SetDirection(tview.FlexColumn).
AddItem(utils.EmptyBoxSpace(style.DialogBgColor), 1, 0, false).
AddItem(d.input, 0, 1, false).
AddItem(utils.EmptyBoxSpace(style.DialogBgColor), 1, 0, false),
1, 0, true)
d.layout.AddItem(d.form, DialogFormHeight, 0, true)
d.layout.SetBorder(true)
d.layout.SetBorderColor(style.DialogBorderColor)
d.layout.SetBackgroundColor(style.DialogBgColor)
}
// SetDescription sets dialogs description.
func (d *SimpleInputDialog) SetDescription(text string) {
d.textview.Clear()
haveDesc := true
_, err := fmt.Fprintf(d.textview, "\n%s", text)
if err != nil {
log.Error().Msgf("failed to write to ui text view: %s", err.Error())
}
fmt.Fprintf(d.textview, "\n%s", text)
if len(text) == 0 {
haveDesc = false
@ -266,8 +293,8 @@ func (d *SimpleInputDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
@ -289,34 +316,3 @@ func (d *SimpleInputDialog) SetCancelFunc(handler func()) *SimpleInputDialog {
return d
}
func (d *SimpleInputDialog) setLayout(haveDesc bool) {
d.layout.Clear()
descHeight := siDescHeight
if !haveDesc {
descHeight = 1
d.height = siDialogHeight - 3 //nolint:mnd
} else {
d.height = siDialogHeight
}
d.layout.AddItem(
tview.NewFlex().SetDirection(tview.FlexColumn).
AddItem(utils.EmptyBoxSpace(style.DialogBgColor), 1, 0, false).
AddItem(d.textview, 0, 1, false).
AddItem(utils.EmptyBoxSpace(style.DialogBgColor), 1, 0, false),
descHeight, 0, true)
d.layout.AddItem(tview.NewFlex().SetDirection(tview.FlexColumn).
AddItem(utils.EmptyBoxSpace(style.DialogBgColor), 1, 0, false).
AddItem(d.input, 0, 1, false).
AddItem(utils.EmptyBoxSpace(style.DialogBgColor), 1, 0, false),
1, 0, true)
d.layout.AddItem(d.form, DialogFormHeight, 0, true)
d.layout.SetBorder(true)
d.layout.SetBorderColor(style.DialogBorderColor)
d.layout.SetBackgroundColor(style.DialogBgColor)
}

View File

@ -13,7 +13,6 @@ import (
// MessageDialog is a simaple message dialog primitive.
type MessageDialog struct {
*tview.Box
layout *tview.Flex
infoType *tview.InputField
textview *tview.TextView
@ -133,7 +132,7 @@ func (d *MessageDialog) SetText(headerType messageInfo, headerMessage string, me
case MessagePodInfo:
msgTypeLabel = "POD ID:"
case MessageContainerInfo:
msgTypeLabel = utils.ContainerIDLabel
msgTypeLabel = "CONTAINER ID:"
case MessageVolumeInfo:
msgTypeLabel = "VOLUME NAME:"
case MessageImageInfo:
@ -237,8 +236,8 @@ func (d *MessageDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}

View File

@ -17,7 +17,6 @@ const (
// ProgressDialog represents progress bar permitive.
type ProgressDialog struct {
*tview.Box
x int
y int
width int
@ -47,8 +46,8 @@ func (d *ProgressDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, _ := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, _ := d.Box.GetInnerRect()
tickStr := d.tickStr(width)
tview.Print(screen, tickStr, x, y, width, tview.AlignLeft, tcell.ColorYellow)
}
@ -91,7 +90,7 @@ func (d *ProgressDialog) IsDisplay() bool {
}
// Focus is called when this primitive receives focus.
func (d *ProgressDialog) Focus(delegate func(p tview.Primitive)) {}
func (d *ProgressDialog) Focus(delegate func(p tview.Primitive)) {} //nolint:revive
// HasFocus returns whether or not this primitive has focus.
func (d *ProgressDialog) HasFocus() bool {
@ -100,16 +99,12 @@ func (d *ProgressDialog) HasFocus() bool {
// InputHandler returns input handler function for this primitive.
func (d *ProgressDialog) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { //nolint:revive
log.Debug().Msgf("progress dialog: event %v received", event)
})
}
func (d *ProgressDialog) tickStr(maxCount int) string {
prgStr := prgCell + prgCell + prgCell + prgCell
prgHeadStr := ""
hWidth := 0
prgEndStr := ""
barColor := style.GetColorHex(style.PrgBarColor)
counter := d.counterValue
@ -119,11 +114,18 @@ func (d *ProgressDialog) tickStr(maxCount int) string {
d.counterValue = 0
}
prgHeadStr := ""
hWidth := 0
prgEndStr := ""
prgStr := ""
for range d.counterValue {
prgHeadStr += fmt.Sprintf("[black::]%s", prgCell) //nolint:perfsprint
hWidth++
}
prgStr = prgCell + prgCell + prgCell + prgCell
for range maxCount + hWidth + 4 {
prgEndStr += fmt.Sprintf("[black::]%s", prgCell) //nolint:perfsprint
}

View File

@ -1,262 +0,0 @@
package dialogs
import (
"github.com/containers/podman-tui/ui/style"
"github.com/containers/podman-tui/ui/utils"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"github.com/rs/zerolog/log"
)
const (
sortDialogMaxWidth = 30
sortDialogMaxHeight = 8
)
const (
sortDialogOptionsFocus = 0 + iota
sortDialogOrderFocus
sortDialogFormFocus
)
type SortDialog struct {
*tview.Box
layout *tview.Flex
sortBy *tview.DropDown
sortOrder *tview.DropDown
form *tview.Form
display bool
selectHandler func()
cancelHandler func()
focusElement int
}
func NewSortDialog(options []string, defaultOption int) *SortDialog {
sd := SortDialog{
Box: tview.NewBox(),
form: tview.NewForm(),
layout: tview.NewFlex(),
focusElement: sortDialogOptionsFocus,
}
sortOrder := "Sort order:"
sd.sortOrder = tview.NewDropDown()
sd.sortOrder.SetLabel(sortOrder)
sd.sortOrder.SetLabelWidth(len(sortOrder) + 1)
sd.sortOrder.SetTitleAlign(tview.AlignRight)
sd.sortOrder.SetLabelColor(style.DialogFgColor)
sd.sortOrder.SetBackgroundColor(style.DialogBgColor)
sd.sortOrder.SetOptions([]string{"ascending", "descending"}, nil)
sd.sortOrder.SetListStyles(style.DropDownUnselected, style.DropDownSelected)
sd.sortOrder.SetFieldBackgroundColor(style.InputFieldBgColor)
sd.sortOrder.SetFieldWidth(0)
sd.sortOrder.SetCurrentOption(0)
sd.sortBy = tview.NewDropDown()
sd.sortBy.SetLabel("Sort by:")
sd.sortBy.SetLabelWidth(len(sortOrder) + 1)
sd.sortBy.SetTitleAlign(tview.AlignRight)
sd.sortBy.SetLabelColor(style.DialogFgColor)
sd.sortBy.SetBackgroundColor(style.DialogBgColor)
sd.sortBy.SetOptions(options, nil)
sd.sortBy.SetListStyles(style.DropDownUnselected, style.DropDownSelected)
sd.sortBy.SetFieldBackgroundColor(style.InputFieldBgColor)
sd.sortBy.SetFieldWidth(0)
if len(options) > 0 {
sd.sortBy.SetCurrentOption(defaultOption)
}
// form
sd.form.AddButton("Cancel", nil)
sd.form.AddButton(" Sort ", nil)
sd.form.SetButtonsAlign(tview.AlignRight)
sd.form.SetBackgroundColor(style.DialogBgColor)
sd.form.SetButtonBackgroundColor(style.ButtonBgColor)
// main layout
sd.layout.SetDirection(tview.FlexRow)
sd.layout.SetBackgroundColor(style.DialogBgColor)
sd.layout.SetBorder(true)
sd.layout.SetBorderColor(style.DialogBorderColor)
sd.layout.AddItem(sd.sortBy, 0, 1, true)
sd.layout.AddItem(utils.EmptyBoxSpace(style.DialogBgColor), 0, 1, false)
sd.layout.AddItem(sd.sortOrder, 0, 1, true)
sd.layout.AddItem(sd.form, DialogFormHeight, 0, true)
return &sd
}
// Display displays this primitive.
func (d *SortDialog) Display() {
d.display = true
}
// IsDisplay returns true if primitive is shown.
func (d *SortDialog) IsDisplay() bool {
return d.display
}
// Hide stops displaying this primitive.
func (d *SortDialog) Hide() {
d.display = false
d.focusElement = sortDialogOptionsFocus
d.form.SetFocus(0)
}
// HasFocus returns whether or not this primitive has focus.
func (d *SortDialog) HasFocus() bool {
if d.sortBy.HasFocus() || d.sortOrder.HasFocus() {
return true
}
if d.form.HasFocus() || d.layout.HasFocus() {
return true
}
return d.Box.HasFocus()
}
// Focus is called when this primitive receives focus.
func (d *SortDialog) Focus(delegate func(p tview.Primitive)) {
switch d.focusElement {
case sortDialogOptionsFocus:
delegate(d.sortBy)
case sortDialogOrderFocus:
delegate(d.sortOrder)
case sortDialogFormFocus:
sortButton := d.form.GetButton(1)
sortButton.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyTab {
d.focusElement = sortDialogOptionsFocus
d.Focus(delegate)
d.form.SetFocus(0)
return nil
}
if event.Key() == tcell.KeyEnter {
d.selectHandler()
}
return event
})
delegate(d.form)
}
}
// InputHandler returns input handler function for this primitive.
func (d *SortDialog) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { //nolint:cyclop
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
log.Debug().Msgf("sort dialog dialog: event %v received", event)
if event.Key() == utils.SwitchFocusKey.Key {
switch d.focusElement {
case sortDialogOptionsFocus:
d.focusElement = sortDialogOrderFocus
case sortDialogOrderFocus:
d.focusElement = sortDialogFormFocus
}
}
// dropdown widgets shall handle events before "Esc" key handler
if d.sortBy.HasFocus() {
event = utils.ParseKeyEventKey(event)
if sortByHandler := d.sortBy.InputHandler(); sortByHandler != nil {
sortByHandler(event, setFocus)
return
}
}
if d.sortOrder.HasFocus() {
event = utils.ParseKeyEventKey(event)
if sortOrderHandler := d.sortOrder.InputHandler(); sortOrderHandler != nil {
sortOrderHandler(event, setFocus)
return
}
}
if d.form.HasFocus() {
if event.Key() == tcell.KeyEsc {
d.cancelHandler()
return
}
if formHandler := d.form.InputHandler(); formHandler != nil {
formHandler(event, setFocus)
return
}
}
})
}
// SetRect set rects for this primitive.
func (d *SortDialog) SetRect(x, y, width, height int) {
if width > sortDialogMaxWidth {
emptySpace := (width - sortDialogMaxWidth) / 2 //nolint:mnd
x += emptySpace
width = sortDialogMaxWidth
}
if height > sortDialogMaxHeight {
emptySpace := (height - sortDialogMaxHeight) / 2 //nolint:mnd
y += emptySpace
height = sortDialogMaxHeight
}
d.Box.SetRect(x, y, width, height)
}
// Draw draws this primitive onto the screen.
func (d *SortDialog) Draw(screen tcell.Screen) {
if !d.display {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
// SetSelectFunc sets form sort button selected function.
func (d *SortDialog) SetSelectFunc(handler func(string, bool)) *SortDialog {
selectHandler := func() {
if d.sortBy.GetOptionCount() > 0 {
_, sortOpt := d.sortBy.GetCurrentOption()
_, order := d.sortOrder.GetCurrentOption()
ascending := true
if order == "descending" {
ascending = false
}
handler(sortOpt, ascending)
}
d.Hide()
}
d.selectHandler = selectHandler
return d
}
// SetCancelFunc sets form cancel button selected function.
func (d *SortDialog) SetCancelFunc(handler func()) *SortDialog {
d.cancelHandler = handler
cancelButton := d.form.GetButton(d.form.GetButtonCount() - 2) //nolint:mnd
cancelButton.SetSelectedFunc(handler)
return d
}

View File

@ -25,7 +25,6 @@ const (
// TopDialog is a simple dialog with pod/container top result table.
type TopDialog struct {
*tview.Box
layout *tview.Flex
table *tview.Table
info *tview.InputField
@ -175,8 +174,8 @@ func (d *TopDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
@ -188,9 +187,28 @@ func (d *TopDialog) SetCancelFunc(handler func()) *TopDialog {
return d
}
func (d *TopDialog) initTable() {
bgColor := style.TableHeaderBgColor
fgColor := style.TableHeaderFgColor
d.table.Clear()
d.table.SetFixed(1, 1)
d.table.SetSelectable(true, false)
for i := range d.tableHeaders {
d.table.SetCell(0, i,
tview.NewTableCell(fmt.Sprintf("[%s::b]%s", style.GetColorHex(fgColor), strings.ToUpper(d.tableHeaders[i]))).
SetExpansion(0).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
}
}
// UpdateResults updates result table.
func (d *TopDialog) UpdateResults(infoType topInfo, id string, name string, data [][]string) {
headerInfo := utils.ContainerIDLabel
headerInfo := "CONTAINER ID:"
if infoType == TopPodInfo {
headerInfo = "POD ID:"
}
@ -279,25 +297,6 @@ func (d *TopDialog) UpdateResults(infoType topInfo, id string, name string, data
}
}
func (d *TopDialog) initTable() {
bgColor := style.TableHeaderBgColor
fgColor := style.TableHeaderFgColor
d.table.Clear()
d.table.SetFixed(1, 1)
d.table.SetSelectable(true, false)
for i := range d.tableHeaders {
d.table.SetCell(0, i,
tview.NewTableCell(fmt.Sprintf("[%s::b]%s", style.GetColorHex(fgColor), strings.ToUpper(d.tableHeaders[i]))).
SetExpansion(0).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
}
}
func (d *TopDialog) getCommandWidth() int {
var (
commandWidth int

View File

@ -12,7 +12,6 @@ import (
// Help is a help primitive dialog.
type Help struct {
*tview.Box
title string
layout *tview.Flex
}
@ -44,8 +43,8 @@ func NewHelp(appName string, appVersion string) *Help {
SetTextAlign(tview.AlignLeft)
appinfo.SetBackgroundColor(bgColor)
licenseInfo := "released under the Apache License 2.0."
appInfoText := fmt.Sprintf("%s %s - %s", appName, appVersion, licenseInfo)
licenseInfo := "Released under the Apache License 2.0."
appInfoText := fmt.Sprintf("%s %s - (C) 2022 podman-tui dev team.\n%s", appName, appVersion, licenseInfo)
appinfo.SetText(appInfoText)
appinfo.SetTextColor(headerColor)
@ -55,7 +54,7 @@ func NewHelp(appName string, appVersion string) *Help {
rowIndex := 0
colIndex := 0
needInit := true
maxRowIndex := len(utils.UIKeysBindings) / 2 //nolint:mnd
maxRowIndex := len(utils.UIKeysBindings)/2 + 1 //nolint:mnd
for i := range utils.UIKeysBindings {
if i >= maxRowIndex {
@ -83,7 +82,7 @@ func NewHelp(appName string, appVersion string) *Help {
// appinfo and appkeys layout
mlayout := tview.NewFlex().SetDirection(tview.FlexRow)
mlayout.AddItem(appinfo, 1, 0, false)
mlayout.AddItem(appinfo, 2, 0, false) //nolint:mnd
mlayout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, false)
mlayout.AddItem(keyinfo, 0, 1, false)
mlayout.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, false)
@ -117,12 +116,12 @@ func (help *Help) Focus(delegate func(p tview.Primitive)) {
// Draw draws this primitive onto the screen.
func (help *Help) Draw(screen tcell.Screen) {
x, y, width, height := help.GetInnerRect()
x, y, width, height := help.Box.GetInnerRect()
if height <= 3 { //nolint:mnd
return
}
help.DrawForSubclass(screen, help)
help.Box.DrawForSubclass(screen, help)
help.layout.SetRect(x, y, width, height)
help.layout.Draw(screen)
}

View File

@ -7,7 +7,6 @@ import (
"github.com/containers/podman-tui/pdcs/images"
"github.com/containers/podman-tui/ui/dialogs"
"github.com/containers/podman-tui/ui/style"
"github.com/containers/podman-tui/ui/utils"
"github.com/rs/zerolog/log"
)
@ -23,7 +22,7 @@ func (img *Images) runCommand(cmd string) { //nolint:cyclop
img.importDialog.Display()
case "inspect":
img.inspect()
case utils.PruneCommandLabel:
case "prune": //nolint:goconst
img.cprune()
case "push":
img.cpush()
@ -140,8 +139,6 @@ func (img *Images) history() {
if err != nil {
title := fmt.Sprintf("IMAGE (%s) HISTORY ERROR", img.selectedID)
img.displayError(title, err)
return
}
img.historyDialog.SetImageInfo(img.selectedID, img.selectedName)
@ -223,9 +220,10 @@ func (img *Images) prune() {
if err != nil {
img.displayError("IMAGE PRUNE ERROR", err)
}
img.appFocusHandler()
img.appFocusHandler()
return
}
}
go prune()
@ -250,17 +248,17 @@ func (img *Images) push() {
img.progressDialog.Display()
push := func() {
err := images.Push(img.selectedID, pushOptions)
img.progressDialog.Hide()
if err != nil {
if err := images.Push(img.selectedID, pushOptions); err != nil {
img.progressDialog.Hide()
title := fmt.Sprintf("IMAGE (%s) PUSH ERROR", img.selectedID)
img.displayError(title, err)
img.appFocusHandler()
return
}
img.appFocusHandler()
img.progressDialog.Hide()
}
go push()
@ -347,9 +345,10 @@ func (img *Images) save() {
title := fmt.Sprintf("IMAGE (%s) SAVE ERROR", img.selectedID)
img.displayError(title, err)
}
img.appFocusHandler()
img.appFocusHandler()
return
}
}
go saveFunc()
@ -362,17 +361,15 @@ func (img *Images) search(term string) {
search := func(term string) {
result, err := images.Search(term)
img.progressDialog.Hide()
if err != nil {
title := fmt.Sprintf("IMAGE (%s) SEARCH ERROR", img.selectedID)
img.displayError(title, err)
img.appFocusHandler()
}
img.searchDialog.UpdateResults(result)
img.appFocusHandler()
img.progressDialog.Hide()
}
go search(term)
@ -405,8 +402,7 @@ func (img *Images) ctag() {
}
func (img *Images) tag(tag string) {
err := images.Tag(img.selectedID, tag)
if err != nil {
if err := images.Tag(img.selectedID, tag); err != nil {
title := fmt.Sprintf("IMAGE (%s) TAG ERROR", img.selectedID)
img.displayError(title, err)
}
@ -467,8 +463,7 @@ func (img *Images) tree() {
}
func (img *Images) untag(id string) {
err := images.Untag(id)
if err != nil {
if err := images.Untag(id); err != nil {
title := fmt.Sprintf("IMAGE (%s) UNTAG ERROR", img.selectedID)
img.displayError(title, err)
@ -482,15 +477,14 @@ func (img *Images) pull(image string) {
pull := func(name string) {
err := images.Pull(name)
img.progressDialog.Hide()
if err != nil {
title := fmt.Sprintf("IMAGE (%s) PULL ERROR", img.selectedID)
img.displayError(title, err)
img.appFocusHandler()
}
img.progressDialog.Hide()
}
go pull(image)

View File

@ -2,9 +2,7 @@ package images
import (
"fmt"
"sort"
"strings"
"time"
"github.com/containers/podman-tui/pdcs/images"
"github.com/containers/podman-tui/ui/style"
@ -12,19 +10,6 @@ import (
"github.com/rs/zerolog/log"
)
// SortView sorts data view called from sort dialog.
func (img *Images) SortView(option string, ascending bool) {
log.Debug().Msgf("view: images sort by %s", option)
img.imagesList.mu.Lock()
defer img.imagesList.mu.Unlock()
img.imagesList.sortBy = option
img.imagesList.ascending = ascending
sort.Sort(imgListSorted{img.imagesList.report, option, ascending})
}
// UpdateData retrieves images list data.
func (img *Images) UpdateData() {
images, err := images.List()
@ -39,8 +24,6 @@ func (img *Images) UpdateData() {
img.imagesList.mu.Lock()
defer img.imagesList.mu.Unlock()
sort.Sort(imgListSorted{images, img.imagesList.sortBy, img.imagesList.ascending})
img.imagesList.report = images
}
@ -78,41 +61,3 @@ func (img *Images) ClearData() {
img.table.SetTitle(fmt.Sprintf("[::b]%s[0]", strings.ToUpper(img.title)))
}
type lprSort []images.ImageListReporter
func (a lprSort) Len() int { return len(a) }
func (a lprSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type imgListSorted struct {
lprSort
option string
ascending bool
}
func (a imgListSorted) Less(i, j int) bool {
switch a.option {
case "size":
if a.ascending {
return a.lprSort[i].Size < a.lprSort[j].Size
}
return a.lprSort[i].Size > a.lprSort[j].Size
case "created":
icreated := time.Unix(a.lprSort[i].Created, 0).UTC()
jcreated := time.Unix(a.lprSort[j].Created, 0).UTC()
if a.ascending {
return icreated.After(jcreated)
}
return icreated.Before(jcreated)
}
if a.ascending {
return a.lprSort[i].Repository < a.lprSort[j].Repository
}
return a.lprSort[i].Repository > a.lprSort[j].Repository
}

View File

@ -5,9 +5,9 @@ import (
)
// Draw draws this primitive onto the screen.
func (img *Images) Draw(screen tcell.Screen) {
img.DrawForSubclass(screen, img)
img.SetBorder(false)
func (img *Images) Draw(screen tcell.Screen) { //nolint:cyclop
img.Box.DrawForSubclass(screen, img)
img.Box.SetBorder(false)
imagewViewX, imagewViewY, imagewViewW, imagewViewH := img.GetInnerRect()
@ -18,21 +18,108 @@ func (img *Images) Draw(screen tcell.Screen) {
img.table.Draw(screen)
x, y, width, height := img.table.GetInnerRect()
for _, dialog := range img.getInnerDialogs() {
if dialog.IsDisplay() {
dialog.SetRect(x, y, width, height)
dialog.Draw(screen)
// error dialog
if img.errorDialog.IsDisplay() {
img.errorDialog.SetRect(x, y, width, height)
img.errorDialog.Draw(screen)
break
}
return
}
for _, dialog := range img.getInnerTopDialogs() {
if dialog.IsDisplay() {
dialog.SetRect(x, y, width, height)
dialog.Draw(screen)
// command dialog
if img.cmdDialog.IsDisplay() {
img.cmdDialog.SetRect(x, y, width, height)
img.cmdDialog.Draw(screen)
break
return
}
// command input dialog
if img.cmdInputDialog.IsDisplay() {
img.cmdInputDialog.SetRect(x, y, width, height)
img.cmdInputDialog.Draw(screen)
return
}
// message dialog
if img.messageDialog.IsDisplay() {
if img.messageDialog.IsDisplayFullSize() {
img.messageDialog.SetRect(imagewViewX, imagewViewY, imagewViewW, imagewViewH)
} else {
img.messageDialog.SetRect(x, y, width, height+1)
}
img.messageDialog.Draw(screen)
return
}
// confirm dialog
if img.confirmDialog.IsDisplay() {
img.confirmDialog.SetRect(x, y, width, height)
img.confirmDialog.Draw(screen)
return
}
// search dialog
if img.searchDialog.IsDisplay() {
img.searchDialog.SetRect(imagewViewX, imagewViewY, imagewViewW, imagewViewH)
img.searchDialog.Draw(screen)
}
// progress dialog
if img.progressDialog.IsDisplay() {
img.progressDialog.SetRect(x, y, width, height)
img.progressDialog.Draw(screen)
}
// history dialog
if img.historyDialog.IsDisplay() {
img.historyDialog.SetRect(imagewViewX, imagewViewY, imagewViewW, imagewViewH)
img.historyDialog.Draw(screen)
return
}
// build dialog
if img.buildDialog.IsDisplay() {
img.buildDialog.SetRect(x, y, width, height)
img.buildDialog.Draw(screen)
return
}
// build progress dialog
if img.buildPrgDialog.IsDisplay() {
img.buildPrgDialog.SetRect(x, y, width, height)
img.buildPrgDialog.Draw(screen)
return
}
// save dialog
if img.saveDialog.IsDisplay() {
img.saveDialog.SetRect(x, y, width, height)
img.saveDialog.Draw(screen)
return
}
// import dialog
if img.importDialog.IsDisplay() {
img.importDialog.SetRect(x, y, width, height)
img.importDialog.Draw(screen)
return
}
// push dialog
if img.pushDialog.IsDisplay() {
img.pushDialog.SetRect(x, y, width, height)
img.pushDialog.Draw(screen)
return
}
}

View File

@ -10,7 +10,6 @@ import (
"github.com/containers/podman-tui/ui/dialogs"
"github.com/containers/podman-tui/ui/images/imgdialogs"
"github.com/containers/podman-tui/ui/style"
"github.com/containers/podman-tui/ui/utils"
"github.com/rivo/tview"
)
@ -38,22 +37,20 @@ var (
// Images implements the images primitive.
type Images struct {
*tview.Box
title string
headers []string
table *tview.Table
errorDialog *dialogs.ErrorDialog
progressDialog *dialogs.ProgressDialog
cmdDialog *dialogs.CommandDialog
cmdInputDialog *dialogs.SimpleInputDialog
messageDialog *dialogs.MessageDialog
confirmDialog *dialogs.ConfirmDialog
sortDialog *dialogs.SortDialog
searchDialog *imgdialogs.ImageSearchDialog
historyDialog *imgdialogs.ImageHistoryDialog
importDialog *imgdialogs.ImageImportDialog
buildDialog *imgdialogs.ImageBuildDialog
buildPrgDialog *imgdialogs.ImageBuildProgressDialog
progressDialog *dialogs.ProgressDialog
saveDialog *imgdialogs.ImageSaveDialog
pushDialog *imgdialogs.ImagePushDialog
imagesList imageListReport
@ -65,10 +62,8 @@ type Images struct {
}
type imageListReport struct {
mu sync.Mutex
report []images.ImageListReporter
sortBy string
ascending bool
mu sync.Mutex
report []images.ImageListReporter
}
// NewImages returns images page view.
@ -78,11 +73,9 @@ func NewImages() *Images {
title: "images",
headers: []string{"repository", "tag", "image id", "created at", "size"},
errorDialog: dialogs.NewErrorDialog(),
progressDialog: dialogs.NewProgressDialog(),
cmdInputDialog: dialogs.NewSimpleInputDialog(""),
messageDialog: dialogs.NewMessageDialog(""),
confirmDialog: dialogs.NewConfirmDialog(),
sortDialog: dialogs.NewSortDialog([]string{"repository", "created", "size"}, 1),
searchDialog: imgdialogs.NewImageSearchDialog(),
historyDialog: imgdialogs.NewImageHistoryDialog(),
importDialog: imgdialogs.NewImageImportDialog(),
@ -90,7 +83,7 @@ func NewImages() *Images {
buildPrgDialog: imgdialogs.NewImageBuildProgressDialog(),
saveDialog: imgdialogs.NewImageSaveDialog(),
pushDialog: imgdialogs.NewImagePushDialog(),
imagesList: imageListReport{sortBy: "created", ascending: true},
progressDialog: dialogs.NewProgressDialog(),
}
images.cmdDialog = dialogs.NewCommandDialog([][]string{
@ -158,7 +151,7 @@ func NewImages() *Images {
images.confirmDialog.Hide()
switch images.confirmData {
case utils.PruneCommandLabel:
case "prune":
images.prune()
case "rm":
images.remove()
@ -212,10 +205,6 @@ func NewImages() *Images {
images.pushDialog.SetPushFunc(images.push)
images.pushDialog.SetCancelFunc(images.pushDialog.Hide)
// set sort dialog functions
images.sortDialog.SetSelectFunc(images.SortView)
images.sortDialog.SetCancelFunc(images.sortDialog.Hide)
return images
}
@ -230,89 +219,155 @@ func (img *Images) GetTitle() string {
}
// HasFocus returns whether or not this primitive has focus.
func (img *Images) HasFocus() bool {
if img.SubDialogHasFocus() {
func (img *Images) HasFocus() bool { //nolint:cyclop
if img.table.HasFocus() || img.messageDialog.HasFocus() {
return true
}
if img.table.HasFocus() || img.Box.HasFocus() {
if img.cmdDialog.HasFocus() || img.cmdInputDialog.HasFocus() {
return true
}
return false
if img.confirmDialog.HasFocus() || img.errorDialog.HasFocus() {
return true
}
if img.searchDialog.HasFocus() || img.progressDialog.HasFocus() {
return true
}
if img.historyDialog.HasFocus() || img.buildDialog.HasFocus() {
return true
}
if img.buildPrgDialog.HasFocus() || img.saveDialog.HasFocus() {
return true
}
if img.importDialog.HasFocus() || img.pushDialog.HasFocus() {
return true
}
return img.Box.HasFocus()
}
// SubDialogHasFocus returns whether or not sub dialog primitive has focus.
func (img *Images) SubDialogHasFocus() bool {
for _, dialog := range img.getInnerDialogs() {
if dialog.HasFocus() {
return true
}
func (img *Images) SubDialogHasFocus() bool { //nolint:cyclop
if img.historyDialog.HasFocus() || img.messageDialog.HasFocus() {
return true
}
for _, dialog := range img.getInnerTopDialogs() {
if dialog.HasFocus() {
return true
}
if img.cmdDialog.HasFocus() || img.cmdInputDialog.HasFocus() {
return true
}
return false
if img.confirmDialog.HasFocus() || img.errorDialog.HasFocus() {
return true
}
if img.searchDialog.HasFocus() || img.progressDialog.HasFocus() {
return true
}
if img.buildDialog.HasFocus() || img.buildPrgDialog.HasFocus() {
return true
}
if img.saveDialog.HasFocus() || img.importDialog.HasFocus() {
return true
}
return img.pushDialog.HasFocus()
}
// Focus is called when this primitive receives focus.
func (img *Images) Focus(delegate func(p tview.Primitive)) {
// since error and confirm dialog can get focus on top of other dialogs
func (img *Images) Focus(delegate func(p tview.Primitive)) { //nolint:cyclop
// error dialog
if img.errorDialog.IsDisplay() {
delegate(img.errorDialog)
return
}
// command dialog
if img.cmdDialog.IsDisplay() {
delegate(img.cmdDialog)
return
}
// command input dialog
if img.cmdInputDialog.IsDisplay() {
delegate(img.cmdInputDialog)
return
}
// message dialog
if img.messageDialog.IsDisplay() {
delegate(img.messageDialog)
return
}
// confirm dialog
if img.confirmDialog.IsDisplay() {
delegate(img.confirmDialog)
return
}
for _, dialog := range img.getInnerDialogs() {
if dialog.IsDisplay() {
delegate(dialog)
// search dialog
if img.searchDialog.IsDisplay() {
delegate(img.searchDialog)
return
}
return
}
for _, dialog := range img.getInnerTopDialogs() {
if dialog.IsDisplay() {
delegate(dialog)
// history dialog
if img.historyDialog.IsDisplay() {
delegate(img.historyDialog)
return
}
return
}
// build dialog
if img.buildDialog.IsDisplay() {
delegate(img.buildDialog)
return
}
// build progress dialog
if img.buildPrgDialog.IsDisplay() {
delegate(img.buildPrgDialog)
return
}
// save dialog
if img.saveDialog.IsDisplay() {
delegate(img.saveDialog)
return
}
// import dialog
if img.importDialog.IsDisplay() {
delegate(img.importDialog)
return
}
// push dialog
if img.pushDialog.IsDisplay() {
delegate(img.pushDialog)
return
}
delegate(img.table)
}
// HideAllDialogs hides all sub dialogs.
func (img *Images) HideAllDialogs() {
for _, dialog := range img.getInnerDialogs() {
if dialog.IsDisplay() {
dialog.Hide()
}
}
for _, dialog := range img.getInnerTopDialogs() {
if dialog.IsDisplay() {
dialog.Hide()
}
}
}
// SetFastRefreshChannel sets channel for fastRefresh func.
func (img *Images) SetFastRefreshChannel(refresh chan bool) {
img.fastRefreshChan = refresh
}
func (img *Images) getSelectedItem() (string, string) {
if img.table.GetRowCount() <= 1 {
return "", ""
@ -327,30 +382,62 @@ func (img *Images) getSelectedItem() (string, string) {
return imageID, imageName
}
func (img *Images) getInnerDialogs() []utils.UIDialog {
dialogs := []utils.UIDialog{
img.cmdDialog,
img.cmdInputDialog,
img.messageDialog,
img.searchDialog,
img.historyDialog,
img.importDialog,
img.buildDialog,
img.buildPrgDialog,
img.saveDialog,
img.pushDialog,
img.sortDialog,
// HideAllDialogs hides all sub dialogs.
func (img *Images) HideAllDialogs() { //nolint:cyclop
if img.errorDialog.IsDisplay() {
img.errorDialog.Hide()
}
return dialogs
}
func (img *Images) getInnerTopDialogs() []utils.UIDialog {
dialogs := []utils.UIDialog{
img.errorDialog,
img.progressDialog,
img.confirmDialog,
if img.progressDialog.IsDisplay() {
img.progressDialog.Hide()
}
return dialogs
if img.cmdDialog.IsDisplay() {
img.cmdDialog.Hide()
}
if img.cmdInputDialog.IsDisplay() {
img.cmdInputDialog.Hide()
}
if img.messageDialog.IsDisplay() {
img.messageDialog.Hide()
}
if img.searchDialog.IsDisplay() {
img.searchDialog.Hide()
}
if img.confirmDialog.IsDisplay() {
img.confirmDialog.Hide()
}
if img.historyDialog.IsDisplay() {
img.historyDialog.Hide()
}
if img.buildDialog.IsDisplay() {
img.buildDialog.Hide()
}
if img.buildPrgDialog.IsDisplay() {
img.buildPrgDialog.Hide()
}
if img.saveDialog.IsDisplay() {
img.saveDialog.Hide()
}
if img.importDialog.IsDisplay() {
img.importDialog.Hide()
}
if img.pushDialog.IsDisplay() {
img.pushDialog.Hide()
}
}
// SetFastRefreshChannel sets channel for fastRefresh func.
func (img *Images) SetFastRefreshChannel(refresh chan bool) {
img.fastRefreshChan = refresh
}

View File

@ -71,7 +71,6 @@ const (
// ImageBuildDialog represents image build dialog primitive.
type ImageBuildDialog struct {
*tview.Box
layout *tview.Flex
form *tview.Form
categoryLabels []string
@ -491,6 +490,117 @@ func NewImageBuildDialog() *ImageBuildDialog { //nolint:maintidx
return buildDialog
}
func (d *ImageBuildDialog) setupLayout() {
bgColor := style.DialogBgColor
// basic info page
d.basicInfoPage.SetDirection(tview.FlexRow)
d.basicInfoPage.AddItem(d.contextDirectoryPath, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.containerFilePath, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.pullPolicyField, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.tagField, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.registryField, 1, 0, true)
d.basicInfoPage.SetBackgroundColor(bgColor)
// layers setup page
secondRowLayout := tview.NewFlex().SetDirection(tview.FlexColumn)
secondRowLayout.SetBackgroundColor(bgColor)
secondRowLayout.AddItem(d.formatField, 0, 2, true) //nolint:mnd
secondRowLayout.AddItem(d.SquashField, 0, 1, true)
secondRowLayout.AddItem(d.layersField, 0, 1, true)
secondRowLayout.AddItem(d.noCacheField, 0, 1, true)
cntRmRowLayout := tview.NewFlex().SetDirection(tview.FlexColumn)
cntRmRowLayout.SetBackgroundColor(bgColor)
cntRmRowLayout.AddItem(d.forceRemoveCntField, 0, 1, true)
cntRmRowLayout.AddItem(d.removeCntField, 0, 2, true) //nolint:mnd
// build setup page
d.buildInfoPage.SetDirection(tview.FlexRow)
d.buildInfoPage.AddItem(d.buildArgsField, 1, 0, true)
d.buildInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.buildInfoPage.AddItem(secondRowLayout, 1, 0, true)
d.buildInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.buildInfoPage.AddItem(d.labelsField, 1, 0, true)
d.buildInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.buildInfoPage.AddItem(d.annotationsField, 1, 0, true)
d.buildInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.buildInfoPage.AddItem(cntRmRowLayout, 1, 0, true)
d.buildInfoPage.SetBackgroundColor(bgColor)
// security options page
d.securityOptsPage.SetDirection(tview.FlexRow)
d.securityOptsPage.AddItem(d.selinuxLabelField, 1, 0, true)
d.securityOptsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.securityOptsPage.AddItem(d.apparmorProfileField, 1, 0, true)
d.securityOptsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.securityOptsPage.AddItem(d.seccompProfilePathField, 1, 0, true)
// networking setup page
netFirstRow := tview.NewFlex().SetDirection(tview.FlexColumn)
netFirstRow.SetBackgroundColor(bgColor)
netFirstRow.AddItem(d.networkField, 0, 1, true)
netFirstRow.AddItem(d.httpProxyField, 0, 1, true)
d.networkingPage.SetDirection(tview.FlexRow)
d.networkingPage.AddItem(netFirstRow, 1, 0, true)
d.networkingPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.networkingPage.AddItem(d.addHostField, 1, 0, true)
d.networkingPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.networkingPage.AddItem(d.dnsServersField, 1, 0, true)
d.networkingPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.networkingPage.AddItem(d.dnsOptionsField, 1, 0, true)
d.networkingPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.networkingPage.AddItem(d.dnsSearchField, 1, 0, true)
d.networkingPage.SetBackgroundColor(bgColor)
// capability page
d.capabilityPage.SetDirection(tview.FlexRow)
d.capabilityPage.AddItem(d.addCapabilityField, 1, 0, true)
d.capabilityPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.capabilityPage.AddItem(d.removeCapabilityField, 1, 0, true)
// cpu and memory page
cpuSetRow := tview.NewFlex().SetDirection(tview.FlexColumn)
cpuSetRow.AddItem(d.cpuSetCpusField, 0, 1, true)
cpuSetRow.AddItem(d.cpuSetMemsField, 0, 1, true)
// memory and swap
memSwapRow := tview.NewFlex().SetDirection(tview.FlexColumn)
memSwapRow.AddItem(d.memoryField, 0, 1, true)
memSwapRow.AddItem(d.memorySwapField, 0, 1, true)
d.cpuMemoryPage.SetDirection(tview.FlexRow)
d.cpuMemoryPage.AddItem(d.cpuPeriodField, 0, 1, true)
d.cpuMemoryPage.AddItem(utils.EmptyBoxSpace(bgColor), 0, 1, true)
d.cpuMemoryPage.AddItem(d.cpuQuataField, 0, 1, true)
d.cpuMemoryPage.AddItem(utils.EmptyBoxSpace(bgColor), 0, 1, true)
d.cpuMemoryPage.AddItem(d.cpuSharesField, 0, 1, true)
d.cpuMemoryPage.AddItem(utils.EmptyBoxSpace(bgColor), 0, 1, true)
d.cpuMemoryPage.AddItem(cpuSetRow, 0, 1, true)
d.cpuMemoryPage.AddItem(utils.EmptyBoxSpace(bgColor), 0, 1, true)
d.cpuMemoryPage.AddItem(memSwapRow, 0, 1, true)
// adding category pages
d.categoryPages.AddPage(d.categoryLabels[buildDialogBasicInfoPageIndex], d.basicInfoPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[buildDialogBuildInfoPageIndex], d.buildInfoPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[buildDialogCapabilityPageIndex], d.capabilityPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[buildDialogCPUMemoryPageIndex], d.cpuMemoryPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[buildDialogNetworkingPageIndex], d.networkingPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[buildDialogSecurityOptsPageIndex], d.securityOptsPage, true, true)
// add it to layout.
_, layoutWidth := utils.AlignStringListWidth(d.categoryLabels)
layout := tview.NewFlex().SetDirection(tview.FlexColumn)
layout.AddItem(d.categories, layoutWidth+6, 0, true) //nolint:mnd
layout.AddItem(d.categoryPages, 0, 1, true)
layout.SetBackgroundColor(bgColor)
d.layout.AddItem(layout, 0, 1, true)
}
// Display displays this primitive.
func (d *ImageBuildDialog) Display() {
d.focusElement = buildDialogContextDirectoryPathFieldFocus
@ -645,7 +755,7 @@ func (d *ImageBuildDialog) InputHandler() func(event *tcell.EventKey, setFocus f
log.Debug().Msgf("image build dialog: event %v received", event)
if event.Key() == utils.CloseDialogKey.Key {
if !d.pullPolicyField.HasFocus() && !d.networkField.HasFocus() && !d.formatField.HasFocus() {
if !(d.pullPolicyField.HasFocus() || d.networkField.HasFocus() || d.formatField.HasFocus()) {
d.cancelHandler()
return
@ -811,8 +921,8 @@ func (d *ImageBuildDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
@ -835,352 +945,6 @@ func (d *ImageBuildDialog) SetBuildFunc(handler func()) *ImageBuildDialog {
return d
}
// ImageBuildOptions returns image build options.
func (d *ImageBuildDialog) ImageBuildOptions() (images.ImageBuildOptions, error) { //nolint:gocognit,gocyclo,cyclop,maintidx,lll
var (
memoryLimit int64
memorySwap int64
cpuPeriod uint64
cpuQuota int64
cpuShares uint64
cpuSetCpus string
cpuSetMems string
containerFiles []string
dnsServers []string
dnsOptions []string
dnsSearchDomains []string
addHost []string
labelOpts []string
apparmorProfile string
seccompProfilePath string
)
// basic info page
// Containerfiles
for _, cntFile := range strings.Split(strings.TrimSpace(d.containerFilePath.GetText()), " ") {
if cntFile != "" {
cFile, err := utils.ResolveHomeDir(cntFile)
if err != nil {
return images.ImageBuildOptions{}, err
}
containerFiles = append(containerFiles, cFile)
}
}
opts := images.ImageBuildOptions{
ContainerFiles: containerFiles,
BuildOptions: entities.BuildOptions{},
}
dir, err := utils.ResolveHomeDir(strings.TrimSpace(d.contextDirectoryPath.GetText()))
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("cannot resolve home directory %w", err)
}
opts.BuildOptions.ContextDirectory = dir
opts.BuildOptions.AdditionalTags = append(opts.BuildOptions.AdditionalTags, strings.TrimSpace(d.tagField.GetText()))
opts.BuildOptions.Registry = strings.TrimSpace(d.registryField.GetText())
_, pullOption := d.pullPolicyField.GetCurrentOption()
switch pullOption {
case "missing":
opts.BuildOptions.PullPolicy = define.PullIfMissing
case "always":
opts.BuildOptions.PullPolicy = define.PullAlways
case "ifnewer":
opts.BuildOptions.PullPolicy = define.PullIfNewer
case "never":
opts.BuildOptions.PullPolicy = define.PullNever
}
_, format := d.formatField.GetCurrentOption()
switch format {
case "oci":
opts.BuildOptions.OutputFormat = define.OCIv1ImageManifest
case "docker":
opts.BuildOptions.OutputFormat = define.Dockerv2ImageManifest
}
// build settings
opts.BuildOptions.Squash = d.SquashField.IsChecked()
opts.BuildOptions.Layers = d.layersField.IsChecked()
opts.BuildOptions.NoCache = d.noCacheField.IsChecked()
opts.BuildOptions.RemoveIntermediateCtrs = d.removeCntField.IsChecked()
opts.BuildOptions.ForceRmIntermediateCtrs = d.forceRemoveCntField.IsChecked()
labels := strings.TrimSpace(d.labelsField.GetText())
if labels != "" {
opts.BuildOptions.Labels = strings.Split(labels, " ")
}
annotations := strings.TrimSpace(d.annotationsField.GetText())
if annotations != "" {
opts.BuildOptions.Annotations = strings.Split(annotations, " ")
}
// capability pages
addCap := strings.TrimSpace(d.addCapabilityField.GetText())
if addCap != "" {
opts.BuildOptions.AddCapabilities = strings.Split(addCap, " ")
}
removeCap := strings.TrimSpace(d.removeCapabilityField.GetText())
if removeCap != "" {
opts.BuildOptions.DropCapabilities = strings.Split(removeCap, " ")
}
// cpu and memory page
opts.BuildOptions.CommonBuildOpts = &define.CommonBuildOptions{}
cpuPeriodVal := d.cpuPeriodField.GetText()
if cpuPeriodVal != "" {
period, err := strconv.Atoi(cpuPeriodVal)
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("invalid CPU period value %q %w", cpuPeriodVal, err)
}
cpuPeriod = uint64(period) //nolint:gosec
}
cpuQuotaVal := d.cpuQuataField.GetText()
if cpuQuotaVal != "" {
quota, err := strconv.Atoi(cpuQuotaVal)
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("invalid CPU quota value %q %w", cpuQuotaVal, err)
}
cpuQuota = int64(quota)
}
cpuSharesVal := d.cpuSharesField.GetText()
if cpuSharesVal != "" {
shares, err := strconv.Atoi(cpuSharesVal)
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("invalid CPU quota value %q %w", cpuSharesVal, err)
}
cpuShares = uint64(shares) //nolint:gosec
}
cpuSetCpusVal := d.cpuSetCpusField.GetText()
if cpuSetCpusVal != "" {
cpuSetCpus = cpuSetCpusVal
}
cpuSetMemsVal := d.cpuSetMemsField.GetText()
if cpuSetMemsVal != "" {
cpuSetMems = cpuSetMemsVal
}
memoryVal := d.memoryField.GetText()
if memoryVal != "" {
memory, err := strconv.Atoi(memoryVal)
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("invalid memory value %q %w", memoryVal, err)
}
memoryLimit = int64(memory)
}
memorySwapVal := d.memorySwapField.GetText()
if memorySwapVal != "" {
swap, err := strconv.Atoi(memorySwapVal)
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("invalid memory swap value %q %w", memorySwapVal, err)
}
memorySwap = int64(swap)
}
// networking page
// network policy
_, configureNetwork := d.networkField.GetCurrentOption()
switch configureNetwork {
case "NetworkDefault":
opts.BuildOptions.ConfigureNetwork = define.NetworkDefault
case "NetworkDisabled":
opts.BuildOptions.ConfigureNetwork = define.NetworkDisabled
case "NetworkEnabled":
opts.BuildOptions.ConfigureNetwork = define.NetworkEnabled
}
// add hosts
hosts := strings.TrimSpace(d.addHostField.GetText())
if hosts != "" {
addHost = strings.Split(hosts, " ")
}
// dns page
dnsServersList := strings.TrimSpace(d.dnsServersField.GetText())
for _, dns := range strings.Split(dnsServersList, " ") {
if dns != "" {
dnsServers = append(dnsServers, dns)
}
}
for _, do := range strings.Split(d.dnsOptionsField.GetText(), " ") {
if do != "" {
dnsOptions = append(dnsOptions, do)
}
}
for _, ds := range strings.Split(d.dnsSearchField.GetText(), " ") {
if ds != "" {
dnsSearchDomains = append(dnsSearchDomains, ds)
}
}
// security options page
for _, selinuxLabel := range strings.Split(d.selinuxLabelField.GetText(), " ") {
if selinuxLabel != "" {
labelOpts = append(labelOpts, selinuxLabel)
}
}
apparmor := strings.TrimSpace(d.apparmorProfileField.GetText())
if apparmor != "" {
apparmorProfile = apparmor
}
seccomp := strings.TrimSpace(d.seccompProfilePathField.GetText())
if seccomp != "" {
seccompProfilePath = seccomp
}
commonOpts := &define.CommonBuildOptions{
AddHost: addHost,
HTTPProxy: d.httpProxyField.IsChecked(),
CPUPeriod: cpuPeriod,
CPUQuota: cpuQuota,
CPUSetCPUs: cpuSetCpus,
CPUSetMems: cpuSetMems,
CPUShares: cpuShares,
DNSServers: dnsServers,
DNSOptions: dnsOptions,
DNSSearch: dnsSearchDomains,
Memory: memoryLimit,
MemorySwap: memorySwap,
LabelOpts: labelOpts,
ApparmorProfile: apparmorProfile,
SeccompProfilePath: seccompProfilePath,
}
opts.BuildOptions.CommonBuildOpts = commonOpts
return opts, nil
}
func (d *ImageBuildDialog) setupLayout() {
bgColor := style.DialogBgColor
// basic info page
d.basicInfoPage.SetDirection(tview.FlexRow)
d.basicInfoPage.AddItem(d.contextDirectoryPath, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.containerFilePath, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.pullPolicyField, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.tagField, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.registryField, 1, 0, true)
d.basicInfoPage.SetBackgroundColor(bgColor)
// layers setup page
secondRowLayout := tview.NewFlex().SetDirection(tview.FlexColumn)
secondRowLayout.SetBackgroundColor(bgColor)
secondRowLayout.AddItem(d.formatField, 0, 2, true) //nolint:mnd
secondRowLayout.AddItem(d.SquashField, 0, 1, true)
secondRowLayout.AddItem(d.layersField, 0, 1, true)
secondRowLayout.AddItem(d.noCacheField, 0, 1, true)
cntRmRowLayout := tview.NewFlex().SetDirection(tview.FlexColumn)
cntRmRowLayout.SetBackgroundColor(bgColor)
cntRmRowLayout.AddItem(d.forceRemoveCntField, 0, 1, true)
cntRmRowLayout.AddItem(d.removeCntField, 0, 2, true) //nolint:mnd
// build setup page
d.buildInfoPage.SetDirection(tview.FlexRow)
d.buildInfoPage.AddItem(d.buildArgsField, 1, 0, true)
d.buildInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.buildInfoPage.AddItem(secondRowLayout, 1, 0, true)
d.buildInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.buildInfoPage.AddItem(d.labelsField, 1, 0, true)
d.buildInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.buildInfoPage.AddItem(d.annotationsField, 1, 0, true)
d.buildInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.buildInfoPage.AddItem(cntRmRowLayout, 1, 0, true)
d.buildInfoPage.SetBackgroundColor(bgColor)
// security options page
d.securityOptsPage.SetDirection(tview.FlexRow)
d.securityOptsPage.AddItem(d.selinuxLabelField, 1, 0, true)
d.securityOptsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.securityOptsPage.AddItem(d.apparmorProfileField, 1, 0, true)
d.securityOptsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.securityOptsPage.AddItem(d.seccompProfilePathField, 1, 0, true)
// networking setup page
netFirstRow := tview.NewFlex().SetDirection(tview.FlexColumn)
netFirstRow.SetBackgroundColor(bgColor)
netFirstRow.AddItem(d.networkField, 0, 1, true)
netFirstRow.AddItem(d.httpProxyField, 0, 1, true)
d.networkingPage.SetDirection(tview.FlexRow)
d.networkingPage.AddItem(netFirstRow, 1, 0, true)
d.networkingPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.networkingPage.AddItem(d.addHostField, 1, 0, true)
d.networkingPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.networkingPage.AddItem(d.dnsServersField, 1, 0, true)
d.networkingPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.networkingPage.AddItem(d.dnsOptionsField, 1, 0, true)
d.networkingPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.networkingPage.AddItem(d.dnsSearchField, 1, 0, true)
d.networkingPage.SetBackgroundColor(bgColor)
// capability page
d.capabilityPage.SetDirection(tview.FlexRow)
d.capabilityPage.AddItem(d.addCapabilityField, 1, 0, true)
d.capabilityPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.capabilityPage.AddItem(d.removeCapabilityField, 1, 0, true)
// cpu and memory page
cpuSetRow := tview.NewFlex().SetDirection(tview.FlexColumn)
cpuSetRow.AddItem(d.cpuSetCpusField, 0, 1, true)
cpuSetRow.AddItem(d.cpuSetMemsField, 0, 1, true)
// memory and swap
memSwapRow := tview.NewFlex().SetDirection(tview.FlexColumn)
memSwapRow.AddItem(d.memoryField, 0, 1, true)
memSwapRow.AddItem(d.memorySwapField, 0, 1, true)
d.cpuMemoryPage.SetDirection(tview.FlexRow)
d.cpuMemoryPage.AddItem(d.cpuPeriodField, 0, 1, true)
d.cpuMemoryPage.AddItem(utils.EmptyBoxSpace(bgColor), 0, 1, true)
d.cpuMemoryPage.AddItem(d.cpuQuataField, 0, 1, true)
d.cpuMemoryPage.AddItem(utils.EmptyBoxSpace(bgColor), 0, 1, true)
d.cpuMemoryPage.AddItem(d.cpuSharesField, 0, 1, true)
d.cpuMemoryPage.AddItem(utils.EmptyBoxSpace(bgColor), 0, 1, true)
d.cpuMemoryPage.AddItem(cpuSetRow, 0, 1, true)
d.cpuMemoryPage.AddItem(utils.EmptyBoxSpace(bgColor), 0, 1, true)
d.cpuMemoryPage.AddItem(memSwapRow, 0, 1, true)
// adding category pages
d.categoryPages.AddPage(d.categoryLabels[buildDialogBasicInfoPageIndex], d.basicInfoPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[buildDialogBuildInfoPageIndex], d.buildInfoPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[buildDialogCapabilityPageIndex], d.capabilityPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[buildDialogCPUMemoryPageIndex], d.cpuMemoryPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[buildDialogNetworkingPageIndex], d.networkingPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[buildDialogSecurityOptsPageIndex], d.securityOptsPage, true, true)
// add it to layout.
_, layoutWidth := utils.AlignStringListWidth(d.categoryLabels)
layout := tview.NewFlex().SetDirection(tview.FlexColumn)
layout.AddItem(d.categories, layoutWidth+6, 0, true) //nolint:mnd
layout.AddItem(d.categoryPages, 0, 1, true)
layout.SetBackgroundColor(bgColor)
d.layout.AddItem(layout, 0, 1, true)
}
func (d *ImageBuildDialog) initData() {
d.setActiveCategory(0)
@ -1459,3 +1223,238 @@ func (d *ImageBuildDialog) setSecurityOptionsPageNextFocus() {
d.focusElement = buildDialogFormFocus
}
// ImageBuildOptions returns image build options.
func (d *ImageBuildDialog) ImageBuildOptions() (images.ImageBuildOptions, error) { //nolint:gocognit,gocyclo,cyclop,maintidx,lll
var (
memoryLimit int64
memorySwap int64
cpuPeriod uint64
cpuQuota int64
cpuShares uint64
cpuSetCpus string
cpuSetMems string
containerFiles []string
dnsServers []string
dnsOptions []string
dnsSearchDomains []string
addHost []string
labelOpts []string
apparmorProfile string
seccompProfilePath string
)
// basic info page
// Containerfiles
for _, cntFile := range strings.Split(strings.TrimSpace(d.containerFilePath.GetText()), " ") {
if cntFile != "" {
cFile, err := utils.ResolveHomeDir(cntFile)
if err != nil {
return images.ImageBuildOptions{}, err
}
containerFiles = append(containerFiles, cFile)
}
}
opts := images.ImageBuildOptions{
ContainerFiles: containerFiles,
BuildOptions: entities.BuildOptions{},
}
dir, err := utils.ResolveHomeDir(strings.TrimSpace(d.contextDirectoryPath.GetText()))
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("cannot resolve home directory %w", err)
}
opts.BuildOptions.ContextDirectory = dir
opts.BuildOptions.AdditionalTags = append(opts.BuildOptions.AdditionalTags, strings.TrimSpace(d.tagField.GetText()))
opts.BuildOptions.Registry = strings.TrimSpace(d.registryField.GetText())
_, pullOption := d.pullPolicyField.GetCurrentOption()
switch pullOption {
case "missing":
opts.BuildOptions.PullPolicy = define.PullIfMissing
case "always":
opts.BuildOptions.PullPolicy = define.PullAlways
case "ifnewer":
opts.BuildOptions.PullPolicy = define.PullIfNewer
case "never":
opts.BuildOptions.PullPolicy = define.PullNever
}
_, format := d.formatField.GetCurrentOption()
switch format {
case "oci":
opts.BuildOptions.OutputFormat = define.OCIv1ImageManifest
case "docker":
opts.BuildOptions.OutputFormat = define.Dockerv2ImageManifest
}
// build settings
opts.BuildOptions.Squash = d.SquashField.IsChecked()
opts.BuildOptions.Layers = d.layersField.IsChecked()
opts.BuildOptions.NoCache = d.noCacheField.IsChecked()
opts.BuildOptions.RemoveIntermediateCtrs = d.removeCntField.IsChecked()
opts.BuildOptions.ForceRmIntermediateCtrs = d.forceRemoveCntField.IsChecked()
labels := strings.TrimSpace(d.labelsField.GetText())
if labels != "" {
opts.BuildOptions.Labels = strings.Split(labels, " ")
}
annotations := strings.TrimSpace(d.annotationsField.GetText())
if annotations != "" {
opts.BuildOptions.Annotations = strings.Split(annotations, " ")
}
// capability pages
addCap := strings.TrimSpace(d.addCapabilityField.GetText())
if addCap != "" {
opts.BuildOptions.AddCapabilities = strings.Split(addCap, " ")
}
removeCap := strings.TrimSpace(d.removeCapabilityField.GetText())
if removeCap != "" {
opts.BuildOptions.DropCapabilities = strings.Split(removeCap, " ")
}
// cpu and memory page
opts.BuildOptions.CommonBuildOpts = &define.CommonBuildOptions{}
cpuPeriodVal := d.cpuPeriodField.GetText()
if cpuPeriodVal != "" {
period, err := strconv.Atoi(cpuPeriodVal)
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("invalid CPU period value %q %w", cpuPeriodVal, err)
}
cpuPeriod = uint64(period) //nolint:gosec
}
cpuQuotaVal := d.cpuQuataField.GetText()
if cpuQuotaVal != "" {
quota, err := strconv.Atoi(cpuQuotaVal)
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("invalid CPU quota value %q %w", cpuQuotaVal, err)
}
cpuQuota = int64(quota)
}
cpuSharesVal := d.cpuSharesField.GetText()
if cpuSharesVal != "" {
shares, err := strconv.Atoi(cpuSharesVal)
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("invalid CPU quota value %q %w", cpuSharesVal, err)
}
cpuShares = uint64(shares) //nolint:gosec
}
cpuSetCpusVal := d.cpuSetCpusField.GetText()
if cpuSetCpusVal != "" {
cpuSetCpus = cpuSetCpusVal
}
cpuSetMemsVal := d.cpuSetMemsField.GetText()
if cpuSetMemsVal != "" {
cpuSetMems = cpuSetMemsVal
}
memoryVal := d.memoryField.GetText()
if memoryVal != "" {
memory, err := strconv.Atoi(memoryVal)
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("invalid memory value %q %w", memoryVal, err)
}
memoryLimit = int64(memory)
}
memorySwapVal := d.memorySwapField.GetText()
if memorySwapVal != "" {
swap, err := strconv.Atoi(memorySwapVal)
if err != nil {
return images.ImageBuildOptions{}, fmt.Errorf("invalid memory swap value %q %w", memorySwapVal, err)
}
memorySwap = int64(swap)
}
// networking page
// network policy
_, configureNetwork := d.networkField.GetCurrentOption()
switch configureNetwork {
case "NetworkDefault":
opts.BuildOptions.ConfigureNetwork = define.NetworkDefault
case "NetworkDisabled":
opts.BuildOptions.ConfigureNetwork = define.NetworkDisabled
case "NetworkEnabled":
opts.BuildOptions.ConfigureNetwork = define.NetworkEnabled
}
// add hosts
hosts := strings.TrimSpace(d.addHostField.GetText())
if hosts != "" {
addHost = strings.Split(hosts, " ")
}
// dns page
dnsServersList := strings.TrimSpace(d.dnsServersField.GetText())
for _, dns := range strings.Split(dnsServersList, " ") {
if dns != "" {
dnsServers = append(dnsServers, dns)
}
}
for _, do := range strings.Split(d.dnsOptionsField.GetText(), " ") {
if do != "" {
dnsOptions = append(dnsOptions, do)
}
}
for _, ds := range strings.Split(d.dnsSearchField.GetText(), " ") {
if ds != "" {
dnsSearchDomains = append(dnsSearchDomains, ds)
}
}
// security options page
for _, selinuxLabel := range strings.Split(d.selinuxLabelField.GetText(), " ") {
if selinuxLabel != "" {
labelOpts = append(labelOpts, selinuxLabel)
}
}
apparmor := strings.TrimSpace(d.apparmorProfileField.GetText())
if apparmor != "" {
apparmorProfile = apparmor
}
seccomp := strings.TrimSpace(d.seccompProfilePathField.GetText())
if seccomp != "" {
seccompProfilePath = seccomp
}
commonOpts := &define.CommonBuildOptions{
AddHost: addHost,
HTTPProxy: d.httpProxyField.IsChecked(),
CPUPeriod: cpuPeriod,
CPUQuota: cpuQuota,
CPUSetCPUs: cpuSetCpus,
CPUSetMems: cpuSetMems,
CPUShares: cpuShares,
DNSServers: dnsServers,
DNSOptions: dnsOptions,
DNSSearch: dnsSearchDomains,
Memory: memoryLimit,
MemorySwap: memorySwap,
LabelOpts: labelOpts,
ApparmorProfile: apparmorProfile,
SeccompProfilePath: seccompProfilePath,
}
opts.BuildOptions.CommonBuildOpts = commonOpts
return opts, nil
}

View File

@ -21,7 +21,6 @@ const (
// ImageBuildProgressDialog implements build progress dialog primitive.
type ImageBuildProgressDialog struct {
*tview.Box
layout *tview.Flex
output *tview.TextView
progressBar *tvxwidgets.ActivityModeGauge
@ -93,9 +92,7 @@ func (d *ImageBuildProgressDialog) IsDisplay() bool {
func (d *ImageBuildProgressDialog) Hide() {
d.display = false
d.cancelChan <- true
close(d.writerChan)
d.output.SetText("")
d.progressBar.Reset()
}
@ -116,7 +113,7 @@ func (d *ImageBuildProgressDialog) Focus(delegate func(p tview.Primitive)) {
// InputHandler returns input handler function for this primitive.
func (d *ImageBuildProgressDialog) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { //nolint:revive
log.Debug().Msgf("image build progress dialog: event %v received", event)
})
}
@ -144,23 +141,12 @@ func (d *ImageBuildProgressDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
// LogWriter returns output log writer.
func (d *ImageBuildProgressDialog) LogWriter() channel.WriteCloser { //nolint:ireturn
return channel.NewWriter(d.writerChan)
}
// SetFastRefreshHandler sets fast refresh handler
// fast refresh is used to print image build output as fast as possible.
func (d *ImageBuildProgressDialog) SetFastRefreshHandler(handler func()) {
d.fastRefreshHandler = handler
}
func (d *ImageBuildProgressDialog) outputReaderLoop() {
tick := time.NewTicker(utils.RefreshInterval)
@ -178,14 +164,20 @@ func (d *ImageBuildProgressDialog) outputReaderLoop() {
return
case data := <-d.writerChan:
d.mu.Lock()
_, err := d.output.Write(data)
if err != nil {
log.Error().Msgf("failed to write data to output: %s", err.Error())
}
d.output.Write(data) //nolint:errcheck
d.mu.Unlock()
d.fastRefreshHandler()
}
}
}
// LogWriter returns output log writer.
func (d *ImageBuildProgressDialog) LogWriter() channel.WriteCloser { //nolint:ireturn
return channel.NewWriter(d.writerChan)
}
// SetFastRefreshHandler sets fast refresh handler
// fast refresh is used to print image build output as fast as possible.
func (d *ImageBuildProgressDialog) SetFastRefreshHandler(handler func()) {
d.fastRefreshHandler = handler
}

View File

@ -27,7 +27,6 @@ const (
// ImageHistoryDialog represents image history dialog primitive.
type ImageHistoryDialog struct {
*tview.Box
layout *tview.Flex
imageInfo *tview.InputField
table *tview.Table
@ -171,8 +170,8 @@ func (d *ImageHistoryDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
@ -186,6 +185,25 @@ func (d *ImageHistoryDialog) SetCancelFunc(handler func()) *ImageHistoryDialog {
return d
}
func (d *ImageHistoryDialog) initTable() {
bgColor := style.TableHeaderBgColor
fgColor := style.TableHeaderFgColor
d.table.Clear()
d.table.SetFixed(1, 1)
d.table.SetSelectable(true, false)
for i := range d.tableHeaders {
d.table.SetCell(0, i,
tview.NewTableCell(fmt.Sprintf("[%s::b]%s", style.GetColorHex(fgColor), strings.ToUpper(d.tableHeaders[i]))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
}
}
// UpdateResults updates result table.
func (d *ImageHistoryDialog) UpdateResults(data [][]string) {
d.results = data
@ -293,22 +311,3 @@ func (d *ImageHistoryDialog) getCreatedByWidth() int {
return createdByWidth
}
func (d *ImageHistoryDialog) initTable() {
bgColor := style.TableHeaderBgColor
fgColor := style.TableHeaderFgColor
d.table.Clear()
d.table.SetFixed(1, 1)
d.table.SetSelectable(true, false)
for i := range d.tableHeaders {
d.table.SetCell(0, i,
tview.NewTableCell(fmt.Sprintf("[%s::b]%s", style.GetColorHex(fgColor), strings.ToUpper(d.tableHeaders[i]))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
}
}

View File

@ -32,7 +32,6 @@ const (
// ImageImportDialog represents image import dialog primitive.
type ImageImportDialog struct {
*tview.Box
layout *tview.Flex
path *tview.InputField
change *tview.InputField
@ -272,12 +271,25 @@ func (d *ImageImportDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
func (d *ImageImportDialog) setFocusElement() {
switch d.focusElement {
case imageImportPathFocus:
d.focusElement = imageImportChangeFocus
case imageImportChangeFocus:
d.focusElement = imageImportCommitMessageFocus
case imageImportCommitMessageFocus:
d.focusElement = imageImportReferenceFocus
case imageImportReferenceFocus:
d.focusElement = imageImportFormFocus
}
}
// SetImportFunc sets form import button selected function.
func (d *ImageImportDialog) SetImportFunc(handler func()) *ImageImportDialog {
d.importHandler = handler
@ -326,8 +338,8 @@ func (d *ImageImportDialog) ImageImportOptions() (images.ImageImportOptions, err
}
errFileName := utils.ValidateFileName(path)
errURL := utils.ValidURL(path)
if errURL == nil {
opts.URL = true
}
@ -340,16 +352,3 @@ func (d *ImageImportDialog) ImageImportOptions() (images.ImageImportOptions, err
return opts, nil
}
func (d *ImageImportDialog) setFocusElement() {
switch d.focusElement {
case imageImportPathFocus:
d.focusElement = imageImportChangeFocus
case imageImportChangeFocus:
d.focusElement = imageImportCommitMessageFocus
case imageImportCommitMessageFocus:
d.focusElement = imageImportReferenceFocus
case imageImportReferenceFocus:
d.focusElement = imageImportFormFocus
}
}

View File

@ -32,7 +32,6 @@ const (
// ImagePushDialog represents image push dialog primitive.
type ImagePushDialog struct {
*tview.Box
layout *tview.Flex
imageInfo *tview.InputField
destination *tview.InputField
@ -246,6 +245,12 @@ func (d *ImagePushDialog) HasFocus() bool { //nolint:cyclop
return d.Box.HasFocus()
}
// dropdownHasFocus returns true if image push dialog dropdown primitives.
// has focus.
func (d *ImagePushDialog) dropdownHasFocus() bool {
return d.format.HasFocus()
}
// Focus is called when this primitive receives focus.
func (d *ImagePushDialog) Focus(delegate func(p tview.Primitive)) {
switch d.focusElement {
@ -363,6 +368,25 @@ func (d *ImagePushDialog) InputHandler() func(event *tcell.EventKey, setFocus fu
})
}
func (d *ImagePushDialog) setFocusElement() {
switch d.focusElement {
case imagePushDesitnationFocus:
d.focusElement = imagePushCompressFocus
case imagePushCompressFocus:
d.focusElement = imagePushFormatFocus
case imagePushFormatFocus:
d.focusElement = imagePushSkipTLSVerifyFocus
case imagePushSkipTLSVerifyFocus:
d.focusElement = imagePushUsernameFocus
case imagePushUsernameFocus:
d.focusElement = imagePushPasswordFocus
case imagePushPasswordFocus:
d.focusElement = imagePushAuthFileFocus
case imagePushAuthFileFocus:
d.focusElement = imagePushFormFocus
}
}
// SetRect set rects for this primitive.
func (d *ImagePushDialog) SetRect(x, y, width, height int) {
if width > imagePushDialogMaxWidth {
@ -386,8 +410,8 @@ func (d *ImagePushDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
@ -433,28 +457,3 @@ func (d *ImagePushDialog) GetImagePushOptions() images.ImagePushOptions {
return opts
}
// dropdownHasFocus returns true if image push dialog dropdown primitives.
// has focus.
func (d *ImagePushDialog) dropdownHasFocus() bool {
return d.format.HasFocus()
}
func (d *ImagePushDialog) setFocusElement() {
switch d.focusElement {
case imagePushDesitnationFocus:
d.focusElement = imagePushCompressFocus
case imagePushCompressFocus:
d.focusElement = imagePushFormatFocus
case imagePushFormatFocus:
d.focusElement = imagePushSkipTLSVerifyFocus
case imagePushSkipTLSVerifyFocus:
d.focusElement = imagePushUsernameFocus
case imagePushUsernameFocus:
d.focusElement = imagePushPasswordFocus
case imagePushPasswordFocus:
d.focusElement = imagePushAuthFileFocus
case imagePushAuthFileFocus:
d.focusElement = imagePushFormFocus
}
}

View File

@ -33,7 +33,6 @@ const (
// ImageSaveDialog represents image save dialog primitive.
type ImageSaveDialog struct {
*tview.Box
layout *tview.Flex
imageInfo *tview.InputField
output *tview.InputField
@ -308,12 +307,25 @@ func (d *ImageSaveDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
}
func (d *ImageSaveDialog) setFocusElement() {
switch d.focusElement {
case imageSaveOutputFocus:
d.focusElement = imageSaveCompressFocus
case imageSaveCompressFocus:
d.focusElement = imageSaveAcceptUncompressedFocus
case imageSaveAcceptUncompressedFocus:
d.focusElement = imageSaveFormatFocus
case imageSaveFormatFocus:
d.focusElement = imageSaveFormFocus
}
}
// SetSaveFunc sets form save button selected function.
func (d *ImageSaveDialog) SetSaveFunc(handler func()) *ImageSaveDialog {
d.saveHandler = handler
@ -372,16 +384,3 @@ func (d *ImageSaveDialog) ImageSaveOptions() (images.ImageSaveOptions, error) {
return opts, nil
}
func (d *ImageSaveDialog) setFocusElement() {
switch d.focusElement {
case imageSaveOutputFocus:
d.focusElement = imageSaveCompressFocus
case imageSaveCompressFocus:
d.focusElement = imageSaveAcceptUncompressedFocus
case imageSaveAcceptUncompressedFocus:
d.focusElement = imageSaveFormatFocus
case imageSaveFormatFocus:
d.focusElement = imageSaveFormFocus
}
}

View File

@ -45,7 +45,6 @@ const (
// ImageSearchDialog represents image search dialogs.
type ImageSearchDialog struct {
*tview.Box
layout *tview.Flex
searchLayout *tview.Flex
input *tview.InputField
@ -127,6 +126,62 @@ func NewImageSearchDialog() *ImageSearchDialog {
return dialog
}
func (d *ImageSearchDialog) initTable() {
bgColor := style.TableHeaderBgColor
fgColor := style.TableHeaderFgColor
d.searchResult.Clear()
d.searchResult.SetCell(0, searchResultIndexColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]INDEX", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
d.searchResult.SetCell(0, searchResultNameColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]NAME", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
d.searchResult.SetCell(0, searchResultStarsColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]STARS", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignCenter).
SetSelectable(false))
d.searchResult.SetCell(0, searchResultOfficialColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]OFFICIAL", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignCenter).
SetSelectable(false))
d.searchResult.SetCell(0, searchResultAutomatedColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]AUTOMATED", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignCenter).
SetSelectable(false))
d.searchResult.SetCell(0, searchResultDescColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]DESCRIPTION", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignCenter).
SetSelectable(false))
d.searchResult.SetFixed(1, 1)
d.searchResult.SetSelectable(true, false)
}
// Display displays this primitive.
func (d *ImageSearchDialog) Display() {
d.display = true
@ -278,9 +333,9 @@ func (d *ImageSearchDialog) Draw(screen tcell.Screen) {
}
bgColor := style.DialogBgColor
d.SetBackgroundColor(bgColor)
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.SetBackgroundColor(bgColor)
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.SetBorder(true)
d.layout.SetBackgroundColor(bgColor)
@ -450,59 +505,3 @@ func (d *ImageSearchDialog) UpdateResults(data [][]string) {
d.searchResult.ScrollToBeginning()
}
}
func (d *ImageSearchDialog) initTable() {
bgColor := style.TableHeaderBgColor
fgColor := style.TableHeaderFgColor
d.searchResult.Clear()
d.searchResult.SetCell(0, searchResultIndexColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]INDEX", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
d.searchResult.SetCell(0, searchResultNameColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]NAME", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignLeft).
SetSelectable(false))
d.searchResult.SetCell(0, searchResultStarsColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]STARS", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignCenter).
SetSelectable(false))
d.searchResult.SetCell(0, searchResultOfficialColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]OFFICIAL", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignCenter).
SetSelectable(false))
d.searchResult.SetCell(0, searchResultAutomatedColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]AUTOMATED", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignCenter).
SetSelectable(false))
d.searchResult.SetCell(0, searchResultDescColIndex,
tview.NewTableCell(fmt.Sprintf("[%s::b]DESCRIPTION", style.GetColorHex(fgColor))).
SetExpansion(1).
SetBackgroundColor(bgColor).
SetTextColor(fgColor).
SetAlign(tview.AlignCenter).
SetSelectable(false))
d.searchResult.SetFixed(1, 1)
d.searchResult.SetSelectable(true, false)
}

View File

@ -8,7 +8,7 @@ import (
)
// InputHandler returns the handler for this primitive.
func (img *Images) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { //nolint:gocognit,cyclop,lll
func (img *Images) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { //nolint:gocognit,gocyclo,lll,cyclop
return img.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
log.Debug().Msgf("view: images event %v received", event)
@ -16,19 +16,88 @@ func (img *Images) InputHandler() func(event *tcell.EventKey, setFocus func(p tv
return
}
for _, dialog := range img.getInnerTopDialogs() {
if dialog.HasFocus() {
if handler := dialog.InputHandler(); handler != nil {
handler(event, setFocus)
}
// error dialog handler
if img.errorDialog.HasFocus() || img.errorDialog.IsDisplay() {
if errorDialogHandler := img.errorDialog.InputHandler(); errorDialogHandler != nil {
errorDialogHandler(event, setFocus)
setFocus(img.errorDialog)
}
}
for _, dialog := range img.getInnerDialogs() {
if dialog.HasFocus() {
if handler := dialog.InputHandler(); handler != nil {
handler(event, setFocus)
}
// message dialog handler
if img.messageDialog.HasFocus() || img.messageDialog.IsDisplay() {
if messageDialogHandler := img.messageDialog.InputHandler(); messageDialogHandler != nil {
messageDialogHandler(event, setFocus)
setFocus(img.messageDialog)
}
}
// command dialog handler
if img.cmdDialog.HasFocus() {
if cmdHandler := img.cmdDialog.InputHandler(); cmdHandler != nil {
cmdHandler(event, setFocus)
}
}
// input dialog handler
if img.cmdInputDialog.HasFocus() {
if cmdInputHandler := img.cmdInputDialog.InputHandler(); cmdInputHandler != nil {
cmdInputHandler(event, setFocus)
}
}
// confirm dialog handler
if img.confirmDialog.HasFocus() {
if confirmDialogHandler := img.confirmDialog.InputHandler(); confirmDialogHandler != nil {
confirmDialogHandler(event, setFocus)
}
}
// search dialog handler
if img.searchDialog.HasFocus() {
if searchDialogHandler := img.searchDialog.InputHandler(); searchDialogHandler != nil {
searchDialogHandler(event, setFocus)
}
}
// history dialog handler
if img.historyDialog.HasFocus() {
if historyDialogHandler := img.historyDialog.InputHandler(); historyDialogHandler != nil {
historyDialogHandler(event, setFocus)
}
}
// build dialog handler
if img.buildDialog.HasFocus() {
if buildDialogHandler := img.buildDialog.InputHandler(); buildDialogHandler != nil {
buildDialogHandler(event, setFocus)
}
}
// build progress dialog handler
if img.buildPrgDialog.HasFocus() {
if buildPrgDialogHandler := img.buildPrgDialog.InputHandler(); buildPrgDialogHandler != nil {
buildPrgDialogHandler(event, setFocus)
}
}
// save dialog handler
if img.saveDialog.HasFocus() {
if saveDialogHandler := img.saveDialog.InputHandler(); saveDialogHandler != nil {
saveDialogHandler(event, setFocus)
}
}
// import dialog handler
if img.importDialog.HasFocus() {
if importDialogHandler := img.importDialog.InputHandler(); importDialogHandler != nil {
importDialogHandler(event, setFocus)
}
}
// push dialog handler
if img.pushDialog.HasFocus() {
if pushDialogHandler := img.pushDialog.InputHandler(); pushDialogHandler != nil {
pushDialogHandler(event, setFocus)
}
}
@ -46,14 +115,6 @@ func (img *Images) InputHandler() func(event *tcell.EventKey, setFocus func(p tv
return
}
// display sort menu
if event.Rune() == utils.SortMenuKey.Rune() {
img.sortDialog.Display()
setFocus(img)
return
}
if event.Key() == utils.DeleteKey.EventKey() {
img.rm()
setFocus(img)

View File

@ -26,7 +26,6 @@ func (img *Images) refresh(_ int) {
SetSelectable(false))
}
currentSelectedRow, _ := img.table.GetSelection()
rowIndex := 1
images := img.getData()
@ -77,11 +76,4 @@ func (img *Images) refresh(_ int) {
rowIndex++
}
if currentSelectedRow > len(images) {
currentSelectedRow--
if currentSelectedRow >= 0 {
img.table.Select(currentSelectedRow, -1)
}
}
}

View File

@ -29,7 +29,6 @@ const (
// InfoBar implements the info bar primitive.
type InfoBar struct {
*tview.Box
table *tview.Table
title string
connStatus registry.ConnStatus
@ -164,9 +163,8 @@ func (info *InfoBar) UpdateSystemUsageInfo(memUsage float64, swapUsage float64)
// UpdateConnStatus updates connection status value.
func (info *InfoBar) UpdateConnStatus(status registry.ConnStatus) {
var connStatus string
info.connStatus = status
connStatus := ""
switch info.connStatus {
case registry.ConnectionStatusConnected:
@ -182,8 +180,8 @@ func (info *InfoBar) UpdateConnStatus(status registry.ConnStatus) {
// Draw draws this primitive onto the screen.
func (info *InfoBar) Draw(screen tcell.Screen) {
info.DrawForSubclass(screen, info)
info.SetBorder(false)
info.Box.DrawForSubclass(screen, info)
info.Box.SetBorder(false)
x, y, width, height := info.GetInnerRect()

View File

@ -8,7 +8,6 @@ import (
"github.com/containers/podman-tui/pdcs/networks"
"github.com/containers/podman-tui/ui/dialogs"
"github.com/containers/podman-tui/ui/style"
"github.com/containers/podman-tui/ui/utils"
"github.com/rs/zerolog/log"
)
@ -22,7 +21,7 @@ func (nets *Networks) runCommand(cmd string) {
nets.cdisconnect()
case "inspect":
nets.inspect()
case utils.PruneCommandLabel:
case "prune": //nolint:goconst
nets.cprune()
case "rm":
nets.rm()
@ -76,15 +75,15 @@ func (nets *Networks) connect() {
nets.progressDialog.SetTitle("podman network connect")
nets.progressDialog.Display()
err := networks.Connect(connectOptions)
nets.progressDialog.Hide()
if err != nil {
if err := networks.Connect(connectOptions); err != nil {
nets.progressDialog.Hide()
nets.displayError("NETWORK CONNECT ERROR", err)
nets.appFocusHandler()
return
}
nets.appFocusHandler()
nets.progressDialog.Hide()
}
go connect()
@ -102,10 +101,8 @@ func (nets *Networks) cdisconnect() {
nets.progressDialog.Display()
cntListReport, err := containers.List()
nets.progressDialog.Hide()
if err != nil {
nets.progressDialog.Hide()
nets.displayError("NETWORK DISCONNECT ERROR", err)
nets.appFocusHandler()
@ -116,6 +113,7 @@ func (nets *Networks) cdisconnect() {
nets.disconnectDialog.SetNetworkInfo(netID, netName)
nets.disconnectDialog.SetContainers(cntListReport)
nets.progressDialog.Hide()
nets.disconnectDialog.Display()
nets.appFocusHandler()
}
@ -131,15 +129,15 @@ func (nets *Networks) disconnect() {
nets.progressDialog.SetTitle("podman network disconnect")
nets.progressDialog.Display()
err := networks.Disconnect(networkName, containerID)
nets.progressDialog.Hide()
if err != nil {
if err := networks.Disconnect(networkName, containerID); err != nil {
nets.progressDialog.Hide()
nets.displayError("NETWORK DISCONNECT ERROR", err)
nets.appFocusHandler()
return
}
nets.appFocusHandler()
nets.progressDialog.Hide()
}
go disconnect()
@ -184,7 +182,7 @@ func (nets *Networks) inspect() {
func (nets *Networks) cprune() {
nets.confirmDialog.SetTitle("podman network prune")
nets.confirmData = utils.PruneCommandLabel
nets.confirmData = "prune"
nets.confirmDialog.SetText("Are you sure you want to remove all un used network ?")
nets.confirmDialog.Display()
@ -195,16 +193,16 @@ func (nets *Networks) prune() {
nets.progressDialog.Display()
prune := func() {
err := networks.Prune()
nets.progressDialog.Hide()
if err != nil {
if err := networks.Prune(); err != nil {
nets.progressDialog.Hide()
nets.displayError("NETWORK PRUNE ERROR", err)
nets.appFocusHandler()
return
}
nets.appFocusHandler()
nets.UpdateData()
nets.progressDialog.Hide()
}
go prune()
@ -244,9 +242,11 @@ func (nets *Networks) remove() {
title := fmt.Sprintf("NETWORK (%s) REMOVE ERROR", nets.selectedID)
nets.displayError(title, err)
nets.appFocusHandler()
return
}
nets.appFocusHandler()
nets.UpdateData()
}

View File

@ -2,29 +2,14 @@ package networks
import (
"fmt"
"sort"
"strings"
"github.com/containers/common/libnetwork/types"
"github.com/containers/podman-tui/pdcs/networks"
"github.com/containers/podman-tui/ui/style"
"github.com/rivo/tview"
"github.com/rs/zerolog/log"
)
// SortView sorts data view called from sort dialog.
func (nets *Networks) SortView(option string, ascending bool) {
log.Debug().Msgf("view: networks sort by %s", option)
nets.networkList.mu.Lock()
defer nets.networkList.mu.Unlock()
nets.networkList.sortBy = option
nets.networkList.ascending = ascending
sort.Sort(netsListSorted{nets.networkList.report, option, ascending})
}
// UpdateData retrieves networks list data.
func (nets *Networks) UpdateData() {
netList, err := networks.List()
@ -37,12 +22,10 @@ func (nets *Networks) UpdateData() {
nets.networkList.mu.Lock()
defer nets.networkList.mu.Unlock()
sort.Sort(netsListSorted{netList, nets.networkList.sortBy, nets.networkList.ascending})
nets.networkList.report = netList
}
func (nets *Networks) getData() []types.Network {
func (nets *Networks) getData() [][]string {
nets.networkList.mu.Lock()
defer nets.networkList.mu.Unlock()
@ -76,31 +59,3 @@ func (nets *Networks) ClearData() {
nets.table.SetTitle(fmt.Sprintf("[::b]%s[0]", strings.ToUpper(nets.title)))
}
type lprSort []types.Network
func (a lprSort) Len() int { return len(a) }
func (a lprSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type netsListSorted struct {
lprSort
option string
ascending bool
}
func (a netsListSorted) Less(i, j int) bool {
if a.option == "driver" {
if a.ascending {
return a.lprSort[i].Driver < a.lprSort[j].Driver
}
return a.lprSort[i].Driver > a.lprSort[j].Driver
}
if a.ascending {
return a.lprSort[i].Name < a.lprSort[j].Name
}
return a.lprSort[i].Name > a.lprSort[j].Name
}

View File

@ -6,8 +6,8 @@ import (
// Draw draws this primitive onto the screen.
func (nets *Networks) Draw(screen tcell.Screen) {
nets.DrawForSubclass(screen, nets)
nets.SetBorder(false)
nets.Box.DrawForSubclass(screen, nets)
nets.Box.SetBorder(false)
netViewX, netViewY, netViewW, netViewH := nets.GetInnerRect()
@ -19,12 +19,70 @@ func (nets *Networks) Draw(screen tcell.Screen) {
x, y, width, height := nets.table.GetInnerRect()
for _, dialog := range nets.getInnerDialogs() {
if dialog.IsDisplay() {
dialog.SetRect(x, y, width, height)
dialog.Draw(screen)
// error dialog
if nets.errorDialog.IsDisplay() {
nets.errorDialog.SetRect(x, y, width, height)
nets.errorDialog.Draw(screen)
return
return
}
// command dialog
if nets.cmdDialog.IsDisplay() {
nets.cmdDialog.SetRect(x, y, width, height)
nets.cmdDialog.Draw(screen)
return
}
// create dialog
if nets.createDialog.IsDisplay() {
nets.createDialog.SetRect(x, y, width, height)
nets.createDialog.Draw(screen)
return
}
// connect dialog
if nets.connectDialog.IsDisplay() {
nets.connectDialog.SetRect(x, y, width, height)
nets.connectDialog.Draw(screen)
return
}
// disconnect dialog
if nets.disconnectDialog.IsDisplay() {
nets.disconnectDialog.SetRect(x, y, width, height)
nets.disconnectDialog.Draw(screen)
return
}
// message dialog
if nets.messageDialog.IsDisplay() {
if nets.messageDialog.IsDisplayFullSize() {
nets.messageDialog.SetRect(netViewX, netViewY, netViewW, netViewH)
} else {
nets.messageDialog.SetRect(x, y, width, height+1)
}
nets.messageDialog.Draw(screen)
return
}
// confirm dialog
if nets.confirmDialog.IsDisplay() {
nets.confirmDialog.SetRect(x, y, width, height)
nets.confirmDialog.Draw(screen)
return
}
// progress dialog
if nets.progressDialog.IsDisplay() {
nets.progressDialog.SetRect(x, y, width, height)
nets.progressDialog.Draw(screen)
}
}

View File

@ -8,7 +8,7 @@ import (
)
// InputHandler returns the handler for this primitive.
func (nets *Networks) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { //nolint:cyclop
func (nets *Networks) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) { //nolint:gocognit,lll,cyclop
return nets.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
log.Debug().Msgf("view: networks event %v received", event)
@ -16,11 +16,52 @@ func (nets *Networks) InputHandler() func(event *tcell.EventKey, setFocus func(p
return
}
for _, dialog := range nets.getInnerDialogs() {
if dialog.HasFocus() {
if dialogHandler := dialog.InputHandler(); dialogHandler != nil {
dialogHandler(event, setFocus)
}
// error dialog handler
if nets.errorDialog.HasFocus() {
if errorDialogHandler := nets.errorDialog.InputHandler(); errorDialogHandler != nil {
errorDialogHandler(event, setFocus)
}
}
// message dialog handler
if nets.messageDialog.HasFocus() {
if messageDialogHandler := nets.messageDialog.InputHandler(); messageDialogHandler != nil {
messageDialogHandler(event, setFocus)
}
}
// create dialog handler
if nets.createDialog.HasFocus() {
if createDialogHandler := nets.createDialog.InputHandler(); createDialogHandler != nil {
createDialogHandler(event, setFocus)
}
}
// connect dialog handler
if nets.connectDialog.HasFocus() {
if connectDialogHandler := nets.connectDialog.InputHandler(); connectDialogHandler != nil {
connectDialogHandler(event, setFocus)
}
}
// disconnect dialog handler
if nets.disconnectDialog.HasFocus() {
if disconnectDialogHandler := nets.disconnectDialog.InputHandler(); disconnectDialogHandler != nil {
disconnectDialogHandler(event, setFocus)
}
}
// confirm dialog handler
if nets.confirmDialog.HasFocus() {
if confirmDialogHandler := nets.confirmDialog.InputHandler(); confirmDialogHandler != nil {
confirmDialogHandler(event, setFocus)
}
}
// command dialog handler
if nets.cmdDialog.HasFocus() {
if cmdHandler := nets.cmdDialog.InputHandler(); cmdHandler != nil {
cmdHandler(event, setFocus)
}
}
@ -38,14 +79,6 @@ func (nets *Networks) InputHandler() func(event *tcell.EventKey, setFocus func(p
return
}
// display sort menu
if event.Rune() == utils.SortMenuKey.Rune() {
nets.sortDialog.Display()
setFocus(nets)
return
}
if event.Key() == utils.DeleteKey.EventKey() {
nets.rm()
setFocus(nets)

View File

@ -32,7 +32,6 @@ const (
// NetworkConnectDialog implements network connect dialog primitive.
type NetworkConnectDialog struct {
*tview.Box
layout *tview.Flex
network *tview.InputField
container *tview.DropDown
@ -323,9 +322,9 @@ func (d *NetworkConnectDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
@ -351,6 +350,21 @@ func (d *NetworkConnectDialog) SetCancelFunc(handler func()) *NetworkConnectDial
return d
}
func (d *NetworkConnectDialog) setFocusElement() {
switch d.focusElement {
case netConnectContainerFocus:
d.focusElement = netConnectAliasesFocus
case netConnectAliasesFocus:
d.focusElement = netConnectAliasesIPv4Focus
case netConnectAliasesIPv4Focus:
d.focusElement = netConnectAliasesIPv6Focus
case netConnectAliasesIPv6Focus:
d.focusElement = netConnectMacAddrFocus
case netConnectMacAddrFocus:
d.focusElement = netConnectFormFocus
}
}
// SetNetworkInfo sets selected network name in connect dialog.
func (d *NetworkConnectDialog) SetNetworkInfo(id, name string) {
d.networkName = name
@ -386,18 +400,3 @@ func (d *NetworkConnectDialog) GetConnectOptions() networks.NetworkConnect {
return connectOptions
}
func (d *NetworkConnectDialog) setFocusElement() {
switch d.focusElement {
case netConnectContainerFocus:
d.focusElement = netConnectAliasesFocus
case netConnectAliasesFocus:
d.focusElement = netConnectAliasesIPv4Focus
case netConnectAliasesIPv4Focus:
d.focusElement = netConnectAliasesIPv6Focus
case netConnectAliasesIPv6Focus:
d.focusElement = netConnectMacAddrFocus
case netConnectMacAddrFocus:
d.focusElement = netConnectFormFocus
}
}

View File

@ -42,7 +42,6 @@ const (
// NetworkCreateDialog implements network create dialog.
type NetworkCreateDialog struct {
*tview.Box
layout *tview.Flex
categoryLabels []string
categories *tview.TextView
@ -205,6 +204,49 @@ func NewNetworkCreateDialog() *NetworkCreateDialog {
return &netDialog
}
func (d *NetworkCreateDialog) setupLayout() {
bgColor := style.DialogBgColor
// basic info page
d.basicInfoPage.SetDirection(tview.FlexRow)
d.basicInfoPage.AddItem(d.networkNameField, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.networkLabelsField, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.networkInternalCheckBox, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.networkDriverField, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.networkDriverOptionsField, 1, 0, true)
d.basicInfoPage.SetBackgroundColor(bgColor)
// ip settings page
d.ipSettingsPage.SetDirection(tview.FlexRow)
d.ipSettingsPage.AddItem(d.networkIpv6CheckBox, 1, 0, true)
d.ipSettingsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.ipSettingsPage.AddItem(d.networkGatewayField, 1, 0, true)
d.ipSettingsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.ipSettingsPage.AddItem(d.networkIPRangeField, 1, 0, true)
d.ipSettingsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.ipSettingsPage.AddItem(d.networkSubnetField, 1, 0, true)
d.ipSettingsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.ipSettingsPage.AddItem(d.networkDisableDNSCheckBox, 1, 0, true)
d.ipSettingsPage.SetBackgroundColor(bgColor)
// adding category pages
d.categoryPages.AddPage(d.categoryLabels[basicInfoPageIndex], d.basicInfoPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[ipSettingsPageIndex], d.ipSettingsPage, true, true)
// add it to layout.
_, layoutWidth := utils.AlignStringListWidth(d.categoryLabels)
layout := tview.NewFlex().SetDirection(tview.FlexColumn)
layout.AddItem(d.categories, layoutWidth+6, 0, true) //nolint:mnd
layout.AddItem(d.categoryPages, 0, 1, true)
layout.SetBackgroundColor(bgColor)
d.layout.AddItem(layout, 0, 1, true)
}
// Display displays this primitive.
func (d *NetworkCreateDialog) Display() {
d.display = true
@ -392,8 +434,8 @@ func (d *NetworkCreateDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
@ -419,115 +461,6 @@ func (d *NetworkCreateDialog) SetCreateFunc(handler func()) *NetworkCreateDialog
return d
}
// NetworkCreateOptions returns new network options.
func (d *NetworkCreateDialog) NetworkCreateOptions() networks.CreateOptions { //nolint:cyclop
var (
labels = make(map[string]string)
options = make(map[string]string)
subnets []string
gateways []string
ipranges []string
)
for _, label := range strings.Split(d.networkLabelsField.GetText(), " ") {
if label != "" {
split := strings.Split(label, "=")
if len(split) == 2 { //nolint:mnd
key := split[0]
value := split[1]
if key != "" && value != "" {
labels[key] = value
}
}
}
}
for _, option := range strings.Split(d.networkDriverOptionsField.GetText(), " ") {
if option != "" {
split := strings.Split(option, "=")
if len(split) == 2 { //nolint:mnd
key := split[0]
value := split[1]
if key != "" && value != "" {
options[key] = value
}
}
}
}
if strings.Trim(d.networkGatewayField.GetText(), " ") != "" {
gateways = strings.Split(d.networkGatewayField.GetText(), " ")
}
if strings.Trim(d.networkSubnetField.GetText(), " ") != "" {
subnets = strings.Split(d.networkSubnetField.GetText(), " ")
}
if strings.Trim(d.networkIPRangeField.GetText(), " ") != "" {
ipranges = strings.Split(d.networkIPRangeField.GetText(), " ")
}
opts := networks.CreateOptions{
Name: strings.TrimSpace(d.networkNameField.GetText()),
Labels: labels,
Internal: d.networkInternalCheckBox.IsChecked(),
Drivers: strings.TrimSpace(d.networkDriverField.GetText()),
DriversOptions: options,
IPv6: d.networkIpv6CheckBox.IsChecked(),
Gateways: gateways,
Subnets: subnets,
IPRanges: ipranges,
DisableDNS: d.networkDisableDNSCheckBox.IsChecked(),
}
return opts
}
func (d *NetworkCreateDialog) setupLayout() {
bgColor := style.DialogBgColor
// basic info page
d.basicInfoPage.SetDirection(tview.FlexRow)
d.basicInfoPage.AddItem(d.networkNameField, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.networkLabelsField, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.networkInternalCheckBox, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.networkDriverField, 1, 0, true)
d.basicInfoPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.basicInfoPage.AddItem(d.networkDriverOptionsField, 1, 0, true)
d.basicInfoPage.SetBackgroundColor(bgColor)
// ip settings page
d.ipSettingsPage.SetDirection(tview.FlexRow)
d.ipSettingsPage.AddItem(d.networkIpv6CheckBox, 1, 0, true)
d.ipSettingsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.ipSettingsPage.AddItem(d.networkGatewayField, 1, 0, true)
d.ipSettingsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.ipSettingsPage.AddItem(d.networkIPRangeField, 1, 0, true)
d.ipSettingsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.ipSettingsPage.AddItem(d.networkSubnetField, 1, 0, true)
d.ipSettingsPage.AddItem(utils.EmptyBoxSpace(bgColor), 1, 0, true)
d.ipSettingsPage.AddItem(d.networkDisableDNSCheckBox, 1, 0, true)
d.ipSettingsPage.SetBackgroundColor(bgColor)
// adding category pages
d.categoryPages.AddPage(d.categoryLabels[basicInfoPageIndex], d.basicInfoPage, true, true)
d.categoryPages.AddPage(d.categoryLabels[ipSettingsPageIndex], d.ipSettingsPage, true, true)
// add it to layout.
_, layoutWidth := utils.AlignStringListWidth(d.categoryLabels)
layout := tview.NewFlex().SetDirection(tview.FlexColumn)
layout.AddItem(d.categories, layoutWidth+6, 0, true) //nolint:mnd
layout.AddItem(d.categoryPages, 0, 1, true)
layout.SetBackgroundColor(bgColor)
d.layout.AddItem(layout, 0, 1, true)
}
func (d *NetworkCreateDialog) setActiveCategory(index int) {
fgColor := style.DialogFgColor
bgColor := style.ButtonBgColor
@ -651,3 +584,69 @@ func (d *NetworkCreateDialog) setIPSettingsPageNextFocus() {
d.focusElement = formFocus
}
// NetworkCreateOptions returns new network options.
func (d *NetworkCreateDialog) NetworkCreateOptions() networks.CreateOptions { //nolint:cyclop
var (
labels = make(map[string]string)
options = make(map[string]string)
subnets []string
gateways []string
ipranges []string
)
for _, label := range strings.Split(d.networkLabelsField.GetText(), " ") {
if label != "" {
split := strings.Split(label, "=")
if len(split) == 2 { //nolint:mnd
key := split[0]
value := split[1]
if key != "" && value != "" {
labels[key] = value
}
}
}
}
for _, option := range strings.Split(d.networkDriverOptionsField.GetText(), " ") {
if option != "" {
split := strings.Split(option, "=")
if len(split) == 2 { //nolint:mnd
key := split[0]
value := split[1]
if key != "" && value != "" {
options[key] = value
}
}
}
}
if strings.Trim(d.networkGatewayField.GetText(), " ") != "" {
gateways = strings.Split(d.networkGatewayField.GetText(), " ")
}
if strings.Trim(d.networkSubnetField.GetText(), " ") != "" {
subnets = strings.Split(d.networkSubnetField.GetText(), " ")
}
if strings.Trim(d.networkIPRangeField.GetText(), " ") != "" {
ipranges = strings.Split(d.networkIPRangeField.GetText(), " ")
}
opts := networks.CreateOptions{
Name: strings.TrimSpace(d.networkNameField.GetText()),
Labels: labels,
Internal: d.networkInternalCheckBox.IsChecked(),
Drivers: strings.TrimSpace(d.networkDriverField.GetText()),
DriversOptions: options,
IPv6: d.networkIpv6CheckBox.IsChecked(),
Gateways: gateways,
Subnets: subnets,
IPRanges: ipranges,
DisableDNS: d.networkDisableDNSCheckBox.IsChecked(),
}
return opts
}

View File

@ -26,7 +26,6 @@ const (
// NetworkDisconnectDialog implements network disconnect dialog primitive.
type NetworkDisconnectDialog struct {
*tview.Box
layout *tview.Flex
network *tview.InputField
container *tview.DropDown
@ -225,9 +224,9 @@ func (d *NetworkDisconnectDialog) Draw(screen tcell.Screen) {
return
}
d.DrawForSubclass(screen, d)
d.Box.DrawForSubclass(screen, d)
x, y, width, height := d.GetInnerRect()
x, y, width, height := d.Box.GetInnerRect()
d.layout.SetRect(x, y, width, height)
d.layout.Draw(screen)
@ -265,6 +264,12 @@ func (d *NetworkDisconnectDialog) SetContainers(cntList []entities.ListContainer
d.container.SetOptions(containers, nil)
}
func (d *NetworkDisconnectDialog) setFocusElement() {
if d.focusElement == netDisconnectContainerFocus {
d.focusElement = netConnectFormFocus
}
}
// SetNetworkInfo sets selected network name in disconnect dialog.
func (d *NetworkDisconnectDialog) SetNetworkInfo(id string, name string) {
d.networkName = name
@ -280,9 +285,3 @@ func (d *NetworkDisconnectDialog) GetDisconnectOptions() (string, string) {
return d.networkName, container
}
func (d *NetworkDisconnectDialog) setFocusElement() {
if d.focusElement == netDisconnectContainerFocus {
d.focusElement = netConnectFormFocus
}
}

View File

@ -6,11 +6,9 @@ import (
"strings"
"sync"
"github.com/containers/common/libnetwork/types"
"github.com/containers/podman-tui/ui/dialogs"
"github.com/containers/podman-tui/ui/networks/netdialogs"
"github.com/containers/podman-tui/ui/style"
"github.com/containers/podman-tui/ui/utils"
"github.com/rivo/tview"
)
@ -30,7 +28,6 @@ var (
// Networks implemnents the Networks page primitive.
type Networks struct {
*tview.Box
title string
headers []string
table *tview.Table
@ -39,7 +36,6 @@ type Networks struct {
confirmDialog *dialogs.ConfirmDialog
cmdDialog *dialogs.CommandDialog
messageDialog *dialogs.MessageDialog
sortDialog *dialogs.SortDialog
createDialog *netdialogs.NetworkCreateDialog
connectDialog *netdialogs.NetworkConnectDialog
disconnectDialog *netdialogs.NetworkDisconnectDialog
@ -50,10 +46,8 @@ type Networks struct {
}
type networkListReport struct {
mu sync.Mutex
report []types.Network
sortBy string
ascending bool
mu sync.Mutex
report [][]string
}
// NewNetworks returns nets page view.
@ -66,11 +60,9 @@ func NewNetworks() *Networks {
progressDialog: dialogs.NewProgressDialog(),
confirmDialog: dialogs.NewConfirmDialog(),
messageDialog: dialogs.NewMessageDialog(""),
sortDialog: dialogs.NewSortDialog([]string{"name", "driver"}, 0),
createDialog: netdialogs.NewNetworkCreateDialog(),
connectDialog: netdialogs.NewNetworkConnectDialog(),
disconnectDialog: netdialogs.NewNetworkDisconnectDialog(),
networkList: networkListReport{sortBy: "name", ascending: true},
}
nets.cmdDialog = dialogs.NewCommandDialog([][]string{
@ -122,7 +114,7 @@ func NewNetworks() *Networks {
nets.confirmDialog.Hide()
switch nets.confirmData {
case utils.PruneCommandLabel:
case "prune":
nets.prune()
case "rm":
nets.remove()
@ -151,10 +143,6 @@ func NewNetworks() *Networks {
nets.disconnectDialog.SetCancelFunc(nets.disconnectDialog.Hide)
nets.disconnectDialog.SetDisconnectFunc(nets.disconnect)
// set sort dialog functions
nets.sortDialog.SetCancelFunc(nets.sortDialog.Hide)
nets.sortDialog.SetSelectFunc(nets.SortView)
return nets
}
@ -169,12 +157,24 @@ func (nets *Networks) GetTitle() string {
}
// HasFocus returns whether or not this primitive has focus.
func (nets *Networks) HasFocus() bool {
if nets.SubDialogHasFocus() {
func (nets *Networks) HasFocus() bool { //nolint:cyclop
if nets.table.HasFocus() || nets.errorDialog.HasFocus() {
return true
}
if nets.table.HasFocus() || nets.Box.HasFocus() {
if nets.cmdDialog.HasFocus() || nets.messageDialog.IsDisplay() {
return true
}
if nets.progressDialog.HasFocus() || nets.confirmDialog.IsDisplay() {
return true
}
if nets.createDialog.HasFocus() || nets.connectDialog.HasFocus() {
return true
}
if nets.disconnectDialog.HasFocus() || nets.Box.HasFocus() {
return true
}
@ -183,10 +183,20 @@ func (nets *Networks) HasFocus() bool {
// SubDialogHasFocus returns whether or not sub dialog primitive has focus.
func (nets *Networks) SubDialogHasFocus() bool {
for _, dialog := range nets.getInnerDialogs() {
if dialog.HasFocus() {
return true
}
if nets.createDialog.HasFocus() || nets.errorDialog.HasFocus() {
return true
}
if nets.cmdDialog.HasFocus() || nets.messageDialog.IsDisplay() {
return true
}
if nets.progressDialog.HasFocus() || nets.confirmDialog.IsDisplay() {
return true
}
if nets.connectDialog.HasFocus() || nets.disconnectDialog.HasFocus() {
return true
}
return false
@ -194,38 +204,58 @@ func (nets *Networks) SubDialogHasFocus() bool {
// Focus is called when this primitive receives focus.
func (nets *Networks) Focus(delegate func(p tview.Primitive)) {
// error dialog
if nets.errorDialog.IsDisplay() {
delegate(nets.errorDialog)
return
}
// command dialog
if nets.cmdDialog.IsDisplay() {
delegate(nets.cmdDialog)
return
}
// message dialog
if nets.messageDialog.IsDisplay() {
delegate(nets.messageDialog)
return
}
// confirm dialog
if nets.confirmDialog.IsDisplay() {
delegate(nets.confirmDialog)
return
}
for _, dialog := range nets.getInnerDialogs() {
if dialog.IsDisplay() {
delegate(dialog)
// create dialog
if nets.createDialog.IsDisplay() {
delegate(nets.createDialog)
return
}
return
}
// connect dialog
if nets.connectDialog.IsDisplay() {
delegate(nets.connectDialog)
return
}
// disconnect dialog
if nets.disconnectDialog.IsDisplay() {
delegate(nets.disconnectDialog)
return
}
delegate(nets.table)
}
// HideAllDialogs hides all sub dialogs.
func (nets *Networks) HideAllDialogs() {
for _, dialog := range nets.getInnerDialogs() {
if dialog.IsDisplay() {
dialog.Hide()
}
}
}
func (nets *Networks) getSelectedItem() (string, string) {
if nets.table.GetRowCount() <= 1 {
return "", ""
@ -238,18 +268,37 @@ func (nets *Networks) getSelectedItem() (string, string) {
return netID, netName
}
func (nets *Networks) getInnerDialogs() []utils.UIDialog {
dialogs := []utils.UIDialog{
nets.errorDialog,
nets.progressDialog,
nets.confirmDialog,
nets.cmdDialog,
nets.messageDialog,
nets.connectDialog,
nets.createDialog,
nets.disconnectDialog,
nets.sortDialog,
// HideAllDialogs hides all sub dialogs.
func (nets *Networks) HideAllDialogs() {
if nets.errorDialog.IsDisplay() {
nets.errorDialog.Hide()
}
return dialogs
if nets.progressDialog.IsDisplay() {
nets.progressDialog.Hide()
}
if nets.confirmDialog.IsDisplay() {
nets.confirmDialog.Hide()
}
if nets.cmdDialog.IsDisplay() {
nets.cmdDialog.Hide()
}
if nets.messageDialog.IsDisplay() {
nets.messageDialog.Hide()
}
if nets.createDialog.IsDisplay() {
nets.createDialog.Hide()
}
if nets.connectDialog.IsDisplay() {
nets.connectDialog.Hide()
}
if nets.disconnectDialog.IsDisplay() {
nets.disconnectDialog.Hide()
}
}

View File

@ -24,38 +24,34 @@ func (nets *Networks) refresh(_ int) {
SetSelectable(false))
}
currentSelectedRow, _ := nets.table.GetSelection()
rowIndex := 1
netList := nets.getData()
nets.table.SetTitle(fmt.Sprintf("[::b]%s[%d]", strings.ToUpper(nets.title), len(netList)))
for _, net := range netList {
for i := range netList {
netID := netList[i][0]
netName := netList[i][1]
netDriver := netList[i][2]
// name column
nets.table.SetCell(rowIndex, viewNetworkNameColIndex,
tview.NewTableCell(net.ID[:12]).
tview.NewTableCell(netID[:12]).
SetExpansion(expand).
SetAlign(alignment))
// version column
nets.table.SetCell(rowIndex, viewNetworkVersionColIndex,
tview.NewTableCell(net.Name).
tview.NewTableCell(netName).
SetExpansion(expand).
SetAlign(alignment))
// plugins at column
nets.table.SetCell(rowIndex, viewNetworkPluginColIndex,
tview.NewTableCell(net.Driver).
tview.NewTableCell(netDriver).
SetExpansion(expand).
SetAlign(alignment))
rowIndex++
}
if currentSelectedRow > len(netList) {
currentSelectedRow--
if currentSelectedRow >= 0 {
nets.table.Select(currentSelectedRow, -1)
}
}
}

View File

@ -7,7 +7,6 @@ import (
ppods "github.com/containers/podman-tui/pdcs/pods"
"github.com/containers/podman-tui/ui/dialogs"
"github.com/containers/podman-tui/ui/style"
"github.com/containers/podman-tui/ui/utils"
"github.com/rs/zerolog/log"
)
@ -21,9 +20,9 @@ func (p *Pods) runCommand(cmd string) { //nolint:cyclop
p.kill()
case "pause":
p.pause()
case utils.PruneCommandLabel:
case "prune": //nolint:goconst
p.confirmDialog.SetTitle("podman pod prune")
p.confirmData = utils.PruneCommandLabel
p.confirmData = "prune"
p.confirmDialog.SetText("Are you sure you want to remove all stopped pods ?")
p.confirmDialog.Display()
case "restart":

View File

@ -2,8 +2,6 @@ package pods
import (
"fmt"
"sort"
"strconv"
"strings"
ppods "github.com/containers/podman-tui/pdcs/pods"
@ -13,18 +11,6 @@ import (
"github.com/rs/zerolog/log"
)
// SortView sorts data view called from sort dialog.
func (pods *Pods) SortView(option string, ascending bool) {
log.Debug().Msgf("view: pods sort by %s", option)
pods.podsList.mu.Lock()
defer pods.podsList.mu.Unlock()
pods.podsList.sortBy = option
pods.podsList.ascending = ascending
sort.Sort(containerListSorted{pods.podsList.report, option, ascending})
}
// UpdateData retrieves pods list data.
func (pods *Pods) UpdateData() {
podList, err := ppods.List()
@ -39,7 +25,6 @@ func (pods *Pods) UpdateData() {
pods.podsList.mu.Lock()
defer pods.podsList.mu.Unlock()
sort.Sort(containerListSorted{podList, pods.podsList.sortBy, pods.podsList.ascending})
pods.podsList.report = podList
}
@ -53,7 +38,7 @@ func (pods *Pods) getData() []*entities.ListPodsReport {
}
// ClearData clears table data.
func (pods *Pods) ClearData() {
func (pods *Pods) ClearData() { //nolint:stylecheck
pods.podsList.mu.Lock()
defer pods.podsList.mu.Unlock()
@ -77,47 +62,3 @@ func (pods *Pods) ClearData() {
pods.table.SetTitle(fmt.Sprintf("[::b]%s[0]", strings.ToUpper(pods.title)))
}
type lprSort []*entities.ListPodsReport
func (a lprSort) Len() int { return len(a) }
func (a lprSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
type containerListSorted struct {
lprSort
option string
ascending bool
}
func (a containerListSorted) Less(i, j int) bool {
switch a.option {
case "# of containers":
iNumOfCnt := strconv.Itoa(len(a.lprSort[i].Containers))
jNumOfCnt := strconv.Itoa(len(a.lprSort[j].Containers))
if a.ascending {
return iNumOfCnt < jNumOfCnt
}
return iNumOfCnt > jNumOfCnt
case "status":
if a.ascending {
return a.lprSort[i].Status < a.lprSort[j].Status
}
return a.lprSort[i].Status > a.lprSort[j].Status
case "created":
if a.ascending {
return a.lprSort[i].Created.After(a.lprSort[j].Created)
}
return a.lprSort[i].Created.Before(a.lprSort[j].Created)
}
if a.ascending {
return a.lprSort[i].Name < a.lprSort[j].Name
}
return a.lprSort[i].Name > a.lprSort[j].Name
}

View File

@ -5,9 +5,9 @@ import (
)
// Draw draws this primitive onto the screen.
func (pods *Pods) Draw(screen tcell.Screen) { //nolint:cyclop
pods.DrawForSubclass(screen, pods)
pods.SetBorder(false)
func (pods *Pods) Draw(screen tcell.Screen) {
pods.Box.DrawForSubclass(screen, pods)
pods.Box.SetBorder(false)
podViewX, podViewY, podViewW, podViewH := pods.GetInnerRect()
@ -78,19 +78,11 @@ func (pods *Pods) Draw(screen tcell.Screen) { //nolint:cyclop
return
}
// stats dialog
// stats dialogs
if pods.statsDialog.IsDisplay() {
pods.statsDialog.SetRect(podViewX, podViewY, podViewW, podViewH)
pods.statsDialog.Draw(screen)
return
}
// sort dialog
if pods.sortDialog.IsDisplay() {
pods.sortDialog.SetRect(podViewX, podViewY, podViewW, podViewH)
pods.sortDialog.Draw(screen)
return
}
}

View File

@ -65,13 +65,6 @@ func (pods *Pods) InputHandler() func(event *tcell.EventKey, setFocus func(p tvi
}
}
// pod sort dialog handler
if pods.sortDialog.HasFocus() {
if podsSortDialogHandler := pods.sortDialog.InputHandler(); podsSortDialogHandler != nil {
podsSortDialogHandler(event, setFocus)
}
}
// table handlers
if pods.table.HasFocus() { //nolint:nestif
pods.selectedID, _ = pods.getSelectedItem()
@ -86,14 +79,6 @@ func (pods *Pods) InputHandler() func(event *tcell.EventKey, setFocus func(p tvi
return
}
// display sort menu
if event.Rune() == utils.SortMenuKey.Rune() {
pods.sortDialog.Display()
setFocus(pods)
return
}
if event.Key() == utils.DeleteKey.EventKey() {
pods.rm()
setFocus(pods)

Some files were not shown because too many files have changed in this diff Show More